Recreate RHEL 6.12.0-211.20.1 from CS10/upstream backports
Add the RHEL 211.19.1..211.20.1 backports (1245-1287) from centos-stream-10 and upstream, on top of 211.18.1, plus the dpll RHEL kABI adaptation (1287). RHEL now ships the smb cifs.spnego fix too; the existing ahead-fix 1105 is byte-identical, so RHEL's redundant copy is omitted. Bump to 211.20.1.
This commit is contained in:
parent
b9d46fff46
commit
f6916a8ae1
@ -0,0 +1,89 @@
|
||||
From 72ad184f05e7a77a7043b9e71943ba3031cc683a Mon Sep 17 00:00:00 2001
|
||||
From: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
|
||||
Date: Tue, 31 Mar 2026 13:17:02 +0000
|
||||
Subject: [PATCH] proc: use the same treatment to check proc_lseek as ones for
|
||||
proc_read_iter et.al
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-163346
|
||||
CVE: CVE-2025-38653
|
||||
|
||||
commit ff7ec8dc1b646296f8d94c39339e8d3833d16c05
|
||||
Author: wangzijie <wangzijie1@honor.com>
|
||||
Date: Sat Jun 7 10:13:53 2025 +0800
|
||||
|
||||
proc: use the same treatment to check proc_lseek as ones for proc_read_iter et.al
|
||||
|
||||
Check pde->proc_ops->proc_lseek directly may cause UAF in rmmod scenario.
|
||||
It's a gap in proc_reg_open() after commit 654b33ada4ab("proc: fix UAF in
|
||||
proc_get_inode()"). Followed by AI Viro's suggestion, fix it in same
|
||||
manner.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20250607021353.1127963-1-wangzijie1@honor.com
|
||||
Fixes: 3f61631d47f1 ("take care to handle NULL ->proc_lseek()")
|
||||
Signed-off-by: wangzijie <wangzijie1@honor.com>
|
||||
Reviewed-by: Alexey Dobriyan <adobriyan@gmail.com>
|
||||
Cc: Alexei Starovoitov <ast@kernel.org>
|
||||
Cc: Al Viro <viro@zeniv.linux.org.uk>
|
||||
Cc: "Edgecombe, Rick P" <rick.p.edgecombe@intel.com>
|
||||
Cc: Kirill A. Shuemov <kirill.shutemov@linux.intel.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
|
||||
Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
|
||||
|
||||
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
|
||||
index 9b3b4efe2041..26e118f1dc9e 100644
|
||||
--- a/fs/proc/generic.c
|
||||
+++ b/fs/proc/generic.c
|
||||
@@ -567,6 +567,8 @@ static void pde_set_flags(struct proc_dir_entry *pde)
|
||||
if (pde->proc_ops->proc_compat_ioctl)
|
||||
pde->flags |= PROC_ENTRY_proc_compat_ioctl;
|
||||
#endif
|
||||
+ if (pde->proc_ops->proc_lseek)
|
||||
+ pde->flags |= PROC_ENTRY_proc_lseek;
|
||||
}
|
||||
|
||||
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
|
||||
diff --git a/fs/proc/inode.c b/fs/proc/inode.c
|
||||
index a3eb3b740f76..73074b9c715a 100644
|
||||
--- a/fs/proc/inode.c
|
||||
+++ b/fs/proc/inode.c
|
||||
@@ -473,7 +473,7 @@ static int proc_reg_open(struct inode *inode, struct file *file)
|
||||
typeof_member(struct proc_ops, proc_open) open;
|
||||
struct pde_opener *pdeo;
|
||||
|
||||
- if (!pde->proc_ops->proc_lseek)
|
||||
+ if (!pde_has_proc_lseek(pde))
|
||||
file->f_mode &= ~FMODE_LSEEK;
|
||||
|
||||
if (pde_is_permanent(pde)) {
|
||||
diff --git a/fs/proc/internal.h b/fs/proc/internal.h
|
||||
index 77a517f91821..2c590e4a4022 100644
|
||||
--- a/fs/proc/internal.h
|
||||
+++ b/fs/proc/internal.h
|
||||
@@ -99,6 +99,11 @@ static inline bool pde_has_proc_compat_ioctl(const struct proc_dir_entry *pde)
|
||||
#endif
|
||||
}
|
||||
|
||||
+static inline bool pde_has_proc_lseek(const struct proc_dir_entry *pde)
|
||||
+{
|
||||
+ return pde->flags & PROC_ENTRY_proc_lseek;
|
||||
+}
|
||||
+
|
||||
extern struct kmem_cache *proc_dir_entry_cache;
|
||||
void pde_free(struct proc_dir_entry *pde);
|
||||
|
||||
diff --git a/include/linux/proc_fs.h b/include/linux/proc_fs.h
|
||||
index ea62201c74c4..703d0c76cc9a 100644
|
||||
--- a/include/linux/proc_fs.h
|
||||
+++ b/include/linux/proc_fs.h
|
||||
@@ -27,6 +27,7 @@ enum {
|
||||
|
||||
PROC_ENTRY_proc_read_iter = 1U << 1,
|
||||
PROC_ENTRY_proc_compat_ioctl = 1U << 2,
|
||||
+ PROC_ENTRY_proc_lseek = 1U << 3,
|
||||
};
|
||||
|
||||
struct proc_ops {
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
140
1246-proc-fix-missing-pde-set-flags-for-net-proc-files.patch
Normal file
140
1246-proc-fix-missing-pde-set-flags-for-net-proc-files.patch
Normal file
@ -0,0 +1,140 @@
|
||||
From 5b2ab6d1f5e5488a9faa61f4d560b239dce0e08b Mon Sep 17 00:00:00 2001
|
||||
From: Abhi Das <adas@redhat.com>
|
||||
Date: Fri, 17 Apr 2026 13:31:25 -0500
|
||||
Subject: [PATCH] proc: fix missing pde_set_flags() for net proc files
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-163346
|
||||
CVE: CVE-2025-38653
|
||||
|
||||
commit 2ce3d282bd5050fca8577defeff08ada0d55d062
|
||||
Author: wangzijie <wangzijie1@honor.com>
|
||||
Date: Mon Aug 18 20:31:02 2025 +0800
|
||||
|
||||
proc: fix missing pde_set_flags() for net proc files
|
||||
|
||||
To avoid potential UAF issues during module removal races, we use
|
||||
pde_set_flags() to save proc_ops flags in PDE itself before
|
||||
proc_register(), and then use pde_has_proc_*() helpers instead of directly
|
||||
dereferencing pde->proc_ops->*.
|
||||
|
||||
However, the pde_set_flags() call was missing when creating net related
|
||||
proc files. This omission caused incorrect behavior which FMODE_LSEEK was
|
||||
being cleared inappropriately in proc_reg_open() for net proc files. Lars
|
||||
reported it in this link[1].
|
||||
|
||||
Fix this by ensuring pde_set_flags() is called when register proc entry,
|
||||
and add NULL check for proc_ops in pde_set_flags().
|
||||
|
||||
[wangzijie1@honor.com: stash pde->proc_ops in a local const variable, per Christian]
|
||||
Link: https://lkml.kernel.org/r/20250821105806.1453833-1-wangzijie1@honor.com
|
||||
Link: https://lkml.kernel.org/r/20250818123102.959595-1-wangzijie1@honor.com
|
||||
Link: https://lore.kernel.org/all/20250815195616.64497967@chagall.paradoxon.rec/ [1]
|
||||
Fixes: ff7ec8dc1b64 ("proc: use the same treatment to check proc_lseek as ones for proc_read_iter et.al")
|
||||
Signed-off-by: wangzijie <wangzijie1@honor.com>
|
||||
Reported-by: Lars Wendler <polynomial-c@gmx.de>
|
||||
Tested-by: Stefano Brivio <sbrivio@redhat.com>
|
||||
Tested-by: Petr Vaněk <pv@excello.cz>
|
||||
Tested by: Lars Wendler <polynomial-c@gmx.de>
|
||||
Cc: Alexei Starovoitov <ast@kernel.org>
|
||||
Cc: Alexey Dobriyan <adobriyan@gmail.com>
|
||||
Cc: Al Viro <viro@zeniv.linux.org.uk>
|
||||
Cc: "Edgecombe, Rick P" <rick.p.edgecombe@intel.com>
|
||||
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Cc: Jiri Slaby <jirislaby@kernel.org>
|
||||
Cc: Kirill A. Shutemov <k.shutemov@gmail.com>
|
||||
Cc: wangzijie <wangzijie1@honor.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
|
||||
Signed-off-by: Abhi Das <adas@redhat.com>
|
||||
|
||||
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
|
||||
index 26e118f1dc9e..88423b46e5ce 100644
|
||||
--- a/fs/proc/generic.c
|
||||
+++ b/fs/proc/generic.c
|
||||
@@ -362,6 +362,25 @@ static const struct inode_operations proc_dir_inode_operations = {
|
||||
.setattr = proc_notify_change,
|
||||
};
|
||||
|
||||
+static void pde_set_flags(struct proc_dir_entry *pde)
|
||||
+{
|
||||
+ const struct proc_ops *proc_ops = pde->proc_ops;
|
||||
+
|
||||
+ if (!proc_ops)
|
||||
+ return;
|
||||
+
|
||||
+ if (proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
|
||||
+ pde->flags |= PROC_ENTRY_PERMANENT;
|
||||
+ if (proc_ops->proc_read_iter)
|
||||
+ pde->flags |= PROC_ENTRY_proc_read_iter;
|
||||
+#ifdef CONFIG_COMPAT
|
||||
+ if (proc_ops->proc_compat_ioctl)
|
||||
+ pde->flags |= PROC_ENTRY_proc_compat_ioctl;
|
||||
+#endif
|
||||
+ if (proc_ops->proc_lseek)
|
||||
+ pde->flags |= PROC_ENTRY_proc_lseek;
|
||||
+}
|
||||
+
|
||||
/* returns the registered entry, or frees dp and returns NULL on failure */
|
||||
struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
|
||||
struct proc_dir_entry *dp)
|
||||
@@ -369,6 +388,8 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
|
||||
if (proc_alloc_inum(&dp->low_ino))
|
||||
goto out_free_entry;
|
||||
|
||||
+ pde_set_flags(dp);
|
||||
+
|
||||
write_lock(&proc_subdir_lock);
|
||||
dp->parent = dir;
|
||||
if (pde_subdir_insert(dir, dp) == false) {
|
||||
@@ -557,20 +578,6 @@ struct proc_dir_entry *proc_create_reg(const char *name, umode_t mode,
|
||||
return p;
|
||||
}
|
||||
|
||||
-static void pde_set_flags(struct proc_dir_entry *pde)
|
||||
-{
|
||||
- if (pde->proc_ops->proc_flags & PROC_ENTRY_PERMANENT)
|
||||
- pde->flags |= PROC_ENTRY_PERMANENT;
|
||||
- if (pde->proc_ops->proc_read_iter)
|
||||
- pde->flags |= PROC_ENTRY_proc_read_iter;
|
||||
-#ifdef CONFIG_COMPAT
|
||||
- if (pde->proc_ops->proc_compat_ioctl)
|
||||
- pde->flags |= PROC_ENTRY_proc_compat_ioctl;
|
||||
-#endif
|
||||
- if (pde->proc_ops->proc_lseek)
|
||||
- pde->flags |= PROC_ENTRY_proc_lseek;
|
||||
-}
|
||||
-
|
||||
struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
|
||||
struct proc_dir_entry *parent,
|
||||
const struct proc_ops *proc_ops, void *data)
|
||||
@@ -581,7 +588,6 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
|
||||
if (!p)
|
||||
return NULL;
|
||||
p->proc_ops = proc_ops;
|
||||
- pde_set_flags(p);
|
||||
return proc_register(parent, p);
|
||||
}
|
||||
EXPORT_SYMBOL(proc_create_data);
|
||||
@@ -632,7 +638,6 @@ struct proc_dir_entry *proc_create_seq_private(const char *name, umode_t mode,
|
||||
p->proc_ops = &proc_seq_ops;
|
||||
p->seq_ops = ops;
|
||||
p->state_size = state_size;
|
||||
- pde_set_flags(p);
|
||||
return proc_register(parent, p);
|
||||
}
|
||||
EXPORT_SYMBOL(proc_create_seq_private);
|
||||
@@ -663,7 +668,6 @@ struct proc_dir_entry *proc_create_single_data(const char *name, umode_t mode,
|
||||
return NULL;
|
||||
p->proc_ops = &proc_single_ops;
|
||||
p->single_show = show;
|
||||
- pde_set_flags(p);
|
||||
return proc_register(parent, p);
|
||||
}
|
||||
EXPORT_SYMBOL(proc_create_single_data);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
59
1247-proc-fix-type-confusion-in-pde-set-flags.patch
Normal file
59
1247-proc-fix-type-confusion-in-pde-set-flags.patch
Normal file
@ -0,0 +1,59 @@
|
||||
From 51903c441e999d6c7afdf651f0724c26627950d8 Mon Sep 17 00:00:00 2001
|
||||
From: Abhi Das <adas@redhat.com>
|
||||
Date: Fri, 17 Apr 2026 13:33:50 -0500
|
||||
Subject: [PATCH] proc: fix type confusion in pde_set_flags()
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-163346
|
||||
CVE: CVE-2025-38653
|
||||
|
||||
commit 0ce9398aa0830f15f92bbed73853f9861c3e74ff
|
||||
Author: wangzijie <wangzijie1@honor.com>
|
||||
Date: Thu Sep 4 21:57:15 2025 +0800
|
||||
|
||||
proc: fix type confusion in pde_set_flags()
|
||||
|
||||
Commit 2ce3d282bd50 ("proc: fix missing pde_set_flags() for net proc
|
||||
files") missed a key part in the definition of proc_dir_entry:
|
||||
|
||||
union {
|
||||
const struct proc_ops *proc_ops;
|
||||
const struct file_operations *proc_dir_ops;
|
||||
};
|
||||
|
||||
So dereference of ->proc_ops assumes it is a proc_ops structure results in
|
||||
type confusion and make NULL check for 'proc_ops' not work for proc dir.
|
||||
|
||||
Add !S_ISDIR(dp->mode) test before calling pde_set_flags() to fix it.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20250904135715.3972782-1-wangzijie1@honor.com
|
||||
Fixes: 2ce3d282bd50 ("proc: fix missing pde_set_flags() for net proc files")
|
||||
Signed-off-by: wangzijie <wangzijie1@honor.com>
|
||||
Reported-by: Brad Spengler <spender@grsecurity.net>
|
||||
Closes: https://lore.kernel.org/all/20250903065758.3678537-1-wangzijie1@honor.com/
|
||||
Cc: Alexey Dobriyan <adobriyan@gmail.com>
|
||||
Cc: Al Viro <viro@zeniv.linux.org.uk>
|
||||
Cc: Christian Brauner <brauner@kernel.org>
|
||||
Cc: Jiri Slaby <jirislaby@kernel.org>
|
||||
Cc: Stefano Brivio <sbrivio@redhat.com>
|
||||
Cc: <stable@vger.kernel.org>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
|
||||
Signed-off-by: Abhi Das <adas@redhat.com>
|
||||
|
||||
diff --git a/fs/proc/generic.c b/fs/proc/generic.c
|
||||
index 88423b46e5ce..69150974ad87 100644
|
||||
--- a/fs/proc/generic.c
|
||||
+++ b/fs/proc/generic.c
|
||||
@@ -388,7 +388,8 @@ struct proc_dir_entry *proc_register(struct proc_dir_entry *dir,
|
||||
if (proc_alloc_inum(&dp->low_ino))
|
||||
goto out_free_entry;
|
||||
|
||||
- pde_set_flags(dp);
|
||||
+ if (!S_ISDIR(dp->mode))
|
||||
+ pde_set_flags(dp);
|
||||
|
||||
write_lock(&proc_subdir_lock);
|
||||
dp->parent = dir;
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
74
1248-nbd-defer-config-unlock-in-nbd-genl-connect.patch
Normal file
74
1248-nbd-defer-config-unlock-in-nbd-genl-connect.patch
Normal file
@ -0,0 +1,74 @@
|
||||
From f78e5b8575baca940843081e029427cfb2679d32 Mon Sep 17 00:00:00 2001
|
||||
From: Ming Lei <ming.lei@redhat.com>
|
||||
Date: Mon, 10 Nov 2025 20:49:20 +0800
|
||||
Subject: [PATCH] nbd: defer config unlock in nbd_genl_connect
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-144763
|
||||
|
||||
commit 1649714b930f9ea6233ce0810ba885999da3b5d4
|
||||
Author: Zheng Qixing <zhengqixing@huawei.com>
|
||||
Date: Mon Nov 10 20:49:20 2025 +0800
|
||||
|
||||
nbd: defer config unlock in nbd_genl_connect
|
||||
|
||||
There is one use-after-free warning when running NBD_CMD_CONNECT and
|
||||
NBD_CLEAR_SOCK:
|
||||
|
||||
nbd_genl_connect
|
||||
nbd_alloc_and_init_config // config_refs=1
|
||||
nbd_start_device // config_refs=2
|
||||
set NBD_RT_HAS_CONFIG_REF open nbd // config_refs=3
|
||||
recv_work done // config_refs=2
|
||||
NBD_CLEAR_SOCK // config_refs=1
|
||||
close nbd // config_refs=0
|
||||
refcount_inc -> uaf
|
||||
|
||||
------------[ cut here ]------------
|
||||
refcount_t: addition on 0; use-after-free.
|
||||
WARNING: CPU: 24 PID: 1014 at lib/refcount.c:25 refcount_warn_saturate+0x12e/0x290
|
||||
nbd_genl_connect+0x16d0/0x1ab0
|
||||
genl_family_rcv_msg_doit+0x1f3/0x310
|
||||
genl_rcv_msg+0x44a/0x790
|
||||
|
||||
The issue can be easily reproduced by adding a small delay before
|
||||
refcount_inc(&nbd->config_refs) in nbd_genl_connect():
|
||||
|
||||
mutex_unlock(&nbd->config_lock);
|
||||
if (!ret) {
|
||||
set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags);
|
||||
+ printk("before sleep\n");
|
||||
+ mdelay(5 * 1000);
|
||||
+ printk("after sleep\n");
|
||||
refcount_inc(&nbd->config_refs);
|
||||
nbd_connect_reply(info, nbd->index);
|
||||
}
|
||||
|
||||
Fixes: e46c7287b1c2 ("nbd: add a basic netlink interface")
|
||||
Signed-off-by: Zheng Qixing <zhengqixing@huawei.com>
|
||||
Reviewed-by: Yu Kuai <yukuai@fnnas.com>
|
||||
Signed-off-by: Jens Axboe <axboe@kernel.dk>
|
||||
|
||||
Signed-off-by: Ming Lei <ming.lei@redhat.com>
|
||||
|
||||
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
|
||||
index 215fc18115b7..a05ff68e58d0 100644
|
||||
--- a/drivers/block/nbd.c
|
||||
+++ b/drivers/block/nbd.c
|
||||
@@ -2241,12 +2241,13 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info)
|
||||
|
||||
ret = nbd_start_device(nbd);
|
||||
out:
|
||||
- mutex_unlock(&nbd->config_lock);
|
||||
if (!ret) {
|
||||
set_bit(NBD_RT_HAS_CONFIG_REF, &config->runtime_flags);
|
||||
refcount_inc(&nbd->config_refs);
|
||||
nbd_connect_reply(info, nbd->index);
|
||||
}
|
||||
+ mutex_unlock(&nbd->config_lock);
|
||||
+
|
||||
nbd_config_put(nbd);
|
||||
if (put_dev)
|
||||
nbd_put(nbd);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,205 @@
|
||||
From 70b8cf8679755c2013b28e9f61da1576eab4c707 Mon Sep 17 00:00:00 2001
|
||||
From: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
|
||||
Date: Mon, 27 Apr 2026 15:02:27 +0000
|
||||
Subject: [PATCH] crypto: authenc - Correctly pass EINPROGRESS back up to the
|
||||
caller
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-130557
|
||||
|
||||
commit 96feb73def02d175850daa0e7c2c90c876681b5c
|
||||
Author: Herbert Xu <herbert@gondor.apana.org.au>
|
||||
Date: Wed Sep 24 18:20:17 2025 +0800
|
||||
|
||||
crypto: authenc - Correctly pass EINPROGRESS back up to the caller
|
||||
|
||||
When authenc is invoked with MAY_BACKLOG, it needs to pass EINPROGRESS
|
||||
notifications back up to the caller when the underlying algorithm
|
||||
returns EBUSY synchronously.
|
||||
|
||||
However, if the EBUSY comes from the second part of an authenc call,
|
||||
i.e., it is asynchronous, both the EBUSY and the subsequent EINPROGRESS
|
||||
notification must not be passed to the caller.
|
||||
|
||||
Implement this by passing a mask to the function that starts the
|
||||
second half of authenc and using it to determine whether EBUSY
|
||||
and EINPROGRESS should be passed to the caller.
|
||||
|
||||
This was a deficiency in the original implementation of authenc
|
||||
because it was not expected to be used with MAY_BACKLOG.
|
||||
|
||||
Reported-by: Ingo Franzki <ifranzki@linux.ibm.com>
|
||||
Reported-by: Mikulas Patocka <mpatocka@redhat.com>
|
||||
Fixes: 180ce7e81030 ("crypto: authenc - Add EINPROGRESS check")
|
||||
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
|
||||
|
||||
Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
|
||||
|
||||
diff --git a/crypto/authenc.c b/crypto/authenc.c
|
||||
index 3aaf3ab4e360..d04068af9833 100644
|
||||
--- a/crypto/authenc.c
|
||||
+++ b/crypto/authenc.c
|
||||
@@ -39,7 +39,7 @@ struct authenc_request_ctx {
|
||||
|
||||
static void authenc_request_complete(struct aead_request *req, int err)
|
||||
{
|
||||
- if (err != -EINPROGRESS)
|
||||
+ if (err != -EINPROGRESS && err != -EBUSY)
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
@@ -109,27 +109,42 @@ static int crypto_authenc_setkey(struct crypto_aead *authenc, const u8 *key,
|
||||
return err;
|
||||
}
|
||||
|
||||
-static void authenc_geniv_ahash_done(void *data, int err)
|
||||
+static void authenc_geniv_ahash_finish(struct aead_request *req)
|
||||
{
|
||||
- struct aead_request *req = data;
|
||||
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
|
||||
struct aead_instance *inst = aead_alg_instance(authenc);
|
||||
struct authenc_instance_ctx *ictx = aead_instance_ctx(inst);
|
||||
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
|
||||
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
|
||||
|
||||
- if (err)
|
||||
- goto out;
|
||||
-
|
||||
scatterwalk_map_and_copy(ahreq->result, req->dst,
|
||||
req->assoclen + req->cryptlen,
|
||||
crypto_aead_authsize(authenc), 1);
|
||||
+}
|
||||
|
||||
-out:
|
||||
+static void authenc_geniv_ahash_done(void *data, int err)
|
||||
+{
|
||||
+ struct aead_request *req = data;
|
||||
+
|
||||
+ if (!err)
|
||||
+ authenc_geniv_ahash_finish(req);
|
||||
aead_request_complete(req, err);
|
||||
}
|
||||
|
||||
-static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
|
||||
+/*
|
||||
+ * Used when the ahash request was invoked in the async callback context
|
||||
+ * of the previous skcipher request. Eat any EINPROGRESS notifications.
|
||||
+ */
|
||||
+static void authenc_geniv_ahash_done2(void *data, int err)
|
||||
+{
|
||||
+ struct aead_request *req = data;
|
||||
+
|
||||
+ if (!err)
|
||||
+ authenc_geniv_ahash_finish(req);
|
||||
+ authenc_request_complete(req, err);
|
||||
+}
|
||||
+
|
||||
+static int crypto_authenc_genicv(struct aead_request *req, unsigned int mask)
|
||||
{
|
||||
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
|
||||
struct aead_instance *inst = aead_alg_instance(authenc);
|
||||
@@ -138,6 +153,7 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
|
||||
struct crypto_ahash *auth = ctx->auth;
|
||||
struct authenc_request_ctx *areq_ctx = aead_request_ctx(req);
|
||||
struct ahash_request *ahreq = (void *)(areq_ctx->tail + ictx->reqoff);
|
||||
+ unsigned int flags = aead_request_flags(req) & ~mask;
|
||||
u8 *hash = areq_ctx->tail;
|
||||
int err;
|
||||
|
||||
@@ -145,7 +161,8 @@ static int crypto_authenc_genicv(struct aead_request *req, unsigned int flags)
|
||||
ahash_request_set_crypt(ahreq, req->dst, hash,
|
||||
req->assoclen + req->cryptlen);
|
||||
ahash_request_set_callback(ahreq, flags,
|
||||
- authenc_geniv_ahash_done, req);
|
||||
+ mask ? authenc_geniv_ahash_done2 :
|
||||
+ authenc_geniv_ahash_done, req);
|
||||
|
||||
err = crypto_ahash_digest(ahreq);
|
||||
if (err)
|
||||
@@ -161,12 +178,11 @@ static void crypto_authenc_encrypt_done(void *data, int err)
|
||||
{
|
||||
struct aead_request *areq = data;
|
||||
|
||||
- if (err)
|
||||
- goto out;
|
||||
-
|
||||
- err = crypto_authenc_genicv(areq, 0);
|
||||
-
|
||||
-out:
|
||||
+ if (err) {
|
||||
+ aead_request_complete(areq, err);
|
||||
+ return;
|
||||
+ }
|
||||
+ err = crypto_authenc_genicv(areq, CRYPTO_TFM_REQ_MAY_SLEEP);
|
||||
authenc_request_complete(areq, err);
|
||||
}
|
||||
|
||||
@@ -219,11 +235,18 @@ static int crypto_authenc_encrypt(struct aead_request *req)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- return crypto_authenc_genicv(req, aead_request_flags(req));
|
||||
+ return crypto_authenc_genicv(req, 0);
|
||||
+}
|
||||
+
|
||||
+static void authenc_decrypt_tail_done(void *data, int err)
|
||||
+{
|
||||
+ struct aead_request *req = data;
|
||||
+
|
||||
+ authenc_request_complete(req, err);
|
||||
}
|
||||
|
||||
static int crypto_authenc_decrypt_tail(struct aead_request *req,
|
||||
- unsigned int flags)
|
||||
+ unsigned int mask)
|
||||
{
|
||||
struct crypto_aead *authenc = crypto_aead_reqtfm(req);
|
||||
struct aead_instance *inst = aead_alg_instance(authenc);
|
||||
@@ -234,6 +257,7 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
|
||||
struct skcipher_request *skreq = (void *)(areq_ctx->tail +
|
||||
ictx->reqoff);
|
||||
unsigned int authsize = crypto_aead_authsize(authenc);
|
||||
+ unsigned int flags = aead_request_flags(req) & ~mask;
|
||||
u8 *ihash = ahreq->result + authsize;
|
||||
struct scatterlist *src, *dst;
|
||||
|
||||
@@ -250,7 +274,9 @@ static int crypto_authenc_decrypt_tail(struct aead_request *req,
|
||||
|
||||
skcipher_request_set_tfm(skreq, ctx->enc);
|
||||
skcipher_request_set_callback(skreq, flags,
|
||||
- req->base.complete, req->base.data);
|
||||
+ mask ? authenc_decrypt_tail_done :
|
||||
+ req->base.complete,
|
||||
+ mask ? req : req->base.data);
|
||||
skcipher_request_set_crypt(skreq, src, dst,
|
||||
req->cryptlen - authsize, req->iv);
|
||||
|
||||
@@ -261,12 +287,11 @@ static void authenc_verify_ahash_done(void *data, int err)
|
||||
{
|
||||
struct aead_request *req = data;
|
||||
|
||||
- if (err)
|
||||
- goto out;
|
||||
-
|
||||
- err = crypto_authenc_decrypt_tail(req, 0);
|
||||
-
|
||||
-out:
|
||||
+ if (err) {
|
||||
+ aead_request_complete(req, err);
|
||||
+ return;
|
||||
+ }
|
||||
+ err = crypto_authenc_decrypt_tail(req, CRYPTO_TFM_REQ_MAY_SLEEP);
|
||||
authenc_request_complete(req, err);
|
||||
}
|
||||
|
||||
@@ -293,7 +318,7 @@ static int crypto_authenc_decrypt(struct aead_request *req)
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- return crypto_authenc_decrypt_tail(req, aead_request_flags(req));
|
||||
+ return crypto_authenc_decrypt_tail(req, 0);
|
||||
}
|
||||
|
||||
static int crypto_authenc_init_tfm(struct crypto_aead *tfm)
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,426 @@
|
||||
From bf31f0732fbaa881f28bf71e012d2aba97e59316 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:50:08 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: detect DPLL channel count from chip ID at
|
||||
runtime
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit 4845f2fff730f0cdf8f7fe6401c8b871891cf1cb
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Fri Feb 27 11:52:59 2026 +0100
|
||||
|
||||
dpll: zl3073x: detect DPLL channel count from chip ID at runtime
|
||||
|
||||
Replace the five per-variant zl3073x_chip_info structures and their
|
||||
exported symbol definitions with a single consolidated chip ID lookup
|
||||
table. The chip variant is now detected at runtime by reading the chip
|
||||
ID register from hardware and looking it up in the table, rather than
|
||||
being selected at compile time via the bus driver match data.
|
||||
|
||||
Repurpose struct zl3073x_chip_info to hold a single chip ID, its
|
||||
channel count, and a flags field. Introduce enum zl3073x_flags with
|
||||
ZL3073X_FLAG_REF_PHASE_COMP_32 to replace the chip_id switch statement
|
||||
in zl3073x_dev_is_ref_phase_comp_32bit(). Store a pointer to the
|
||||
detected chip_info entry in struct zl3073x_dev for runtime access.
|
||||
|
||||
This simplifies the bus drivers by removing per-variant .data and
|
||||
.driver_data references from the I2C/SPI match tables, and makes
|
||||
adding support for new chip variants a single-line table addition.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260227105300.710272-2-ivecera@redhat.com
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
(cherry picked from commit 4845f2fff730f0cdf8f7fe6401c8b871891cf1cb)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index 37f3c33570ee..c8af34301045 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -20,79 +20,30 @@
|
||||
#include "dpll.h"
|
||||
#include "regs.h"
|
||||
|
||||
-/* Chip IDs for zl30731 */
|
||||
-static const u16 zl30731_ids[] = {
|
||||
- 0x0E93,
|
||||
- 0x1E93,
|
||||
- 0x2E93,
|
||||
+#define ZL_CHIP_INFO(_id, _nchannels, _flags) \
|
||||
+ { .id = (_id), .num_channels = (_nchannels), .flags = (_flags) }
|
||||
+
|
||||
+static const struct zl3073x_chip_info zl3073x_chip_ids[] = {
|
||||
+ ZL_CHIP_INFO(0x0E30, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
+ ZL_CHIP_INFO(0x0E93, 1, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
+ ZL_CHIP_INFO(0x0E94, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
+ ZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
+ ZL_CHIP_INFO(0x0E96, 4, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
+ ZL_CHIP_INFO(0x0E97, 5, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
+ ZL_CHIP_INFO(0x1E93, 1, 0),
|
||||
+ ZL_CHIP_INFO(0x1E94, 2, 0),
|
||||
+ ZL_CHIP_INFO(0x1E95, 3, 0),
|
||||
+ ZL_CHIP_INFO(0x1E96, 4, 0),
|
||||
+ ZL_CHIP_INFO(0x1E97, 5, 0),
|
||||
+ ZL_CHIP_INFO(0x1F60, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
+ ZL_CHIP_INFO(0x2E93, 1, 0),
|
||||
+ ZL_CHIP_INFO(0x2E94, 2, 0),
|
||||
+ ZL_CHIP_INFO(0x2E95, 3, 0),
|
||||
+ ZL_CHIP_INFO(0x2E96, 4, 0),
|
||||
+ ZL_CHIP_INFO(0x2E97, 5, 0),
|
||||
+ ZL_CHIP_INFO(0x3FC4, 2, 0),
|
||||
};
|
||||
|
||||
-const struct zl3073x_chip_info zl30731_chip_info = {
|
||||
- .ids = zl30731_ids,
|
||||
- .num_ids = ARRAY_SIZE(zl30731_ids),
|
||||
- .num_channels = 1,
|
||||
-};
|
||||
-EXPORT_SYMBOL_NS_GPL(zl30731_chip_info, "ZL3073X");
|
||||
-
|
||||
-/* Chip IDs for zl30732 */
|
||||
-static const u16 zl30732_ids[] = {
|
||||
- 0x0E30,
|
||||
- 0x0E94,
|
||||
- 0x1E94,
|
||||
- 0x1F60,
|
||||
- 0x2E94,
|
||||
- 0x3FC4,
|
||||
-};
|
||||
-
|
||||
-const struct zl3073x_chip_info zl30732_chip_info = {
|
||||
- .ids = zl30732_ids,
|
||||
- .num_ids = ARRAY_SIZE(zl30732_ids),
|
||||
- .num_channels = 2,
|
||||
-};
|
||||
-EXPORT_SYMBOL_NS_GPL(zl30732_chip_info, "ZL3073X");
|
||||
-
|
||||
-/* Chip IDs for zl30733 */
|
||||
-static const u16 zl30733_ids[] = {
|
||||
- 0x0E95,
|
||||
- 0x1E95,
|
||||
- 0x2E95,
|
||||
-};
|
||||
-
|
||||
-const struct zl3073x_chip_info zl30733_chip_info = {
|
||||
- .ids = zl30733_ids,
|
||||
- .num_ids = ARRAY_SIZE(zl30733_ids),
|
||||
- .num_channels = 3,
|
||||
-};
|
||||
-EXPORT_SYMBOL_NS_GPL(zl30733_chip_info, "ZL3073X");
|
||||
-
|
||||
-/* Chip IDs for zl30734 */
|
||||
-static const u16 zl30734_ids[] = {
|
||||
- 0x0E96,
|
||||
- 0x1E96,
|
||||
- 0x2E96,
|
||||
-};
|
||||
-
|
||||
-const struct zl3073x_chip_info zl30734_chip_info = {
|
||||
- .ids = zl30734_ids,
|
||||
- .num_ids = ARRAY_SIZE(zl30734_ids),
|
||||
- .num_channels = 4,
|
||||
-};
|
||||
-EXPORT_SYMBOL_NS_GPL(zl30734_chip_info, "ZL3073X");
|
||||
-
|
||||
-/* Chip IDs for zl30735 */
|
||||
-static const u16 zl30735_ids[] = {
|
||||
- 0x0E97,
|
||||
- 0x1E97,
|
||||
- 0x2E97,
|
||||
-};
|
||||
-
|
||||
-const struct zl3073x_chip_info zl30735_chip_info = {
|
||||
- .ids = zl30735_ids,
|
||||
- .num_ids = ARRAY_SIZE(zl30735_ids),
|
||||
- .num_channels = 5,
|
||||
-};
|
||||
-EXPORT_SYMBOL_NS_GPL(zl30735_chip_info, "ZL3073X");
|
||||
-
|
||||
#define ZL_RANGE_OFFSET 0x80
|
||||
#define ZL_PAGE_SIZE 0x80
|
||||
#define ZL_NUM_PAGES 256
|
||||
@@ -942,7 +893,7 @@ static void zl3073x_dev_dpll_fini(void *ptr)
|
||||
}
|
||||
|
||||
static int
|
||||
-zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
||||
+zl3073x_devm_dpll_init(struct zl3073x_dev *zldev)
|
||||
{
|
||||
struct kthread_worker *kworker;
|
||||
struct zl3073x_dpll *zldpll;
|
||||
@@ -952,7 +903,7 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
||||
INIT_LIST_HEAD(&zldev->dplls);
|
||||
|
||||
/* Allocate all DPLLs */
|
||||
- for (i = 0; i < num_dplls; i++) {
|
||||
+ for (i = 0; i < zldev->info->num_channels; i++) {
|
||||
zldpll = zl3073x_dpll_alloc(zldev, i);
|
||||
if (IS_ERR(zldpll)) {
|
||||
dev_err_probe(zldev->dev, PTR_ERR(zldpll),
|
||||
@@ -992,14 +943,12 @@ zl3073x_devm_dpll_init(struct zl3073x_dev *zldev, u8 num_dplls)
|
||||
/**
|
||||
* zl3073x_dev_probe - initialize zl3073x device
|
||||
* @zldev: pointer to zl3073x device
|
||||
- * @chip_info: chip info based on compatible
|
||||
*
|
||||
* Common initialization of zl3073x device structure.
|
||||
*
|
||||
* Returns: 0 on success, <0 on error
|
||||
*/
|
||||
-int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
- const struct zl3073x_chip_info *chip_info)
|
||||
+int zl3073x_dev_probe(struct zl3073x_dev *zldev)
|
||||
{
|
||||
u16 id, revision, fw_ver;
|
||||
unsigned int i;
|
||||
@@ -1011,18 +960,17 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
- /* Check it matches */
|
||||
- for (i = 0; i < chip_info->num_ids; i++) {
|
||||
- if (id == chip_info->ids[i])
|
||||
+ /* Detect chip variant */
|
||||
+ for (i = 0; i < ARRAY_SIZE(zl3073x_chip_ids); i++) {
|
||||
+ if (zl3073x_chip_ids[i].id == id)
|
||||
break;
|
||||
}
|
||||
|
||||
- if (i == chip_info->num_ids) {
|
||||
+ if (i == ARRAY_SIZE(zl3073x_chip_ids))
|
||||
return dev_err_probe(zldev->dev, -ENODEV,
|
||||
- "Unknown or non-match chip ID: 0x%0x\n",
|
||||
- id);
|
||||
- }
|
||||
- zldev->chip_id = id;
|
||||
+ "Unknown chip ID: 0x%04x\n", id);
|
||||
+
|
||||
+ zldev->info = &zl3073x_chip_ids[i];
|
||||
|
||||
/* Read revision, firmware version and custom config version */
|
||||
rc = zl3073x_read_u16(zldev, ZL_REG_REVISION, &revision);
|
||||
@@ -1061,7 +1009,7 @@ int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
"Failed to initialize mutex\n");
|
||||
|
||||
/* Register DPLL channels */
|
||||
- rc = zl3073x_devm_dpll_init(zldev, chip_info->num_channels);
|
||||
+ rc = zl3073x_devm_dpll_init(zldev);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
|
||||
index fd2af3c62a7d..fde5c8371fbd 100644
|
||||
--- a/drivers/dpll/zl3073x/core.h
|
||||
+++ b/drivers/dpll/zl3073x/core.h
|
||||
@@ -30,12 +30,32 @@ struct zl3073x_dpll;
|
||||
#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
|
||||
ZL3073X_NUM_OUTPUT_PINS)
|
||||
|
||||
+enum zl3073x_flags {
|
||||
+ ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
|
||||
+ ZL3073X_FLAGS_NBITS /* must be last */
|
||||
+};
|
||||
+
|
||||
+#define __ZL3073X_FLAG(name) BIT(ZL3073X_FLAG_ ## name ## _BIT)
|
||||
+#define ZL3073X_FLAG_REF_PHASE_COMP_32 __ZL3073X_FLAG(REF_PHASE_COMP_32)
|
||||
+
|
||||
+/**
|
||||
+ * struct zl3073x_chip_info - chip variant identification
|
||||
+ * @id: chip ID
|
||||
+ * @num_channels: number of DPLL channels supported by this variant
|
||||
+ * @flags: chip variant flags
|
||||
+ */
|
||||
+struct zl3073x_chip_info {
|
||||
+ u16 id;
|
||||
+ u8 num_channels;
|
||||
+ unsigned long flags;
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* struct zl3073x_dev - zl3073x device
|
||||
* @dev: pointer to device
|
||||
* @regmap: regmap to access device registers
|
||||
+ * @info: detected chip info
|
||||
* @multiop_lock: to serialize multiple register operations
|
||||
- * @chip_id: chip ID read from hardware
|
||||
* @ref: array of input references' invariants
|
||||
* @out: array of outs' invariants
|
||||
* @synth: array of synths' invariants
|
||||
@@ -46,10 +66,10 @@ struct zl3073x_dpll;
|
||||
* @phase_avg_factor: phase offset measurement averaging factor
|
||||
*/
|
||||
struct zl3073x_dev {
|
||||
- struct device *dev;
|
||||
- struct regmap *regmap;
|
||||
- struct mutex multiop_lock;
|
||||
- u16 chip_id;
|
||||
+ struct device *dev;
|
||||
+ struct regmap *regmap;
|
||||
+ const struct zl3073x_chip_info *info;
|
||||
+ struct mutex multiop_lock;
|
||||
|
||||
/* Invariants */
|
||||
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
||||
@@ -68,22 +88,10 @@ struct zl3073x_dev {
|
||||
u8 phase_avg_factor;
|
||||
};
|
||||
|
||||
-struct zl3073x_chip_info {
|
||||
- const u16 *ids;
|
||||
- size_t num_ids;
|
||||
- int num_channels;
|
||||
-};
|
||||
-
|
||||
-extern const struct zl3073x_chip_info zl30731_chip_info;
|
||||
-extern const struct zl3073x_chip_info zl30732_chip_info;
|
||||
-extern const struct zl3073x_chip_info zl30733_chip_info;
|
||||
-extern const struct zl3073x_chip_info zl30734_chip_info;
|
||||
-extern const struct zl3073x_chip_info zl30735_chip_info;
|
||||
extern const struct regmap_config zl3073x_regmap_config;
|
||||
|
||||
struct zl3073x_dev *zl3073x_devm_alloc(struct device *dev);
|
||||
-int zl3073x_dev_probe(struct zl3073x_dev *zldev,
|
||||
- const struct zl3073x_chip_info *chip_info);
|
||||
+int zl3073x_dev_probe(struct zl3073x_dev *zldev);
|
||||
|
||||
int zl3073x_dev_start(struct zl3073x_dev *zldev, bool full);
|
||||
void zl3073x_dev_stop(struct zl3073x_dev *zldev);
|
||||
@@ -158,18 +166,7 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel);
|
||||
static inline bool
|
||||
zl3073x_dev_is_ref_phase_comp_32bit(struct zl3073x_dev *zldev)
|
||||
{
|
||||
- switch (zldev->chip_id) {
|
||||
- case 0x0E30:
|
||||
- case 0x0E93:
|
||||
- case 0x0E94:
|
||||
- case 0x0E95:
|
||||
- case 0x0E96:
|
||||
- case 0x0E97:
|
||||
- case 0x1F60:
|
||||
- return true;
|
||||
- default:
|
||||
- return false;
|
||||
- }
|
||||
+ return zldev->info->flags & ZL3073X_FLAG_REF_PHASE_COMP_32;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
diff --git a/drivers/dpll/zl3073x/i2c.c b/drivers/dpll/zl3073x/i2c.c
|
||||
index 7bbfdd4ed867..979df85826ab 100644
|
||||
--- a/drivers/dpll/zl3073x/i2c.c
|
||||
+++ b/drivers/dpll/zl3073x/i2c.c
|
||||
@@ -22,40 +22,25 @@ static int zl3073x_i2c_probe(struct i2c_client *client)
|
||||
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
|
||||
"Failed to initialize regmap\n");
|
||||
|
||||
- return zl3073x_dev_probe(zldev, i2c_get_match_data(client));
|
||||
+ return zl3073x_dev_probe(zldev);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id zl3073x_i2c_id[] = {
|
||||
- {
|
||||
- .name = "zl30731",
|
||||
- .driver_data = (kernel_ulong_t)&zl30731_chip_info,
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30732",
|
||||
- .driver_data = (kernel_ulong_t)&zl30732_chip_info,
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30733",
|
||||
- .driver_data = (kernel_ulong_t)&zl30733_chip_info,
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30734",
|
||||
- .driver_data = (kernel_ulong_t)&zl30734_chip_info,
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30735",
|
||||
- .driver_data = (kernel_ulong_t)&zl30735_chip_info,
|
||||
- },
|
||||
+ { "zl30731" },
|
||||
+ { "zl30732" },
|
||||
+ { "zl30733" },
|
||||
+ { "zl30734" },
|
||||
+ { "zl30735" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, zl3073x_i2c_id);
|
||||
|
||||
static const struct of_device_id zl3073x_i2c_of_match[] = {
|
||||
- { .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
|
||||
- { .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
|
||||
- { .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
|
||||
- { .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
|
||||
- { .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
|
||||
+ { .compatible = "microchip,zl30731" },
|
||||
+ { .compatible = "microchip,zl30732" },
|
||||
+ { .compatible = "microchip,zl30733" },
|
||||
+ { .compatible = "microchip,zl30734" },
|
||||
+ { .compatible = "microchip,zl30735" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zl3073x_i2c_of_match);
|
||||
diff --git a/drivers/dpll/zl3073x/spi.c b/drivers/dpll/zl3073x/spi.c
|
||||
index af901b4d6dda..f024f42b78d0 100644
|
||||
--- a/drivers/dpll/zl3073x/spi.c
|
||||
+++ b/drivers/dpll/zl3073x/spi.c
|
||||
@@ -22,40 +22,25 @@ static int zl3073x_spi_probe(struct spi_device *spi)
|
||||
return dev_err_probe(dev, PTR_ERR(zldev->regmap),
|
||||
"Failed to initialize regmap\n");
|
||||
|
||||
- return zl3073x_dev_probe(zldev, spi_get_device_match_data(spi));
|
||||
+ return zl3073x_dev_probe(zldev);
|
||||
}
|
||||
|
||||
static const struct spi_device_id zl3073x_spi_id[] = {
|
||||
- {
|
||||
- .name = "zl30731",
|
||||
- .driver_data = (kernel_ulong_t)&zl30731_chip_info
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30732",
|
||||
- .driver_data = (kernel_ulong_t)&zl30732_chip_info,
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30733",
|
||||
- .driver_data = (kernel_ulong_t)&zl30733_chip_info,
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30734",
|
||||
- .driver_data = (kernel_ulong_t)&zl30734_chip_info,
|
||||
- },
|
||||
- {
|
||||
- .name = "zl30735",
|
||||
- .driver_data = (kernel_ulong_t)&zl30735_chip_info,
|
||||
- },
|
||||
+ { "zl30731" },
|
||||
+ { "zl30732" },
|
||||
+ { "zl30733" },
|
||||
+ { "zl30734" },
|
||||
+ { "zl30735" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, zl3073x_spi_id);
|
||||
|
||||
static const struct of_device_id zl3073x_spi_of_match[] = {
|
||||
- { .compatible = "microchip,zl30731", .data = &zl30731_chip_info },
|
||||
- { .compatible = "microchip,zl30732", .data = &zl30732_chip_info },
|
||||
- { .compatible = "microchip,zl30733", .data = &zl30733_chip_info },
|
||||
- { .compatible = "microchip,zl30734", .data = &zl30734_chip_info },
|
||||
- { .compatible = "microchip,zl30735", .data = &zl30735_chip_info },
|
||||
+ { .compatible = "microchip,zl30731" },
|
||||
+ { .compatible = "microchip,zl30732" },
|
||||
+ { .compatible = "microchip,zl30733" },
|
||||
+ { .compatible = "microchip,zl30734" },
|
||||
+ { .compatible = "microchip,zl30735" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, zl3073x_spi_of_match);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,183 @@
|
||||
From 0320baa880dd1e760bf8d15d5d8da898b265f4ed Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:50:27 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: add die temperature reporting for supported
|
||||
chips
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit 3a97e02b3e91e4d40095ad9bb6e466d8d7c1a1bc
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Fri Feb 27 11:53:00 2026 +0100
|
||||
|
||||
dpll: zl3073x: add die temperature reporting for supported chips
|
||||
|
||||
Some zl3073x chip variants (0x1Exx, 0x2Exx and 0x3FC4) provide a die
|
||||
temperature status register with 0.1 C resolution.
|
||||
|
||||
Add a ZL3073X_FLAG_DIE_TEMP chip flag to identify these variants and
|
||||
implement zl3073x_dpll_temp_get() as the dpll_device_ops.temp_get
|
||||
callback. The register value is converted from 0.1 C units to
|
||||
millidegrees as expected by the DPLL subsystem.
|
||||
|
||||
To support per-instance ops selection, copy the base dpll_device_ops
|
||||
into struct zl3073x_dpll and conditionally set .temp_get during device
|
||||
registration based on the chip flag.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260227105300.710272-3-ivecera@redhat.com
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
(cherry picked from commit 3a97e02b3e91e4d40095ad9bb6e466d8d7c1a1bc)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index c8af34301045..10e036ccf08f 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -30,18 +30,18 @@ static const struct zl3073x_chip_info zl3073x_chip_ids[] = {
|
||||
ZL_CHIP_INFO(0x0E95, 3, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x0E96, 4, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
ZL_CHIP_INFO(0x0E97, 5, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
- ZL_CHIP_INFO(0x1E93, 1, 0),
|
||||
- ZL_CHIP_INFO(0x1E94, 2, 0),
|
||||
- ZL_CHIP_INFO(0x1E95, 3, 0),
|
||||
- ZL_CHIP_INFO(0x1E96, 4, 0),
|
||||
- ZL_CHIP_INFO(0x1E97, 5, 0),
|
||||
+ ZL_CHIP_INFO(0x1E93, 1, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x1E94, 2, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x1E95, 3, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x1E96, 4, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x1E97, 5, ZL3073X_FLAG_DIE_TEMP),
|
||||
ZL_CHIP_INFO(0x1F60, 2, ZL3073X_FLAG_REF_PHASE_COMP_32),
|
||||
- ZL_CHIP_INFO(0x2E93, 1, 0),
|
||||
- ZL_CHIP_INFO(0x2E94, 2, 0),
|
||||
- ZL_CHIP_INFO(0x2E95, 3, 0),
|
||||
- ZL_CHIP_INFO(0x2E96, 4, 0),
|
||||
- ZL_CHIP_INFO(0x2E97, 5, 0),
|
||||
- ZL_CHIP_INFO(0x3FC4, 2, 0),
|
||||
+ ZL_CHIP_INFO(0x2E93, 1, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x2E94, 2, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x2E95, 3, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x2E96, 4, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x2E97, 5, ZL3073X_FLAG_DIE_TEMP),
|
||||
+ ZL_CHIP_INFO(0x3FC4, 2, ZL3073X_FLAG_DIE_TEMP),
|
||||
};
|
||||
|
||||
#define ZL_RANGE_OFFSET 0x80
|
||||
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
|
||||
index fde5c8371fbd..b6f22ee1c0bd 100644
|
||||
--- a/drivers/dpll/zl3073x/core.h
|
||||
+++ b/drivers/dpll/zl3073x/core.h
|
||||
@@ -32,11 +32,13 @@ struct zl3073x_dpll;
|
||||
|
||||
enum zl3073x_flags {
|
||||
ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
|
||||
+ ZL3073X_FLAG_DIE_TEMP_BIT,
|
||||
ZL3073X_FLAGS_NBITS /* must be last */
|
||||
};
|
||||
|
||||
#define __ZL3073X_FLAG(name) BIT(ZL3073X_FLAG_ ## name ## _BIT)
|
||||
#define ZL3073X_FLAG_REF_PHASE_COMP_32 __ZL3073X_FLAG(REF_PHASE_COMP_32)
|
||||
+#define ZL3073X_FLAG_DIE_TEMP __ZL3073X_FLAG(DIE_TEMP)
|
||||
|
||||
/**
|
||||
* struct zl3073x_chip_info - chip variant identification
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 8ffbede117c6..31926a74c414 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -1065,6 +1065,25 @@ zl3073x_dpll_output_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+zl3073x_dpll_temp_get(const struct dpll_device *dpll, void *dpll_priv,
|
||||
+ s32 *temp, struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
+ struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ u16 val;
|
||||
+ int rc;
|
||||
+
|
||||
+ rc = zl3073x_read_u16(zldev, ZL_REG_DIE_TEMP_STATUS, &val);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ /* Register value is in units of 0.1 C, convert to millidegrees */
|
||||
+ *temp = (s16)val * 100;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_lock_status *status,
|
||||
@@ -1671,6 +1690,10 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
|
||||
zldpll->forced_ref = FIELD_GET(ZL_DPLL_MODE_REFSEL_REF,
|
||||
dpll_mode_refsel);
|
||||
|
||||
+ zldpll->ops = zl3073x_dpll_device_ops;
|
||||
+ if (zldev->info->flags & ZL3073X_FLAG_DIE_TEMP)
|
||||
+ zldpll->ops.temp_get = zl3073x_dpll_temp_get;
|
||||
+
|
||||
zldpll->dpll_dev = dpll_device_get(zldev->clock_id, zldpll->id,
|
||||
THIS_MODULE, &zldpll->tracker);
|
||||
if (IS_ERR(zldpll->dpll_dev)) {
|
||||
@@ -1682,7 +1705,7 @@ zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
|
||||
|
||||
rc = dpll_device_register(zldpll->dpll_dev,
|
||||
zl3073x_prop_dpll_type_get(zldev, zldpll->id),
|
||||
- &zl3073x_dpll_device_ops, zldpll);
|
||||
+ &zldpll->ops, zldpll);
|
||||
if (rc) {
|
||||
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
|
||||
zldpll->dpll_dev = NULL;
|
||||
@@ -1705,8 +1728,7 @@ zl3073x_dpll_device_unregister(struct zl3073x_dpll *zldpll)
|
||||
|
||||
cancel_work_sync(&zldpll->change_work);
|
||||
|
||||
- dpll_device_unregister(zldpll->dpll_dev, &zl3073x_dpll_device_ops,
|
||||
- zldpll);
|
||||
+ dpll_device_unregister(zldpll->dpll_dev, &zldpll->ops, zldpll);
|
||||
dpll_device_put(zldpll->dpll_dev, &zldpll->tracker);
|
||||
zldpll->dpll_dev = NULL;
|
||||
}
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h
|
||||
index c65c798c3792..278a24f357c9 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.h
|
||||
+++ b/drivers/dpll/zl3073x/dpll.h
|
||||
@@ -17,6 +17,7 @@
|
||||
* @forced_ref: selected reference in forced reference lock mode
|
||||
* @check_count: periodic check counter
|
||||
* @phase_monitor: is phase offset monitor enabled
|
||||
+ * @ops: DPLL device operations for this instance
|
||||
* @dpll_dev: pointer to registered DPLL device
|
||||
* @tracker: tracking object for the acquired reference
|
||||
* @lock_status: last saved DPLL lock status
|
||||
@@ -31,6 +32,7 @@ struct zl3073x_dpll {
|
||||
u8 forced_ref;
|
||||
u8 check_count;
|
||||
bool phase_monitor;
|
||||
+ struct dpll_device_ops ops;
|
||||
struct dpll_device *dpll_dev;
|
||||
dpll_tracker tracker;
|
||||
enum dpll_lock_status lock_status;
|
||||
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
|
||||
index 5573d7188406..19c598daa784 100644
|
||||
--- a/drivers/dpll/zl3073x/regs.h
|
||||
+++ b/drivers/dpll/zl3073x/regs.h
|
||||
@@ -78,6 +78,8 @@
|
||||
#define ZL_REG_RESET_STATUS ZL_REG(0, 0x18, 1)
|
||||
#define ZL_REG_RESET_STATUS_RESET BIT(0)
|
||||
|
||||
+#define ZL_REG_DIE_TEMP_STATUS ZL_REG(0, 0x44, 2)
|
||||
+
|
||||
/*************************
|
||||
* Register Page 2, Status
|
||||
*************************/
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
305
1252-dpll-zl3073x-use-struct-group-to-partition-states.patch
Normal file
305
1252-dpll-zl3073x-use-struct-group-to-partition-states.patch
Normal file
@ -0,0 +1,305 @@
|
||||
From f222542cbe20b882e741d32029bd9d3a2136cb1c Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:50:47 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: use struct_group to partition states
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit f327f5a8115e80d954598cf2d5c461873042b7f6
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Sun Mar 15 18:42:19 2026 +0100
|
||||
|
||||
dpll: zl3073x: use struct_group to partition states
|
||||
|
||||
Organize the zl3073x_out, zl3073x_ref, and zl3073x_synth structures
|
||||
using struct_group() to partition fields into semantic groups:
|
||||
|
||||
* cfg: mutable configuration written to HW via state_set
|
||||
* inv: invariant fields set once during state_fetch
|
||||
* stat: read-only status
|
||||
|
||||
This enables group-level operations in place of field-by-field copies:
|
||||
|
||||
* state_set validates invariants haven't changed (WARN_ON + -EINVAL)
|
||||
* state_set short-circuits when cfg is unchanged
|
||||
* state_set copy entire groups in a single assignment instead of
|
||||
enumerating each field
|
||||
|
||||
Add kernel doc for zl3073x_out_state_set and zl3073x_ref_state_set
|
||||
documenting the new invariant validation and short-circuit semantics.
|
||||
|
||||
Remove forward declaration of zl3073x_synth_state_set().
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260315174224.399074-2-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit f327f5a8115e80d954598cf2d5c461873042b7f6)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/out.c b/drivers/dpll/zl3073x/out.c
|
||||
index 86829a0c1c02..eb5628aebcee 100644
|
||||
--- a/drivers/dpll/zl3073x/out.c
|
||||
+++ b/drivers/dpll/zl3073x/out.c
|
||||
@@ -106,12 +106,32 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
||||
return &zldev->out[index];
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_out_state_set - commit output state changes to hardware
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ * @index: output index to set state for
|
||||
+ * @out: desired output state
|
||||
+ *
|
||||
+ * Validates that invariant fields have not been modified, skips the HW
|
||||
+ * write if the mutable configuration is unchanged, and otherwise writes
|
||||
+ * only the changed cfg fields to hardware via the mailbox interface.
|
||||
+ *
|
||||
+ * Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
|
||||
+ */
|
||||
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_out *out)
|
||||
{
|
||||
struct zl3073x_out *dout = &zldev->out[index];
|
||||
int rc;
|
||||
|
||||
+ /* Reject attempts to change invariant fields (set at fetch only) */
|
||||
+ if (WARN_ON(memcmp(&dout->inv, &out->inv, sizeof(out->inv))))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Skip HW write if configuration hasn't changed */
|
||||
+ if (!memcmp(&dout->cfg, &out->cfg, sizeof(out->cfg)))
|
||||
+ return 0;
|
||||
+
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read output configuration into mailbox */
|
||||
@@ -146,12 +166,7 @@ int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
return rc;
|
||||
|
||||
/* After successful commit store new state */
|
||||
- dout->div = out->div;
|
||||
- dout->width = out->width;
|
||||
- dout->esync_n_period = out->esync_n_period;
|
||||
- dout->esync_n_width = out->esync_n_width;
|
||||
- dout->mode = out->mode;
|
||||
- dout->phase_comp = out->phase_comp;
|
||||
+ dout->cfg = out->cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
diff --git a/drivers/dpll/zl3073x/out.h b/drivers/dpll/zl3073x/out.h
|
||||
index 318f9bb8da3a..edf40432bba5 100644
|
||||
--- a/drivers/dpll/zl3073x/out.h
|
||||
+++ b/drivers/dpll/zl3073x/out.h
|
||||
@@ -4,6 +4,7 @@
|
||||
#define _ZL3073X_OUT_H
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
+#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
@@ -17,17 +18,21 @@ struct zl3073x_dev;
|
||||
* @esync_n_period: embedded sync or n-pin period (for n-div formats)
|
||||
* @esync_n_width: embedded sync or n-pin pulse width
|
||||
* @phase_comp: phase compensation
|
||||
- * @ctrl: output control
|
||||
* @mode: output mode
|
||||
+ * @ctrl: output control
|
||||
*/
|
||||
struct zl3073x_out {
|
||||
- u32 div;
|
||||
- u32 width;
|
||||
- u32 esync_n_period;
|
||||
- u32 esync_n_width;
|
||||
- s32 phase_comp;
|
||||
- u8 ctrl;
|
||||
- u8 mode;
|
||||
+ struct_group(cfg, /* Config */
|
||||
+ u32 div;
|
||||
+ u32 width;
|
||||
+ u32 esync_n_period;
|
||||
+ u32 esync_n_width;
|
||||
+ s32 phase_comp;
|
||||
+ u8 mode;
|
||||
+ );
|
||||
+ struct_group(inv, /* Invariants */
|
||||
+ u8 ctrl;
|
||||
+ );
|
||||
};
|
||||
|
||||
int zl3073x_out_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
diff --git a/drivers/dpll/zl3073x/ref.c b/drivers/dpll/zl3073x/ref.c
|
||||
index 6b65e6103999..8b4c4807bcc4 100644
|
||||
--- a/drivers/dpll/zl3073x/ref.c
|
||||
+++ b/drivers/dpll/zl3073x/ref.c
|
||||
@@ -73,14 +73,8 @@ int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
struct zl3073x_ref *p_ref = ref - 1; /* P-pin counterpart*/
|
||||
|
||||
/* Copy the shared items from the P-pin */
|
||||
- ref->config = p_ref->config;
|
||||
- ref->esync_n_div = p_ref->esync_n_div;
|
||||
- ref->freq_base = p_ref->freq_base;
|
||||
- ref->freq_mult = p_ref->freq_mult;
|
||||
- ref->freq_ratio_m = p_ref->freq_ratio_m;
|
||||
- ref->freq_ratio_n = p_ref->freq_ratio_n;
|
||||
- ref->phase_comp = p_ref->phase_comp;
|
||||
- ref->sync_ctrl = p_ref->sync_ctrl;
|
||||
+ ref->cfg = p_ref->cfg;
|
||||
+ ref->inv = p_ref->inv;
|
||||
|
||||
return 0; /* Finish - no non-shared items for now */
|
||||
}
|
||||
@@ -154,12 +148,32 @@ zl3073x_ref_state_get(struct zl3073x_dev *zldev, u8 index)
|
||||
return &zldev->ref[index];
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_ref_state_set - commit input reference state changes to hardware
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ * @index: input reference index to set state for
|
||||
+ * @ref: desired reference state
|
||||
+ *
|
||||
+ * Validates that invariant fields have not been modified, skips the HW
|
||||
+ * write if the mutable configuration is unchanged, and otherwise writes
|
||||
+ * only the changed cfg fields to hardware via the mailbox interface.
|
||||
+ *
|
||||
+ * Return: 0 on success, -EINVAL if invariants changed, <0 on HW error
|
||||
+ */
|
||||
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_ref *ref)
|
||||
{
|
||||
struct zl3073x_ref *dref = &zldev->ref[index];
|
||||
int rc;
|
||||
|
||||
+ /* Reject attempts to change invariant fields (set at init only) */
|
||||
+ if (WARN_ON(memcmp(&dref->inv, &ref->inv, sizeof(ref->inv))))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Skip HW write if configuration hasn't changed */
|
||||
+ if (!memcmp(&dref->cfg, &ref->cfg, sizeof(ref->cfg)))
|
||||
+ return 0;
|
||||
+
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read reference configuration into mailbox */
|
||||
@@ -207,13 +221,7 @@ int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
return rc;
|
||||
|
||||
/* After successful commit store new state */
|
||||
- dref->freq_base = ref->freq_base;
|
||||
- dref->freq_mult = ref->freq_mult;
|
||||
- dref->freq_ratio_m = ref->freq_ratio_m;
|
||||
- dref->freq_ratio_n = ref->freq_ratio_n;
|
||||
- dref->esync_n_div = ref->esync_n_div;
|
||||
- dref->sync_ctrl = ref->sync_ctrl;
|
||||
- dref->phase_comp = ref->phase_comp;
|
||||
+ dref->cfg = ref->cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
|
||||
index 0d8618f5ce8d..ab3a816c2910 100644
|
||||
--- a/drivers/dpll/zl3073x/ref.h
|
||||
+++ b/drivers/dpll/zl3073x/ref.h
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/math64.h>
|
||||
+#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
@@ -13,28 +14,34 @@ struct zl3073x_dev;
|
||||
|
||||
/**
|
||||
* struct zl3073x_ref - input reference state
|
||||
- * @ffo: current fractional frequency offset
|
||||
* @phase_comp: phase compensation
|
||||
* @esync_n_div: divisor for embedded sync or n-divided signal formats
|
||||
* @freq_base: frequency base
|
||||
* @freq_mult: frequnecy multiplier
|
||||
* @freq_ratio_m: FEC mode multiplier
|
||||
* @freq_ratio_n: FEC mode divisor
|
||||
- * @config: reference config
|
||||
* @sync_ctrl: reference sync control
|
||||
+ * @config: reference config
|
||||
+ * @ffo: current fractional frequency offset
|
||||
* @mon_status: reference monitor status
|
||||
*/
|
||||
struct zl3073x_ref {
|
||||
- s64 ffo;
|
||||
- u64 phase_comp;
|
||||
- u32 esync_n_div;
|
||||
- u16 freq_base;
|
||||
- u16 freq_mult;
|
||||
- u16 freq_ratio_m;
|
||||
- u16 freq_ratio_n;
|
||||
- u8 config;
|
||||
- u8 sync_ctrl;
|
||||
- u8 mon_status;
|
||||
+ struct_group(cfg, /* Configuration */
|
||||
+ u64 phase_comp;
|
||||
+ u32 esync_n_div;
|
||||
+ u16 freq_base;
|
||||
+ u16 freq_mult;
|
||||
+ u16 freq_ratio_m;
|
||||
+ u16 freq_ratio_n;
|
||||
+ u8 sync_ctrl;
|
||||
+ );
|
||||
+ struct_group(inv, /* Invariants */
|
||||
+ u8 config;
|
||||
+ );
|
||||
+ struct_group(stat, /* Status */
|
||||
+ s64 ffo;
|
||||
+ u8 mon_status;
|
||||
+ );
|
||||
};
|
||||
|
||||
int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
diff --git a/drivers/dpll/zl3073x/synth.h b/drivers/dpll/zl3073x/synth.h
|
||||
index 6c55eb8a888c..89e13ea2e6d9 100644
|
||||
--- a/drivers/dpll/zl3073x/synth.h
|
||||
+++ b/drivers/dpll/zl3073x/synth.h
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/math64.h>
|
||||
+#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include "regs.h"
|
||||
@@ -20,11 +21,13 @@ struct zl3073x_dev;
|
||||
* @ctrl: synth control
|
||||
*/
|
||||
struct zl3073x_synth {
|
||||
- u32 freq_mult;
|
||||
- u16 freq_base;
|
||||
- u16 freq_m;
|
||||
- u16 freq_n;
|
||||
- u8 ctrl;
|
||||
+ struct_group(inv, /* Invariants */
|
||||
+ u32 freq_mult;
|
||||
+ u16 freq_base;
|
||||
+ u16 freq_m;
|
||||
+ u16 freq_n;
|
||||
+ u8 ctrl;
|
||||
+ );
|
||||
};
|
||||
|
||||
int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
|
||||
@@ -32,9 +35,6 @@ int zl3073x_synth_state_fetch(struct zl3073x_dev *zldev, u8 synth_id);
|
||||
const struct zl3073x_synth *zl3073x_synth_state_get(struct zl3073x_dev *zldev,
|
||||
u8 synth_id);
|
||||
|
||||
-int zl3073x_synth_state_set(struct zl3073x_dev *zldev, u8 synth_id,
|
||||
- const struct zl3073x_synth *synth);
|
||||
-
|
||||
/**
|
||||
* zl3073x_synth_dpll_get - get DPLL ID the synth is driven by
|
||||
* @synth: pointer to synth state
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
115
1253-dpll-zl3073x-add-zl3073x-ref-state-update-helper.patch
Normal file
115
1253-dpll-zl3073x-add-zl3073x-ref-state-update-helper.patch
Normal file
@ -0,0 +1,115 @@
|
||||
From 98b442e23ddaf8448ee6e7ed30dcbeb137a4f7ad Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:51:01 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: add zl3073x_ref_state_update helper
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit 05ea2ab3b10075e8fcfcd5e283e486df7055de5e
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Sun Mar 15 18:42:20 2026 +0100
|
||||
|
||||
dpll: zl3073x: add zl3073x_ref_state_update helper
|
||||
|
||||
Extract the per-reference monitor status HW read into a dedicated
|
||||
zl3073x_ref_state_update() helper in the ref module. Rename
|
||||
zl3073x_dev_ref_status_update() to zl3073x_dev_ref_states_update()
|
||||
and use the new helper in it. Call it from zl3073x_ref_state_fetch()
|
||||
as well so that mon_status is initialized at device startup. This
|
||||
keeps direct register access and struct field writes behind the ref
|
||||
module's interface, consistent with the state management pattern
|
||||
used for other ref operations.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260315174224.399074-3-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 05ea2ab3b10075e8fcfcd5e283e486df7055de5e)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index 10e036ccf08f..07626082aae3 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -543,13 +543,12 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
|
||||
}
|
||||
|
||||
static void
|
||||
-zl3073x_dev_ref_status_update(struct zl3073x_dev *zldev)
|
||||
+zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
|
||||
{
|
||||
int i, rc;
|
||||
|
||||
for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
||||
- rc = zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(i),
|
||||
- &zldev->ref[i].mon_status);
|
||||
+ rc = zl3073x_ref_state_update(zldev, i);
|
||||
if (rc)
|
||||
dev_warn(zldev->dev,
|
||||
"Failed to get REF%u status: %pe\n", i,
|
||||
@@ -679,8 +678,8 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
struct zl3073x_dpll *zldpll;
|
||||
int rc;
|
||||
|
||||
- /* Update input references status */
|
||||
- zl3073x_dev_ref_status_update(zldev);
|
||||
+ /* Update input references' states */
|
||||
+ zl3073x_dev_ref_states_update(zldev);
|
||||
|
||||
/* Update DPLL-to-connected-ref phase offsets registers */
|
||||
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
|
||||
diff --git a/drivers/dpll/zl3073x/ref.c b/drivers/dpll/zl3073x/ref.c
|
||||
index 8b4c4807bcc4..825ac30bcd6f 100644
|
||||
--- a/drivers/dpll/zl3073x/ref.c
|
||||
+++ b/drivers/dpll/zl3073x/ref.c
|
||||
@@ -51,6 +51,21 @@ zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_ref_state_update - update input reference status from HW
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ * @index: input reference index
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on error
|
||||
+ */
|
||||
+int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index)
|
||||
+{
|
||||
+ struct zl3073x_ref *ref = &zldev->ref[index];
|
||||
+
|
||||
+ return zl3073x_read_u8(zldev, ZL_REG_REF_MON_STATUS(index),
|
||||
+ &ref->mon_status);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_ref_state_fetch - fetch input reference state from hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
@@ -79,6 +94,11 @@ int zl3073x_ref_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
return 0; /* Finish - no non-shared items for now */
|
||||
}
|
||||
|
||||
+ /* Read reference status */
|
||||
+ rc = zl3073x_ref_state_update(zldev, index);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
guard(mutex)(&zldev->multiop_lock);
|
||||
|
||||
/* Read reference configuration */
|
||||
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
|
||||
index ab3a816c2910..06d8d4d97ea2 100644
|
||||
--- a/drivers/dpll/zl3073x/ref.h
|
||||
+++ b/drivers/dpll/zl3073x/ref.h
|
||||
@@ -52,6 +52,8 @@ const struct zl3073x_ref *zl3073x_ref_state_get(struct zl3073x_dev *zldev,
|
||||
int zl3073x_ref_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_ref *ref);
|
||||
|
||||
+int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index);
|
||||
+
|
||||
int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
|
||||
|
||||
/**
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,563 @@
|
||||
From 99589ed2ea279188527603207e5beeeeb232c7ed Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:51:15 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: introduce zl3073x_chan for DPLL channel state
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit 3032e95987fa0da656ce3a5eb454674e7cc60a12
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Sun Mar 15 18:42:21 2026 +0100
|
||||
|
||||
dpll: zl3073x: introduce zl3073x_chan for DPLL channel state
|
||||
|
||||
Extract DPLL channel state management into a dedicated zl3073x_chan
|
||||
module, following the pattern already established by zl3073x_ref,
|
||||
zl3073x_out and zl3073x_synth.
|
||||
|
||||
The new struct zl3073x_chan caches the raw mode_refsel register value
|
||||
in a cfg group with inline getters and setters to extract and update
|
||||
the bitfields. Three standard state management functions are provided:
|
||||
|
||||
- zl3073x_chan_state_fetch: read the mode_refsel register from HW
|
||||
- zl3073x_chan_state_get: return cached channel state
|
||||
- zl3073x_chan_state_set: write changed state to HW, skip if unchanged
|
||||
|
||||
The channel state array chan[ZL3073X_MAX_CHANNELS] is added to struct
|
||||
zl3073x_dev. Channel state is fetched as part of
|
||||
zl3073x_dev_state_fetch, using the chip-specific channel count.
|
||||
|
||||
The refsel_mode and forced_ref fields are removed from struct
|
||||
zl3073x_dpll and all direct register accesses in dpll.c are replaced
|
||||
with the new chan state operations.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260315174224.399074-4-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 3032e95987fa0da656ce3a5eb454674e7cc60a12)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/Makefile b/drivers/dpll/zl3073x/Makefile
|
||||
index bd324c7fe710..906ec3fbcc20 100644
|
||||
--- a/drivers/dpll/zl3073x/Makefile
|
||||
+++ b/drivers/dpll/zl3073x/Makefile
|
||||
@@ -1,8 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_ZL3073X) += zl3073x.o
|
||||
-zl3073x-objs := core.o devlink.o dpll.o flash.o fw.o \
|
||||
- out.o prop.o ref.o synth.o
|
||||
+zl3073x-objs := chan.o core.o devlink.o dpll.o \
|
||||
+ flash.o fw.o out.o prop.o ref.o synth.o
|
||||
|
||||
obj-$(CONFIG_ZL3073X_I2C) += zl3073x_i2c.o
|
||||
zl3073x_i2c-objs := i2c.o
|
||||
diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c
|
||||
new file mode 100644
|
||||
index 000000000000..8019b8ce7351
|
||||
--- /dev/null
|
||||
+++ b/drivers/dpll/zl3073x/chan.c
|
||||
@@ -0,0 +1,79 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+
|
||||
+#include <linux/dev_printk.h>
|
||||
+#include <linux/string.h>
|
||||
+#include <linux/types.h>
|
||||
+
|
||||
+#include "chan.h"
|
||||
+#include "core.h"
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_state_fetch - fetch DPLL channel state from hardware
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ * @index: DPLL channel index to fetch state for
|
||||
+ *
|
||||
+ * Reads the mode_refsel register for the given DPLL channel and stores
|
||||
+ * the raw value for later use.
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on error
|
||||
+ */
|
||||
+int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
+{
|
||||
+ struct zl3073x_chan *chan = &zldev->chan[index];
|
||||
+ int rc;
|
||||
+
|
||||
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
||||
+ &chan->mode_refsel);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index,
|
||||
+ zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan));
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_state_get - get current DPLL channel state
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ * @index: DPLL channel index to get state for
|
||||
+ *
|
||||
+ * Return: pointer to given DPLL channel state
|
||||
+ */
|
||||
+const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
||||
+ u8 index)
|
||||
+{
|
||||
+ return &zldev->chan[index];
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_state_set - commit DPLL channel state changes to hardware
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ * @index: DPLL channel index to set state for
|
||||
+ * @chan: desired channel state
|
||||
+ *
|
||||
+ * Skips the HW write if the configuration is unchanged, and otherwise
|
||||
+ * writes the mode_refsel register to hardware.
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on HW error
|
||||
+ */
|
||||
+int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
+ const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ struct zl3073x_chan *dchan = &zldev->chan[index];
|
||||
+ int rc;
|
||||
+
|
||||
+ /* Skip HW write if configuration hasn't changed */
|
||||
+ if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg)))
|
||||
+ return 0;
|
||||
+
|
||||
+ rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
||||
+ chan->mode_refsel);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ /* After successful write store new state */
|
||||
+ dchan->cfg = chan->cfg;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
|
||||
new file mode 100644
|
||||
index 000000000000..3e6ffaef0c74
|
||||
--- /dev/null
|
||||
+++ b/drivers/dpll/zl3073x/chan.h
|
||||
@@ -0,0 +1,74 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
+
|
||||
+#ifndef _ZL3073X_CHAN_H
|
||||
+#define _ZL3073X_CHAN_H
|
||||
+
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/stddef.h>
|
||||
+#include <linux/types.h>
|
||||
+
|
||||
+#include "regs.h"
|
||||
+
|
||||
+struct zl3073x_dev;
|
||||
+
|
||||
+/**
|
||||
+ * struct zl3073x_chan - DPLL channel state
|
||||
+ * @mode_refsel: mode and reference selection register value
|
||||
+ */
|
||||
+struct zl3073x_chan {
|
||||
+ struct_group(cfg,
|
||||
+ u8 mode_refsel;
|
||||
+ );
|
||||
+};
|
||||
+
|
||||
+int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
+const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
||||
+ u8 index);
|
||||
+int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
+ const struct zl3073x_chan *chan);
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_mode_get - get DPLL channel operating mode
|
||||
+ * @chan: pointer to channel state
|
||||
+ *
|
||||
+ * Return: reference selection mode of the given DPLL channel
|
||||
+ */
|
||||
+static inline u8 zl3073x_chan_mode_get(const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE, chan->mode_refsel);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_ref_get - get manually selected reference
|
||||
+ * @chan: pointer to channel state
|
||||
+ *
|
||||
+ * Return: reference selected in forced reference lock mode
|
||||
+ */
|
||||
+static inline u8 zl3073x_chan_ref_get(const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_DPLL_MODE_REFSEL_REF, chan->mode_refsel);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_mode_set - set DPLL channel operating mode
|
||||
+ * @chan: pointer to channel state
|
||||
+ * @mode: mode to set
|
||||
+ */
|
||||
+static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)
|
||||
+{
|
||||
+ chan->mode_refsel &= ~ZL_DPLL_MODE_REFSEL_MODE;
|
||||
+ chan->mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, mode);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_ref_set - set manually selected reference
|
||||
+ * @chan: pointer to channel state
|
||||
+ * @ref: reference to set
|
||||
+ */
|
||||
+static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
|
||||
+{
|
||||
+ chan->mode_refsel &= ~ZL_DPLL_MODE_REFSEL_REF;
|
||||
+ chan->mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
||||
+}
|
||||
+
|
||||
+#endif /* _ZL3073X_CHAN_H */
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index 07626082aae3..b03e59fa0834 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -539,6 +539,16 @@ zl3073x_dev_state_fetch(struct zl3073x_dev *zldev)
|
||||
}
|
||||
}
|
||||
|
||||
+ for (i = 0; i < zldev->info->num_channels; i++) {
|
||||
+ rc = zl3073x_chan_state_fetch(zldev, i);
|
||||
+ if (rc) {
|
||||
+ dev_err(zldev->dev,
|
||||
+ "Failed to fetch channel state: %pe\n",
|
||||
+ ERR_PTR(rc));
|
||||
+ return rc;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
return rc;
|
||||
}
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
|
||||
index b6f22ee1c0bd..2cfb9dd74aa5 100644
|
||||
--- a/drivers/dpll/zl3073x/core.h
|
||||
+++ b/drivers/dpll/zl3073x/core.h
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
+#include "chan.h"
|
||||
#include "out.h"
|
||||
#include "ref.h"
|
||||
#include "regs.h"
|
||||
@@ -61,6 +62,7 @@ struct zl3073x_chip_info {
|
||||
* @ref: array of input references' invariants
|
||||
* @out: array of outs' invariants
|
||||
* @synth: array of synths' invariants
|
||||
+ * @chan: array of DPLL channels' state
|
||||
* @dplls: list of DPLLs
|
||||
* @kworker: thread for periodic work
|
||||
* @work: periodic work
|
||||
@@ -77,6 +79,7 @@ struct zl3073x_dev {
|
||||
struct zl3073x_ref ref[ZL3073X_NUM_REFS];
|
||||
struct zl3073x_out out[ZL3073X_NUM_OUTS];
|
||||
struct zl3073x_synth synth[ZL3073X_NUM_SYNTHS];
|
||||
+ struct zl3073x_chan chan[ZL3073X_MAX_CHANNELS];
|
||||
|
||||
/* DPLL channels */
|
||||
struct list_head dplls;
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 31926a74c414..767af6dd724d 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -259,10 +259,13 @@ static int
|
||||
zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
||||
{
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
u8 state, value;
|
||||
int rc;
|
||||
|
||||
- switch (zldpll->refsel_mode) {
|
||||
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+
|
||||
+ switch (zl3073x_chan_mode_get(chan)) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
|
||||
/* For automatic mode read refsel_status register */
|
||||
rc = zl3073x_read_u8(zldev,
|
||||
@@ -282,7 +285,7 @@ zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
||||
break;
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
||||
/* For manual mode return stored value */
|
||||
- *ref = zldpll->forced_ref;
|
||||
+ *ref = zl3073x_chan_ref_get(chan);
|
||||
break;
|
||||
default:
|
||||
/* For other modes like NCO, freerun... there is no input ref */
|
||||
@@ -307,10 +310,11 @@ static int
|
||||
zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref)
|
||||
{
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- u8 mode, mode_refsel;
|
||||
- int rc;
|
||||
+ struct zl3073x_chan chan;
|
||||
+ u8 mode;
|
||||
|
||||
- mode = zldpll->refsel_mode;
|
||||
+ chan = *zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+ mode = zl3073x_chan_mode_get(&chan);
|
||||
|
||||
switch (mode) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
||||
@@ -328,8 +332,8 @@ zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref)
|
||||
break;
|
||||
}
|
||||
/* Keep selected reference */
|
||||
- ref = zldpll->forced_ref;
|
||||
- } else if (ref == zldpll->forced_ref) {
|
||||
+ ref = zl3073x_chan_ref_get(&chan);
|
||||
+ } else if (ref == zl3073x_chan_ref_get(&chan)) {
|
||||
/* No register update - same mode and same ref */
|
||||
return 0;
|
||||
}
|
||||
@@ -351,21 +355,10 @@ zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
- /* Build mode_refsel value */
|
||||
- mode_refsel = FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, mode) |
|
||||
- FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
||||
-
|
||||
- /* Update dpll_mode_refsel register */
|
||||
- rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
|
||||
- mode_refsel);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Store new mode and forced reference */
|
||||
- zldpll->refsel_mode = mode;
|
||||
- zldpll->forced_ref = ref;
|
||||
+ zl3073x_chan_mode_set(&chan, mode);
|
||||
+ zl3073x_chan_ref_set(&chan, ref);
|
||||
|
||||
- return rc;
|
||||
+ return zl3073x_chan_state_set(zldev, zldpll->id, &chan);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -624,9 +617,11 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
u8 ref, ref_conn;
|
||||
int rc;
|
||||
|
||||
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
|
||||
/* Get currently connected reference */
|
||||
@@ -643,7 +638,7 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
|
||||
* selectable and its monitor does not report any error then report
|
||||
* pin as selectable.
|
||||
*/
|
||||
- if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
||||
+ if (zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
||||
zl3073x_dev_ref_is_status_ok(zldev, ref) && pin->selectable) {
|
||||
*state = DPLL_PIN_STATE_SELECTABLE;
|
||||
return 0;
|
||||
@@ -678,10 +673,13 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *dpll_pin,
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
u8 new_ref;
|
||||
int rc;
|
||||
|
||||
- switch (zldpll->refsel_mode) {
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+
|
||||
+ switch (zl3073x_chan_mode_get(chan)) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
||||
@@ -1092,10 +1090,13 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
u8 mon_status, state;
|
||||
int rc;
|
||||
|
||||
- switch (zldpll->refsel_mode) {
|
||||
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+
|
||||
+ switch (zl3073x_chan_mode_get(chan)) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_NCO:
|
||||
/* In FREERUN and NCO modes the DPLL is always unlocked */
|
||||
@@ -1140,13 +1141,16 @@ zl3073x_dpll_supported_modes_get(const struct dpll_device *dpll,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
+
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
|
||||
/* We support switching between automatic and manual mode, except in
|
||||
* a case where the DPLL channel is configured to run in NCO mode.
|
||||
* In this case, report only the manual mode to which the NCO is mapped
|
||||
* as the only supported one.
|
||||
*/
|
||||
- if (zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
||||
+ if (zl3073x_chan_mode_get(chan) != ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
||||
__set_bit(DPLL_MODE_AUTOMATIC, modes);
|
||||
|
||||
__set_bit(DPLL_MODE_MANUAL, modes);
|
||||
@@ -1159,8 +1163,11 @@ zl3073x_dpll_mode_get(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_mode *mode, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
|
||||
- switch (zldpll->refsel_mode) {
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+
|
||||
+ switch (zl3073x_chan_mode_get(chan)) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_NCO:
|
||||
@@ -1239,7 +1246,8 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_mode mode, struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
- u8 hw_mode, mode_refsel, ref;
|
||||
+ struct zl3073x_chan chan;
|
||||
+ u8 hw_mode, ref;
|
||||
int rc;
|
||||
|
||||
rc = zl3073x_dpll_selected_ref_get(zldpll, &ref);
|
||||
@@ -1287,26 +1295,18 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
|
||||
hw_mode = ZL_DPLL_MODE_REFSEL_MODE_AUTO;
|
||||
}
|
||||
|
||||
- /* Build mode_refsel value */
|
||||
- mode_refsel = FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, hw_mode);
|
||||
-
|
||||
+ chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ zl3073x_chan_mode_set(&chan, hw_mode);
|
||||
if (ZL3073X_DPLL_REF_IS_VALID(ref))
|
||||
- mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
||||
+ zl3073x_chan_ref_set(&chan, ref);
|
||||
|
||||
- /* Update dpll_mode_refsel register */
|
||||
- rc = zl3073x_write_u8(zldpll->dev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
|
||||
- mode_refsel);
|
||||
+ rc = zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan);
|
||||
if (rc) {
|
||||
NL_SET_ERR_MSG_MOD(extack,
|
||||
"failed to set reference selection mode");
|
||||
return rc;
|
||||
}
|
||||
|
||||
- zldpll->refsel_mode = hw_mode;
|
||||
-
|
||||
- if (ZL3073X_DPLL_REF_IS_VALID(ref))
|
||||
- zldpll->forced_ref = ref;
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1559,15 +1559,18 @@ zl3073x_dpll_pin_is_registrable(struct zl3073x_dpll *zldpll,
|
||||
enum dpll_pin_direction dir, u8 index)
|
||||
{
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
bool is_diff, is_enabled;
|
||||
const char *name;
|
||||
|
||||
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+
|
||||
if (dir == DPLL_PIN_DIRECTION_INPUT) {
|
||||
u8 ref_id = zl3073x_input_pin_ref_get(index);
|
||||
const struct zl3073x_ref *ref;
|
||||
|
||||
/* Skip the pin if the DPLL is running in NCO mode */
|
||||
- if (zldpll->refsel_mode == ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
||||
+ if (zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_NCO)
|
||||
return false;
|
||||
|
||||
name = "REF";
|
||||
@@ -1675,21 +1678,8 @@ static int
|
||||
zl3073x_dpll_device_register(struct zl3073x_dpll *zldpll)
|
||||
{
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- u8 dpll_mode_refsel;
|
||||
int rc;
|
||||
|
||||
- /* Read DPLL mode and forcibly selected reference */
|
||||
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(zldpll->id),
|
||||
- &dpll_mode_refsel);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Extract mode and selected input reference */
|
||||
- zldpll->refsel_mode = FIELD_GET(ZL_DPLL_MODE_REFSEL_MODE,
|
||||
- dpll_mode_refsel);
|
||||
- zldpll->forced_ref = FIELD_GET(ZL_DPLL_MODE_REFSEL_REF,
|
||||
- dpll_mode_refsel);
|
||||
-
|
||||
zldpll->ops = zl3073x_dpll_device_ops;
|
||||
if (zldev->info->flags & ZL3073X_FLAG_DIE_TEMP)
|
||||
zldpll->ops.temp_get = zl3073x_dpll_temp_get;
|
||||
@@ -1844,8 +1834,10 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
enum dpll_lock_status lock_status;
|
||||
struct device *dev = zldev->dev;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
struct zl3073x_dpll_pin *pin;
|
||||
int rc;
|
||||
+ u8 mode;
|
||||
|
||||
zldpll->check_count++;
|
||||
|
||||
@@ -1867,8 +1859,10 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
||||
/* Input pin monitoring does make sense only in automatic
|
||||
* or forced reference modes.
|
||||
*/
|
||||
- if (zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
||||
- zldpll->refsel_mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK)
|
||||
+ chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+ mode = zl3073x_chan_mode_get(chan);
|
||||
+ if (mode != ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
||||
+ mode != ZL_DPLL_MODE_REFSEL_MODE_REFLOCK)
|
||||
return;
|
||||
|
||||
/* Update phase offset latch registers for this DPLL if the phase
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h
|
||||
index 278a24f357c9..115ee4f67e7a 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.h
|
||||
+++ b/drivers/dpll/zl3073x/dpll.h
|
||||
@@ -13,8 +13,6 @@
|
||||
* @list: this DPLL list entry
|
||||
* @dev: pointer to multi-function parent device
|
||||
* @id: DPLL index
|
||||
- * @refsel_mode: reference selection mode
|
||||
- * @forced_ref: selected reference in forced reference lock mode
|
||||
* @check_count: periodic check counter
|
||||
* @phase_monitor: is phase offset monitor enabled
|
||||
* @ops: DPLL device operations for this instance
|
||||
@@ -28,8 +26,6 @@ struct zl3073x_dpll {
|
||||
struct list_head list;
|
||||
struct zl3073x_dev *dev;
|
||||
u8 id;
|
||||
- u8 refsel_mode;
|
||||
- u8 forced_ref;
|
||||
u8 check_count;
|
||||
bool phase_monitor;
|
||||
struct dpll_device_ops ops;
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,288 @@
|
||||
From 1325513643b0d024227fc1ef13470e2a84989622 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:51:26 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: add DPLL channel status fields to zl3073x_chan
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit 41bab554d7e9840e17e4c8c7e93647161c6596bf
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Sun Mar 15 18:42:22 2026 +0100
|
||||
|
||||
dpll: zl3073x: add DPLL channel status fields to zl3073x_chan
|
||||
|
||||
Add mon_status and refsel_status fields to struct zl3073x_chan in a
|
||||
stat group to cache the 'dpll_mon_status' and 'dpll_refsel_status'
|
||||
registers.
|
||||
|
||||
Add zl3073x_chan_lock_state_get(), zl3073x_chan_is_ho_ready(),
|
||||
zl3073x_chan_refsel_state_get() and zl3073x_chan_refsel_ref_get()
|
||||
inline helpers for reading cached state, and zl3073x_chan_state_update()
|
||||
for refreshing both registers from hardware. Call it from
|
||||
zl3073x_chan_state_fetch() as well so that channel status is
|
||||
initialized at device startup.
|
||||
|
||||
Call zl3073x_dev_chan_states_update() from the periodic work to
|
||||
keep the cached state up to date and convert
|
||||
zl3073x_dpll_lock_status_get() and zl3073x_dpll_selected_ref_get()
|
||||
to use the cached state via the new helpers instead of direct register
|
||||
reads.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260315174224.399074-5-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 41bab554d7e9840e17e4c8c7e93647161c6596bf)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c
|
||||
index 8019b8ce7351..71fb60a9859b 100644
|
||||
--- a/drivers/dpll/zl3073x/chan.c
|
||||
+++ b/drivers/dpll/zl3073x/chan.c
|
||||
@@ -7,6 +7,27 @@
|
||||
#include "chan.h"
|
||||
#include "core.h"
|
||||
|
||||
+/**
|
||||
+ * zl3073x_chan_state_update - update DPLL channel status from HW
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ * @index: DPLL channel index
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on error
|
||||
+ */
|
||||
+int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
|
||||
+{
|
||||
+ struct zl3073x_chan *chan = &zldev->chan[index];
|
||||
+ int rc;
|
||||
+
|
||||
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index),
|
||||
+ &chan->mon_status);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),
|
||||
+ &chan->refsel_status);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_chan_state_fetch - fetch DPLL channel state from hardware
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
@@ -30,6 +51,17 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
dev_dbg(zldev->dev, "DPLL%u mode: %u, ref: %u\n", index,
|
||||
zl3073x_chan_mode_get(chan), zl3073x_chan_ref_get(chan));
|
||||
|
||||
+ rc = zl3073x_chan_state_update(zldev, index);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ dev_dbg(zldev->dev,
|
||||
+ "DPLL%u lock_state: %u, ho: %u, sel_state: %u, sel_ref: %u\n",
|
||||
+ index, zl3073x_chan_lock_state_get(chan),
|
||||
+ zl3073x_chan_is_ho_ready(chan) ? 1 : 0,
|
||||
+ zl3073x_chan_refsel_state_get(chan),
|
||||
+ zl3073x_chan_refsel_ref_get(chan));
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
|
||||
index 3e6ffaef0c74..f73a07610855 100644
|
||||
--- a/drivers/dpll/zl3073x/chan.h
|
||||
+++ b/drivers/dpll/zl3073x/chan.h
|
||||
@@ -14,11 +14,17 @@ struct zl3073x_dev;
|
||||
/**
|
||||
* struct zl3073x_chan - DPLL channel state
|
||||
* @mode_refsel: mode and reference selection register value
|
||||
+ * @mon_status: monitor status register value
|
||||
+ * @refsel_status: reference selection status register value
|
||||
*/
|
||||
struct zl3073x_chan {
|
||||
struct_group(cfg,
|
||||
u8 mode_refsel;
|
||||
);
|
||||
+ struct_group(stat,
|
||||
+ u8 mon_status;
|
||||
+ u8 refsel_status;
|
||||
+ );
|
||||
};
|
||||
|
||||
int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index);
|
||||
@@ -27,6 +33,8 @@ const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
||||
int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_chan *chan);
|
||||
|
||||
+int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
|
||||
+
|
||||
/**
|
||||
* zl3073x_chan_mode_get - get DPLL channel operating mode
|
||||
* @chan: pointer to channel state
|
||||
@@ -71,4 +79,48 @@ static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
|
||||
chan->mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_chan_lock_state_get - get DPLL channel lock state
|
||||
+ * @chan: pointer to channel state
|
||||
+ *
|
||||
+ * Return: lock state of the given DPLL channel
|
||||
+ */
|
||||
+static inline u8 zl3073x_chan_lock_state_get(const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_DPLL_MON_STATUS_STATE, chan->mon_status);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_is_ho_ready - check if holdover is ready
|
||||
+ * @chan: pointer to channel state
|
||||
+ *
|
||||
+ * Return: true if holdover is ready, false otherwise
|
||||
+ */
|
||||
+static inline bool zl3073x_chan_is_ho_ready(const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ return !!FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, chan->mon_status);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_refsel_state_get - get reference selection state
|
||||
+ * @chan: pointer to channel state
|
||||
+ *
|
||||
+ * Return: reference selection state of the given DPLL channel
|
||||
+ */
|
||||
+static inline u8 zl3073x_chan_refsel_state_get(const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, chan->refsel_status);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_refsel_ref_get - get currently selected reference in auto mode
|
||||
+ * @chan: pointer to channel state
|
||||
+ *
|
||||
+ * Return: reference selected by the DPLL in automatic mode
|
||||
+ */
|
||||
+static inline u8 zl3073x_chan_refsel_ref_get(const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, chan->refsel_status);
|
||||
+}
|
||||
+
|
||||
#endif /* _ZL3073X_CHAN_H */
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index b03e59fa0834..6363002d48d4 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -566,6 +566,20 @@ zl3073x_dev_ref_states_update(struct zl3073x_dev *zldev)
|
||||
}
|
||||
}
|
||||
|
||||
+static void
|
||||
+zl3073x_dev_chan_states_update(struct zl3073x_dev *zldev)
|
||||
+{
|
||||
+ int i, rc;
|
||||
+
|
||||
+ for (i = 0; i < zldev->info->num_channels; i++) {
|
||||
+ rc = zl3073x_chan_state_update(zldev, i);
|
||||
+ if (rc)
|
||||
+ dev_warn(zldev->dev,
|
||||
+ "Failed to get DPLL%u state: %pe\n", i,
|
||||
+ ERR_PTR(rc));
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_ref_phase_offsets_update - update reference phase offsets
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
@@ -691,6 +705,9 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
/* Update input references' states */
|
||||
zl3073x_dev_ref_states_update(zldev);
|
||||
|
||||
+ /* Update DPLL channels' states */
|
||||
+ zl3073x_dev_chan_states_update(zldev);
|
||||
+
|
||||
/* Update DPLL-to-connected-ref phase offsets registers */
|
||||
rc = zl3073x_ref_phase_offsets_update(zldev, -1);
|
||||
if (rc)
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 767af6dd724d..33c4f05ec9d3 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -258,28 +258,16 @@ zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
||||
static int
|
||||
zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
||||
{
|
||||
- struct zl3073x_dev *zldev = zldpll->dev;
|
||||
const struct zl3073x_chan *chan;
|
||||
- u8 state, value;
|
||||
- int rc;
|
||||
|
||||
- chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
|
||||
switch (zl3073x_chan_mode_get(chan)) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
|
||||
- /* For automatic mode read refsel_status register */
|
||||
- rc = zl3073x_read_u8(zldev,
|
||||
- ZL_REG_DPLL_REFSEL_STATUS(zldpll->id),
|
||||
- &value);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Extract reference state */
|
||||
- state = FIELD_GET(ZL_DPLL_REFSEL_STATUS_STATE, value);
|
||||
-
|
||||
/* Return the reference only if the DPLL is locked to it */
|
||||
- if (state == ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
|
||||
- *ref = FIELD_GET(ZL_DPLL_REFSEL_STATUS_REFSEL, value);
|
||||
+ if (zl3073x_chan_refsel_state_get(chan) ==
|
||||
+ ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
|
||||
+ *ref = zl3073x_chan_refsel_ref_get(chan);
|
||||
else
|
||||
*ref = ZL3073X_DPLL_REF_NONE;
|
||||
break;
|
||||
@@ -1089,12 +1077,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
- struct zl3073x_dev *zldev = zldpll->dev;
|
||||
const struct zl3073x_chan *chan;
|
||||
- u8 mon_status, state;
|
||||
- int rc;
|
||||
|
||||
- chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
|
||||
switch (zl3073x_chan_mode_get(chan)) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
||||
@@ -1107,16 +1092,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
||||
break;
|
||||
}
|
||||
|
||||
- /* Read DPLL monitor status */
|
||||
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(zldpll->id),
|
||||
- &mon_status);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
- state = FIELD_GET(ZL_DPLL_MON_STATUS_STATE, mon_status);
|
||||
-
|
||||
- switch (state) {
|
||||
+ switch (zl3073x_chan_lock_state_get(chan)) {
|
||||
case ZL_DPLL_MON_STATUS_STATE_LOCK:
|
||||
- if (FIELD_GET(ZL_DPLL_MON_STATUS_HO_READY, mon_status))
|
||||
+ if (zl3073x_chan_is_ho_ready(chan))
|
||||
*status = DPLL_LOCK_STATUS_LOCKED_HO_ACQ;
|
||||
else
|
||||
*status = DPLL_LOCK_STATUS_LOCKED;
|
||||
@@ -1126,8 +1104,9 @@ zl3073x_dpll_lock_status_get(const struct dpll_device *dpll, void *dpll_priv,
|
||||
*status = DPLL_LOCK_STATUS_HOLDOVER;
|
||||
break;
|
||||
default:
|
||||
- dev_warn(zldev->dev, "Unknown DPLL monitor status: 0x%02x\n",
|
||||
- mon_status);
|
||||
+ dev_warn(zldpll->dev->dev,
|
||||
+ "Unknown DPLL monitor status: 0x%02x\n",
|
||||
+ chan->mon_status);
|
||||
*status = DPLL_LOCK_STATUS_UNLOCKED;
|
||||
break;
|
||||
}
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
713
1256-dpll-zl3073x-add-reference-priority-to-zl3073x-chan.patch
Normal file
713
1256-dpll-zl3073x-add-reference-priority-to-zl3073x-chan.patch
Normal file
@ -0,0 +1,713 @@
|
||||
From 4b33e0ad68ce2edfb7cdce8dc91d166eebf7f54b Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:51:32 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: add reference priority to zl3073x_chan
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit f6b075bc3ad545d1c91e181c3f8ea6193d01602f
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Sun Mar 15 18:42:23 2026 +0100
|
||||
|
||||
dpll: zl3073x: add reference priority to zl3073x_chan
|
||||
|
||||
Cache the ZL_REG_DPLL_REF_PRIO registers in the zl3073x_chan cfg group.
|
||||
These mailbox-based registers store per-reference priority values
|
||||
(4 bits each, P/N packed) used for automatic reference selection.
|
||||
|
||||
Add ref_prio[] array to struct zl3073x_chan and provide inline helpers
|
||||
zl3073x_chan_ref_prio_get(), zl3073x_chan_ref_prio_set(), and
|
||||
zl3073x_chan_ref_is_selectable() for nibble-level access and priority
|
||||
queries. Extend state_fetch and state_set with DPLL mailbox operations
|
||||
to read and write the priority registers.
|
||||
|
||||
Replace the ad-hoc zl3073x_dpll_ref_prio_get/set functions in dpll.c
|
||||
with the cached state pattern, removing direct mailbox access from
|
||||
the DPLL layer. This also simplifies pin registration since reading
|
||||
priority from cached state cannot fail.
|
||||
|
||||
Remove the pin->selectable flag from struct zl3073x_dpll_pin and
|
||||
derive the selectable state from the cached ref priority via
|
||||
zl3073x_chan_ref_is_selectable(), eliminating a redundant cache.
|
||||
|
||||
Inline zl3073x_dpll_selected_ref_set() into
|
||||
zl3073x_dpll_input_pin_state_on_dpll_set(), unifying all manual and
|
||||
automatic mode paths to commit changes through a single
|
||||
zl3073x_chan_state_set() call at the end of the function.
|
||||
|
||||
Move hardware limit constants from core.h to regs.h so that chan.h
|
||||
can reference ZL3073X_NUM_REFS for the ref_prio array size.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260315174224.399074-6-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit f6b075bc3ad545d1c91e181c3f8ea6193d01602f)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c
|
||||
index 71fb60a9859b..2f48ca239149 100644
|
||||
--- a/drivers/dpll/zl3073x/chan.c
|
||||
+++ b/drivers/dpll/zl3073x/chan.c
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
+#include <linux/cleanup.h>
|
||||
#include <linux/dev_printk.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
@@ -33,15 +34,15 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
* @index: DPLL channel index to fetch state for
|
||||
*
|
||||
- * Reads the mode_refsel register for the given DPLL channel and stores
|
||||
- * the raw value for later use.
|
||||
+ * Reads the mode_refsel register and reference priority registers for
|
||||
+ * the given DPLL channel and stores the raw values for later use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_chan *chan = &zldev->chan[index];
|
||||
- int rc;
|
||||
+ int rc, i;
|
||||
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
||||
&chan->mode_refsel);
|
||||
@@ -62,6 +63,22 @@ int zl3073x_chan_state_fetch(struct zl3073x_dev *zldev, u8 index)
|
||||
zl3073x_chan_refsel_state_get(chan),
|
||||
zl3073x_chan_refsel_ref_get(chan));
|
||||
|
||||
+ guard(mutex)(&zldev->multiop_lock);
|
||||
+
|
||||
+ /* Read DPLL configuration from mailbox */
|
||||
+ rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
||||
+ ZL_REG_DPLL_MB_MASK, BIT(index));
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ /* Read reference priority registers */
|
||||
+ for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
|
||||
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(i),
|
||||
+ &chan->ref_prio[i]);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -85,7 +102,9 @@ const struct zl3073x_chan *zl3073x_chan_state_get(struct zl3073x_dev *zldev,
|
||||
* @chan: desired channel state
|
||||
*
|
||||
* Skips the HW write if the configuration is unchanged, and otherwise
|
||||
- * writes the mode_refsel register to hardware.
|
||||
+ * writes only the changed registers to hardware. The mode_refsel register
|
||||
+ * is written directly, while the reference priority registers are written
|
||||
+ * via the DPLL mailbox interface.
|
||||
*
|
||||
* Return: 0 on success, <0 on HW error
|
||||
*/
|
||||
@@ -93,14 +112,49 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_chan *chan)
|
||||
{
|
||||
struct zl3073x_chan *dchan = &zldev->chan[index];
|
||||
- int rc;
|
||||
+ int rc, i;
|
||||
|
||||
/* Skip HW write if configuration hasn't changed */
|
||||
if (!memcmp(&dchan->cfg, &chan->cfg, sizeof(chan->cfg)))
|
||||
return 0;
|
||||
|
||||
- rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
||||
- chan->mode_refsel);
|
||||
+ /* Direct register write for mode_refsel */
|
||||
+ if (dchan->mode_refsel != chan->mode_refsel) {
|
||||
+ rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MODE_REFSEL(index),
|
||||
+ chan->mode_refsel);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+ dchan->mode_refsel = chan->mode_refsel;
|
||||
+ }
|
||||
+
|
||||
+ /* Mailbox write for ref_prio if changed */
|
||||
+ if (!memcmp(dchan->ref_prio, chan->ref_prio, sizeof(chan->ref_prio))) {
|
||||
+ dchan->cfg = chan->cfg;
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ guard(mutex)(&zldev->multiop_lock);
|
||||
+
|
||||
+ /* Read DPLL configuration into mailbox */
|
||||
+ rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
||||
+ ZL_REG_DPLL_MB_MASK, BIT(index));
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ /* Update changed ref_prio registers */
|
||||
+ for (i = 0; i < ARRAY_SIZE(chan->ref_prio); i++) {
|
||||
+ if (dchan->ref_prio[i] != chan->ref_prio[i]) {
|
||||
+ rc = zl3073x_write_u8(zldev,
|
||||
+ ZL_REG_DPLL_REF_PRIO(i),
|
||||
+ chan->ref_prio[i]);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Commit DPLL configuration */
|
||||
+ rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
|
||||
+ ZL_REG_DPLL_MB_MASK, BIT(index));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
|
||||
index f73a07610855..e0f02d343208 100644
|
||||
--- a/drivers/dpll/zl3073x/chan.h
|
||||
+++ b/drivers/dpll/zl3073x/chan.h
|
||||
@@ -14,12 +14,14 @@ struct zl3073x_dev;
|
||||
/**
|
||||
* struct zl3073x_chan - DPLL channel state
|
||||
* @mode_refsel: mode and reference selection register value
|
||||
+ * @ref_prio: reference priority registers (4 bits per ref, P/N packed)
|
||||
* @mon_status: monitor status register value
|
||||
* @refsel_status: reference selection status register value
|
||||
*/
|
||||
struct zl3073x_chan {
|
||||
struct_group(cfg,
|
||||
u8 mode_refsel;
|
||||
+ u8 ref_prio[ZL3073X_NUM_REFS / 2];
|
||||
);
|
||||
struct_group(stat,
|
||||
u8 mon_status;
|
||||
@@ -79,6 +81,57 @@ static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
|
||||
chan->mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_chan_ref_prio_get - get reference priority
|
||||
+ * @chan: pointer to channel state
|
||||
+ * @ref: input reference index
|
||||
+ *
|
||||
+ * Return: priority of the given reference <0, 15>
|
||||
+ */
|
||||
+static inline u8
|
||||
+zl3073x_chan_ref_prio_get(const struct zl3073x_chan *chan, u8 ref)
|
||||
+{
|
||||
+ u8 val = chan->ref_prio[ref / 2];
|
||||
+
|
||||
+ if (!(ref & 1))
|
||||
+ return FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, val);
|
||||
+ else
|
||||
+ return FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, val);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_ref_prio_set - set reference priority
|
||||
+ * @chan: pointer to channel state
|
||||
+ * @ref: input reference index
|
||||
+ * @prio: priority to set
|
||||
+ */
|
||||
+static inline void
|
||||
+zl3073x_chan_ref_prio_set(struct zl3073x_chan *chan, u8 ref, u8 prio)
|
||||
+{
|
||||
+ u8 *val = &chan->ref_prio[ref / 2];
|
||||
+
|
||||
+ if (!(ref & 1)) {
|
||||
+ *val &= ~ZL_DPLL_REF_PRIO_REF_P;
|
||||
+ *val |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_P, prio);
|
||||
+ } else {
|
||||
+ *val &= ~ZL_DPLL_REF_PRIO_REF_N;
|
||||
+ *val |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_N, prio);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_chan_ref_is_selectable - check if reference is selectable
|
||||
+ * @chan: pointer to channel state
|
||||
+ * @ref: input reference index
|
||||
+ *
|
||||
+ * Return: true if the reference priority is not NONE, false otherwise
|
||||
+ */
|
||||
+static inline bool
|
||||
+zl3073x_chan_ref_is_selectable(const struct zl3073x_chan *chan, u8 ref)
|
||||
+{
|
||||
+ return zl3073x_chan_ref_prio_get(chan, ref) != ZL_DPLL_REF_PRIO_NONE;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_chan_lock_state_get - get DPLL channel lock state
|
||||
* @chan: pointer to channel state
|
||||
diff --git a/drivers/dpll/zl3073x/core.h b/drivers/dpll/zl3073x/core.h
|
||||
index 2cfb9dd74aa5..99440620407d 100644
|
||||
--- a/drivers/dpll/zl3073x/core.h
|
||||
+++ b/drivers/dpll/zl3073x/core.h
|
||||
@@ -19,17 +19,6 @@ struct device;
|
||||
struct regmap;
|
||||
struct zl3073x_dpll;
|
||||
|
||||
-/*
|
||||
- * Hardware limits for ZL3073x chip family
|
||||
- */
|
||||
-#define ZL3073X_MAX_CHANNELS 5
|
||||
-#define ZL3073X_NUM_REFS 10
|
||||
-#define ZL3073X_NUM_OUTS 10
|
||||
-#define ZL3073X_NUM_SYNTHS 5
|
||||
-#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
|
||||
-#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
|
||||
-#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
|
||||
- ZL3073X_NUM_OUTPUT_PINS)
|
||||
|
||||
enum zl3073x_flags {
|
||||
ZL3073X_FLAG_REF_PHASE_COMP_32_BIT,
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 33c4f05ec9d3..9ea66a7e26b8 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -34,7 +34,6 @@
|
||||
* @dir: pin direction
|
||||
* @id: pin id
|
||||
* @prio: pin priority <0, 14>
|
||||
- * @selectable: pin is selectable in automatic mode
|
||||
* @esync_control: embedded sync is controllable
|
||||
* @phase_gran: phase adjustment granularity
|
||||
* @pin_state: last saved pin state
|
||||
@@ -50,7 +49,6 @@ struct zl3073x_dpll_pin {
|
||||
enum dpll_pin_direction dir;
|
||||
u8 id;
|
||||
u8 prio;
|
||||
- bool selectable;
|
||||
bool esync_control;
|
||||
s32 phase_gran;
|
||||
enum dpll_pin_state pin_state;
|
||||
@@ -284,71 +282,6 @@ zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * zl3073x_dpll_selected_ref_set - select reference in manual mode
|
||||
- * @zldpll: pointer to zl3073x_dpll
|
||||
- * @ref: input reference to be selected
|
||||
- *
|
||||
- * Selects the given reference for the DPLL channel it should be
|
||||
- * locked to.
|
||||
- *
|
||||
- * Return: 0 on success, <0 on error
|
||||
- */
|
||||
-static int
|
||||
-zl3073x_dpll_selected_ref_set(struct zl3073x_dpll *zldpll, u8 ref)
|
||||
-{
|
||||
- struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- struct zl3073x_chan chan;
|
||||
- u8 mode;
|
||||
-
|
||||
- chan = *zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
- mode = zl3073x_chan_mode_get(&chan);
|
||||
-
|
||||
- switch (mode) {
|
||||
- case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
||||
- /* Manual mode with ref selected */
|
||||
- if (ref == ZL3073X_DPLL_REF_NONE) {
|
||||
- switch (zldpll->lock_status) {
|
||||
- case DPLL_LOCK_STATUS_LOCKED_HO_ACQ:
|
||||
- case DPLL_LOCK_STATUS_HOLDOVER:
|
||||
- /* Switch to forced holdover */
|
||||
- mode = ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER;
|
||||
- break;
|
||||
- default:
|
||||
- /* Switch to freerun */
|
||||
- mode = ZL_DPLL_MODE_REFSEL_MODE_FREERUN;
|
||||
- break;
|
||||
- }
|
||||
- /* Keep selected reference */
|
||||
- ref = zl3073x_chan_ref_get(&chan);
|
||||
- } else if (ref == zl3073x_chan_ref_get(&chan)) {
|
||||
- /* No register update - same mode and same ref */
|
||||
- return 0;
|
||||
- }
|
||||
- break;
|
||||
- case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
||||
- case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
||||
- /* Manual mode without no ref */
|
||||
- if (ref == ZL3073X_DPLL_REF_NONE)
|
||||
- /* No register update - keep current mode */
|
||||
- return 0;
|
||||
-
|
||||
- /* Switch to reflock mode and update ref selection */
|
||||
- mode = ZL_DPLL_MODE_REFSEL_MODE_REFLOCK;
|
||||
- break;
|
||||
- default:
|
||||
- /* For other modes like automatic or NCO ref cannot be selected
|
||||
- * manually
|
||||
- */
|
||||
- return -EOPNOTSUPP;
|
||||
- }
|
||||
-
|
||||
- zl3073x_chan_mode_set(&chan, mode);
|
||||
- zl3073x_chan_ref_set(&chan, ref);
|
||||
-
|
||||
- return zl3073x_chan_state_set(zldev, zldpll->id, &chan);
|
||||
-}
|
||||
-
|
||||
/**
|
||||
* zl3073x_dpll_connected_ref_get - get currently connected reference
|
||||
* @zldpll: pointer to zl3073x_dpll
|
||||
@@ -497,98 +430,6 @@ zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
|
||||
return zl3073x_ref_state_set(zldev, ref_id, &ref);
|
||||
}
|
||||
|
||||
-/**
|
||||
- * zl3073x_dpll_ref_prio_get - get priority for given input pin
|
||||
- * @pin: pointer to pin
|
||||
- * @prio: place to store priority
|
||||
- *
|
||||
- * Reads current priority for the given input pin and stores the value
|
||||
- * to @prio.
|
||||
- *
|
||||
- * Return: 0 on success, <0 on error
|
||||
- */
|
||||
-static int
|
||||
-zl3073x_dpll_ref_prio_get(struct zl3073x_dpll_pin *pin, u8 *prio)
|
||||
-{
|
||||
- struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
- struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- u8 ref, ref_prio;
|
||||
- int rc;
|
||||
-
|
||||
- guard(mutex)(&zldev->multiop_lock);
|
||||
-
|
||||
- /* Read DPLL configuration */
|
||||
- rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
||||
- ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Read reference priority - one value for P&N pins (4 bits/pin) */
|
||||
- ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2),
|
||||
- &ref_prio);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Select nibble according pin type */
|
||||
- if (zl3073x_dpll_is_p_pin(pin))
|
||||
- *prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_P, ref_prio);
|
||||
- else
|
||||
- *prio = FIELD_GET(ZL_DPLL_REF_PRIO_REF_N, ref_prio);
|
||||
-
|
||||
- return rc;
|
||||
-}
|
||||
-
|
||||
-/**
|
||||
- * zl3073x_dpll_ref_prio_set - set priority for given input pin
|
||||
- * @pin: pointer to pin
|
||||
- * @prio: place to store priority
|
||||
- *
|
||||
- * Sets priority for the given input pin.
|
||||
- *
|
||||
- * Return: 0 on success, <0 on error
|
||||
- */
|
||||
-static int
|
||||
-zl3073x_dpll_ref_prio_set(struct zl3073x_dpll_pin *pin, u8 prio)
|
||||
-{
|
||||
- struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
- struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- u8 ref, ref_prio;
|
||||
- int rc;
|
||||
-
|
||||
- guard(mutex)(&zldev->multiop_lock);
|
||||
-
|
||||
- /* Read DPLL configuration into mailbox */
|
||||
- rc = zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_RD,
|
||||
- ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Read reference priority - one value shared between P&N pins */
|
||||
- ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
- rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), &ref_prio);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Update nibble according pin type */
|
||||
- if (zl3073x_dpll_is_p_pin(pin)) {
|
||||
- ref_prio &= ~ZL_DPLL_REF_PRIO_REF_P;
|
||||
- ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_P, prio);
|
||||
- } else {
|
||||
- ref_prio &= ~ZL_DPLL_REF_PRIO_REF_N;
|
||||
- ref_prio |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_N, prio);
|
||||
- }
|
||||
-
|
||||
- /* Update reference priority */
|
||||
- rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_REF_PRIO(ref / 2), ref_prio);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Commit configuration */
|
||||
- return zl3073x_mb_op(zldev, ZL_REG_DPLL_MB_SEM, ZL_DPLL_MB_SEM_WR,
|
||||
- ZL_REG_DPLL_MB_MASK, BIT(zldpll->id));
|
||||
-}
|
||||
-
|
||||
/**
|
||||
* zl3073x_dpll_ref_state_get - get status for given input pin
|
||||
* @pin: pointer to pin
|
||||
@@ -627,7 +468,8 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
|
||||
* pin as selectable.
|
||||
*/
|
||||
if (zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
||||
- zl3073x_dev_ref_is_status_ok(zldev, ref) && pin->selectable) {
|
||||
+ zl3073x_dev_ref_is_status_ok(zldev, ref) &&
|
||||
+ zl3073x_chan_ref_is_selectable(chan, ref)) {
|
||||
*state = DPLL_PIN_STATE_SELECTABLE;
|
||||
return 0;
|
||||
}
|
||||
@@ -661,71 +503,81 @@ zl3073x_dpll_input_pin_state_on_dpll_set(const struct dpll_pin *dpll_pin,
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
- const struct zl3073x_chan *chan;
|
||||
- u8 new_ref;
|
||||
+ struct zl3073x_chan chan;
|
||||
+ u8 mode, ref;
|
||||
int rc;
|
||||
|
||||
- chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ mode = zl3073x_chan_mode_get(&chan);
|
||||
|
||||
- switch (zl3073x_chan_mode_get(chan)) {
|
||||
+ switch (mode) {
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
||||
- case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
||||
- case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
||||
if (state == DPLL_PIN_STATE_CONNECTED) {
|
||||
/* Choose the pin as new selected reference */
|
||||
- new_ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ zl3073x_chan_ref_set(&chan, ref);
|
||||
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
|
||||
- /* No reference */
|
||||
- new_ref = ZL3073X_DPLL_REF_NONE;
|
||||
+ /* Choose new mode based on lock status */
|
||||
+ switch (zldpll->lock_status) {
|
||||
+ case DPLL_LOCK_STATUS_LOCKED_HO_ACQ:
|
||||
+ case DPLL_LOCK_STATUS_HOLDOVER:
|
||||
+ mode = ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER;
|
||||
+ break;
|
||||
+ default:
|
||||
+ mode = ZL_DPLL_MODE_REFSEL_MODE_FREERUN;
|
||||
+ break;
|
||||
+ }
|
||||
+ zl3073x_chan_mode_set(&chan, mode);
|
||||
} else {
|
||||
- NL_SET_ERR_MSG_MOD(extack,
|
||||
- "Invalid pin state for manual mode");
|
||||
- return -EINVAL;
|
||||
+ goto invalid_state;
|
||||
+ }
|
||||
+ break;
|
||||
+ case ZL_DPLL_MODE_REFSEL_MODE_FREERUN:
|
||||
+ case ZL_DPLL_MODE_REFSEL_MODE_HOLDOVER:
|
||||
+ if (state == DPLL_PIN_STATE_CONNECTED) {
|
||||
+ /* Choose the pin as new selected reference */
|
||||
+ zl3073x_chan_ref_set(&chan, ref);
|
||||
+ /* Switch to reflock mode */
|
||||
+ zl3073x_chan_mode_set(&chan,
|
||||
+ ZL_DPLL_MODE_REFSEL_MODE_REFLOCK);
|
||||
+ } else if (state != DPLL_PIN_STATE_DISCONNECTED) {
|
||||
+ goto invalid_state;
|
||||
}
|
||||
-
|
||||
- rc = zl3073x_dpll_selected_ref_set(zldpll, new_ref);
|
||||
break;
|
||||
-
|
||||
case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
|
||||
if (state == DPLL_PIN_STATE_SELECTABLE) {
|
||||
- if (pin->selectable)
|
||||
+ if (zl3073x_chan_ref_is_selectable(&chan, ref))
|
||||
return 0; /* Pin is already selectable */
|
||||
|
||||
/* Restore pin priority in HW */
|
||||
- rc = zl3073x_dpll_ref_prio_set(pin, pin->prio);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Mark pin as selectable */
|
||||
- pin->selectable = true;
|
||||
+ zl3073x_chan_ref_prio_set(&chan, ref, pin->prio);
|
||||
} else if (state == DPLL_PIN_STATE_DISCONNECTED) {
|
||||
- if (!pin->selectable)
|
||||
+ if (!zl3073x_chan_ref_is_selectable(&chan, ref))
|
||||
return 0; /* Pin is already disconnected */
|
||||
|
||||
/* Set pin priority to none in HW */
|
||||
- rc = zl3073x_dpll_ref_prio_set(pin,
|
||||
- ZL_DPLL_REF_PRIO_NONE);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Mark pin as non-selectable */
|
||||
- pin->selectable = false;
|
||||
+ zl3073x_chan_ref_prio_set(&chan, ref,
|
||||
+ ZL_DPLL_REF_PRIO_NONE);
|
||||
} else {
|
||||
- NL_SET_ERR_MSG(extack,
|
||||
- "Invalid pin state for automatic mode");
|
||||
- return -EINVAL;
|
||||
+ goto invalid_state;
|
||||
}
|
||||
break;
|
||||
-
|
||||
default:
|
||||
/* In other modes we cannot change input reference */
|
||||
NL_SET_ERR_MSG(extack,
|
||||
"Pin state cannot be changed in current mode");
|
||||
- rc = -EOPNOTSUPP;
|
||||
- break;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
- return rc;
|
||||
+ /* Commit DPLL channel changes */
|
||||
+ rc = zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ return 0;
|
||||
+invalid_state:
|
||||
+ NL_SET_ERR_MSG_MOD(extack, "Invalid pin state for this device mode");
|
||||
+ return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -745,15 +597,21 @@ zl3073x_dpll_input_pin_prio_set(const struct dpll_pin *dpll_pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
u32 prio, struct netlink_ext_ack *extack)
|
||||
{
|
||||
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
+ struct zl3073x_chan chan;
|
||||
+ u8 ref;
|
||||
int rc;
|
||||
|
||||
if (prio > ZL_DPLL_REF_PRIO_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
/* If the pin is selectable then update HW registers */
|
||||
- if (pin->selectable) {
|
||||
- rc = zl3073x_dpll_ref_prio_set(pin, prio);
|
||||
+ chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ if (zl3073x_chan_ref_is_selectable(&chan, ref)) {
|
||||
+ zl3073x_chan_ref_prio_set(&chan, ref, prio);
|
||||
+ rc = zl3073x_chan_state_set(zldpll->dev, zldpll->id, &chan);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
@@ -1235,6 +1093,8 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
|
||||
return rc;
|
||||
}
|
||||
|
||||
+ chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+
|
||||
if (mode == DPLL_MODE_MANUAL) {
|
||||
/* We are switching from automatic to manual mode:
|
||||
* - if we have a valid reference selected during auto mode then
|
||||
@@ -1256,25 +1116,21 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
|
||||
* it is selectable after switch to automatic mode
|
||||
* - switch to automatic mode
|
||||
*/
|
||||
- struct zl3073x_dpll_pin *pin;
|
||||
-
|
||||
- pin = zl3073x_dpll_pin_get_by_ref(zldpll, ref);
|
||||
- if (pin && !pin->selectable) {
|
||||
- /* Restore pin priority in HW */
|
||||
- rc = zl3073x_dpll_ref_prio_set(pin, pin->prio);
|
||||
- if (rc) {
|
||||
- NL_SET_ERR_MSG_MOD(extack,
|
||||
- "failed to restore pin priority");
|
||||
- return rc;
|
||||
+ if (ZL3073X_DPLL_REF_IS_VALID(ref) &&
|
||||
+ !zl3073x_chan_ref_is_selectable(&chan, ref)) {
|
||||
+ struct zl3073x_dpll_pin *pin;
|
||||
+
|
||||
+ pin = zl3073x_dpll_pin_get_by_ref(zldpll, ref);
|
||||
+ if (pin) {
|
||||
+ /* Restore pin priority in HW */
|
||||
+ zl3073x_chan_ref_prio_set(&chan, ref,
|
||||
+ pin->prio);
|
||||
}
|
||||
-
|
||||
- pin->selectable = true;
|
||||
}
|
||||
|
||||
hw_mode = ZL_DPLL_MODE_REFSEL_MODE_AUTO;
|
||||
}
|
||||
|
||||
- chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
zl3073x_chan_mode_set(&chan, hw_mode);
|
||||
if (ZL3073X_DPLL_REF_IS_VALID(ref))
|
||||
zl3073x_chan_ref_set(&chan, ref);
|
||||
@@ -1426,18 +1282,16 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
|
||||
pin->phase_gran = props->dpll_props.phase_gran;
|
||||
|
||||
if (zl3073x_dpll_is_input_pin(pin)) {
|
||||
- rc = zl3073x_dpll_ref_prio_get(pin, &pin->prio);
|
||||
- if (rc)
|
||||
- goto err_prio_get;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
+ u8 ref;
|
||||
+
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ pin->prio = zl3073x_chan_ref_prio_get(chan, ref);
|
||||
|
||||
- if (pin->prio == ZL_DPLL_REF_PRIO_NONE) {
|
||||
- /* Clamp prio to max value & mark pin non-selectable */
|
||||
+ if (pin->prio == ZL_DPLL_REF_PRIO_NONE)
|
||||
+ /* Clamp prio to max value */
|
||||
pin->prio = ZL_DPLL_REF_PRIO_MAX;
|
||||
- pin->selectable = false;
|
||||
- } else {
|
||||
- /* Mark pin as selectable */
|
||||
- pin->selectable = true;
|
||||
- }
|
||||
}
|
||||
|
||||
/* Create or get existing DPLL pin */
|
||||
@@ -1466,7 +1320,6 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
|
||||
|
||||
err_register:
|
||||
dpll_pin_put(pin->dpll_pin, &pin->tracker);
|
||||
-err_prio_get:
|
||||
pin->dpll_pin = NULL;
|
||||
err_pin_get:
|
||||
zl3073x_pin_props_put(props);
|
||||
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
|
||||
index 19c598daa784..5ae50cb761a9 100644
|
||||
--- a/drivers/dpll/zl3073x/regs.h
|
||||
+++ b/drivers/dpll/zl3073x/regs.h
|
||||
@@ -6,6 +6,18 @@
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
+/*
|
||||
+ * Hardware limits for ZL3073x chip family
|
||||
+ */
|
||||
+#define ZL3073X_MAX_CHANNELS 5
|
||||
+#define ZL3073X_NUM_REFS 10
|
||||
+#define ZL3073X_NUM_OUTS 10
|
||||
+#define ZL3073X_NUM_SYNTHS 5
|
||||
+#define ZL3073X_NUM_INPUT_PINS ZL3073X_NUM_REFS
|
||||
+#define ZL3073X_NUM_OUTPUT_PINS (ZL3073X_NUM_OUTS * 2)
|
||||
+#define ZL3073X_NUM_PINS (ZL3073X_NUM_INPUT_PINS + \
|
||||
+ ZL3073X_NUM_OUTPUT_PINS)
|
||||
+
|
||||
/*
|
||||
* Register address structure:
|
||||
* ===========================
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,188 @@
|
||||
From 6d8e25eab47b0e42b2df4fee21761986644d4f7b Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:51:39 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: drop selected and simplify connected ref
|
||||
getter
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-171979
|
||||
|
||||
commit acee049a6af2a7b4baabd28f16fb53628ea6e7a5
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Sun Mar 15 18:42:24 2026 +0100
|
||||
|
||||
dpll: zl3073x: drop selected and simplify connected ref getter
|
||||
|
||||
The HW reports the currently selected reference in the
|
||||
dpll_refsel_status register regardless of the DPLL mode. Use this to
|
||||
delete zl3073x_dpll_selected_ref_get() and have callers read the
|
||||
register directly via the cached channel state.
|
||||
|
||||
Simplify zl3073x_dpll_connected_ref_get() to check refsel_state for
|
||||
LOCK directly and return the reference index, changing the return
|
||||
type from int to u8. The redundant ref_is_status_ok check is removed
|
||||
since the DPLL cannot be in LOCK state with a failed reference.
|
||||
|
||||
In zl3073x_dpll_mode_set(), replace the selected_ref_get() call with
|
||||
zl3073x_chan_refsel_ref_get() to read the currently selected
|
||||
reference directly from the cached channel state.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260315174224.399074-7-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit acee049a6af2a7b4baabd28f16fb53628ea6e7a5)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 9ea66a7e26b8..cf7a335baed5 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -243,72 +243,27 @@ zl3073x_dpll_input_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
||||
return zl3073x_ref_state_set(zldev, ref_id, &ref);
|
||||
}
|
||||
|
||||
-/**
|
||||
- * zl3073x_dpll_selected_ref_get - get currently selected reference
|
||||
- * @zldpll: pointer to zl3073x_dpll
|
||||
- * @ref: place to store selected reference
|
||||
- *
|
||||
- * Check for currently selected reference the DPLL should be locked to
|
||||
- * and stores its index to given @ref.
|
||||
- *
|
||||
- * Return: 0 on success, <0 on error
|
||||
- */
|
||||
-static int
|
||||
-zl3073x_dpll_selected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
||||
-{
|
||||
- const struct zl3073x_chan *chan;
|
||||
-
|
||||
- chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
-
|
||||
- switch (zl3073x_chan_mode_get(chan)) {
|
||||
- case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
|
||||
- /* Return the reference only if the DPLL is locked to it */
|
||||
- if (zl3073x_chan_refsel_state_get(chan) ==
|
||||
- ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
|
||||
- *ref = zl3073x_chan_refsel_ref_get(chan);
|
||||
- else
|
||||
- *ref = ZL3073X_DPLL_REF_NONE;
|
||||
- break;
|
||||
- case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
||||
- /* For manual mode return stored value */
|
||||
- *ref = zl3073x_chan_ref_get(chan);
|
||||
- break;
|
||||
- default:
|
||||
- /* For other modes like NCO, freerun... there is no input ref */
|
||||
- *ref = ZL3073X_DPLL_REF_NONE;
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
/**
|
||||
* zl3073x_dpll_connected_ref_get - get currently connected reference
|
||||
* @zldpll: pointer to zl3073x_dpll
|
||||
- * @ref: place to store selected reference
|
||||
*
|
||||
- * Looks for currently connected the DPLL is locked to and stores its index
|
||||
- * to given @ref.
|
||||
+ * Looks for currently connected reference the DPLL is locked to.
|
||||
*
|
||||
- * Return: 0 on success, <0 on error
|
||||
+ * Return: reference index if locked, ZL3073X_DPLL_REF_NONE otherwise
|
||||
*/
|
||||
-static int
|
||||
-zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll, u8 *ref)
|
||||
+static u8
|
||||
+zl3073x_dpll_connected_ref_get(struct zl3073x_dpll *zldpll)
|
||||
{
|
||||
- struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- int rc;
|
||||
-
|
||||
- /* Get currently selected input reference */
|
||||
- rc = zl3073x_dpll_selected_ref_get(zldpll, ref);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
+ const struct zl3073x_chan *chan = zl3073x_chan_state_get(zldpll->dev,
|
||||
+ zldpll->id);
|
||||
+ u8 state;
|
||||
|
||||
- /* If the monitor indicates an error nothing is connected */
|
||||
- if (ZL3073X_DPLL_REF_IS_VALID(*ref) &&
|
||||
- !zl3073x_dev_ref_is_status_ok(zldev, *ref))
|
||||
- *ref = ZL3073X_DPLL_REF_NONE;
|
||||
+ /* A reference is connected only when the DPLL is locked to it */
|
||||
+ state = zl3073x_chan_refsel_state_get(chan);
|
||||
+ if (state == ZL_DPLL_REFSEL_STATUS_STATE_LOCK)
|
||||
+ return zl3073x_chan_refsel_ref_get(chan);
|
||||
|
||||
- return 0;
|
||||
+ return ZL3073X_DPLL_REF_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -324,12 +279,9 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
|
||||
const struct zl3073x_ref *ref;
|
||||
u8 conn_id, ref_id;
|
||||
s64 ref_phase;
|
||||
- int rc;
|
||||
|
||||
/* Get currently connected reference */
|
||||
- rc = zl3073x_dpll_connected_ref_get(zldpll, &conn_id);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
+ conn_id = zl3073x_dpll_connected_ref_get(zldpll);
|
||||
|
||||
/* Report phase offset only for currently connected pin if the phase
|
||||
* monitor feature is disabled and only if the input pin signal is
|
||||
@@ -367,7 +319,7 @@ zl3073x_dpll_input_pin_phase_offset_get(const struct dpll_pin *dpll_pin,
|
||||
|
||||
*phase_offset = ref_phase * DPLL_PHASE_OFFSET_DIVIDER;
|
||||
|
||||
- return rc;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -447,18 +399,13 @@ zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
|
||||
struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
const struct zl3073x_chan *chan;
|
||||
- u8 ref, ref_conn;
|
||||
- int rc;
|
||||
+ u8 ref;
|
||||
|
||||
chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
|
||||
- /* Get currently connected reference */
|
||||
- rc = zl3073x_dpll_connected_ref_get(zldpll, &ref_conn);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- if (ref == ref_conn) {
|
||||
+ /* Check if the pin reference is connected */
|
||||
+ if (ref == zl3073x_dpll_connected_ref_get(zldpll)) {
|
||||
*state = DPLL_PIN_STATE_CONNECTED;
|
||||
return 0;
|
||||
}
|
||||
@@ -1087,13 +1034,8 @@ zl3073x_dpll_mode_set(const struct dpll_device *dpll, void *dpll_priv,
|
||||
u8 hw_mode, ref;
|
||||
int rc;
|
||||
|
||||
- rc = zl3073x_dpll_selected_ref_get(zldpll, &ref);
|
||||
- if (rc) {
|
||||
- NL_SET_ERR_MSG_MOD(extack, "failed to get selected reference");
|
||||
- return rc;
|
||||
- }
|
||||
-
|
||||
chan = *zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ ref = zl3073x_chan_refsel_ref_get(&chan);
|
||||
|
||||
if (mode == DPLL_MODE_MANUAL) {
|
||||
/* We are switching from automatic to manual mode:
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
211
1258-dpll-add-frequency-monitoring-to-netlink-spec.patch
Normal file
211
1258-dpll-add-frequency-monitoring-to-netlink-spec.patch
Normal file
@ -0,0 +1,211 @@
|
||||
From cf443396fbf1d5230af6f3e6ff9429f897ac1e2e Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Mon, 13 Apr 2026 17:47:07 +0200
|
||||
Subject: [PATCH] dpll: add frequency monitoring to netlink spec
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-165161
|
||||
|
||||
commit 3fdea79c09d169b6ea172b8d36232c3773f39973
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Thu Apr 2 20:40:55 2026 +0200
|
||||
|
||||
dpll: add frequency monitoring to netlink spec
|
||||
|
||||
Add DPLL_A_FREQUENCY_MONITOR device attribute to allow control over
|
||||
the frequency monitor feature. The attribute uses the existing
|
||||
dpll_feature_state enum (enable/disable) and is present in both
|
||||
device-get reply and device-set request.
|
||||
|
||||
Add DPLL_A_PIN_MEASURED_FREQUENCY pin attribute to expose the measured
|
||||
input frequency in millihertz (mHz). The attribute is present in the
|
||||
pin-get reply. Add DPLL_PIN_MEASURED_FREQUENCY_DIVIDER constant to
|
||||
allow userspace to extract integer and fractional parts.
|
||||
|
||||
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260402184057.1890514-2-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 3fdea79c09d169b6ea172b8d36232c3773f39973)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
|
||||
index 83118c728ed9..93c191b2d089 100644
|
||||
--- a/Documentation/driver-api/dpll.rst
|
||||
+++ b/Documentation/driver-api/dpll.rst
|
||||
@@ -250,6 +250,24 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
|
||||
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
|
||||
=============================== ========================
|
||||
|
||||
+Frequency monitor
|
||||
+=================
|
||||
+
|
||||
+Some DPLL devices may offer the capability to measure the actual
|
||||
+frequency of all available input pins. The attribute and current feature state
|
||||
+shall be included in the response message of the ``DPLL_CMD_DEVICE_GET``
|
||||
+command for supported DPLL devices. In such cases, users can also control
|
||||
+the feature using the ``DPLL_CMD_DEVICE_SET`` command by setting the
|
||||
+``enum dpll_feature_state`` values for the attribute.
|
||||
+Once enabled the measured input frequency for each input pin shall be
|
||||
+returned in the ``DPLL_A_PIN_MEASURED_FREQUENCY`` attribute. The value
|
||||
+is in millihertz (mHz), using ``DPLL_PIN_MEASURED_FREQUENCY_DIVIDER``
|
||||
+as the divider.
|
||||
+
|
||||
+ =============================== ========================
|
||||
+ ``DPLL_A_FREQUENCY_MONITOR`` attr state of a feature
|
||||
+ =============================== ========================
|
||||
+
|
||||
Embedded SYNC
|
||||
=============
|
||||
|
||||
@@ -411,6 +429,8 @@ according to attribute purpose.
|
||||
``DPLL_A_PIN_STATE`` attr state of pin on the parent
|
||||
pin
|
||||
``DPLL_A_PIN_CAPABILITIES`` attr bitmask of pin capabilities
|
||||
+ ``DPLL_A_PIN_MEASURED_FREQUENCY`` attr measured frequency of
|
||||
+ an input pin in mHz
|
||||
==================================== ==================================
|
||||
|
||||
==================================== =================================
|
||||
diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
|
||||
index ed5866238682..cb5da356bb64 100644
|
||||
--- a/Documentation/netlink/specs/dpll.yaml
|
||||
+++ b/Documentation/netlink/specs/dpll.yaml
|
||||
@@ -240,6 +240,20 @@ definitions:
|
||||
integer part of a measured phase offset value.
|
||||
Value of (DPLL_A_PHASE_OFFSET % DPLL_PHASE_OFFSET_DIVIDER) is a
|
||||
fractional part of a measured phase offset value.
|
||||
+ -
|
||||
+ type: const
|
||||
+ name: pin-measured-frequency-divider
|
||||
+ value: 1000
|
||||
+ doc: |
|
||||
+ pin measured frequency divider allows userspace to calculate
|
||||
+ a value of measured input frequency as a fractional value with
|
||||
+ three digit decimal precision (millihertz).
|
||||
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
||||
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part of
|
||||
+ a measured frequency value.
|
||||
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
||||
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part of
|
||||
+ a measured frequency value.
|
||||
-
|
||||
type: enum
|
||||
name: feature-state
|
||||
@@ -319,6 +333,13 @@ attribute-sets:
|
||||
name: phase-offset-avg-factor
|
||||
type: u32
|
||||
doc: Averaging factor applied to calculation of reported phase offset.
|
||||
+ -
|
||||
+ name: frequency-monitor
|
||||
+ type: u32
|
||||
+ enum: feature-state
|
||||
+ doc: Current or desired state of the frequency monitor feature.
|
||||
+ If enabled, dpll device shall measure all currently available
|
||||
+ inputs for their actual input frequency.
|
||||
-
|
||||
name: pin
|
||||
enum-name: dpll_a_pin
|
||||
@@ -456,6 +477,17 @@ attribute-sets:
|
||||
Value is in PPT (parts per trillion, 10^-12).
|
||||
Note: This attribute provides higher resolution than the standard
|
||||
fractional-frequency-offset (which is in PPM).
|
||||
+ -
|
||||
+ name: measured-frequency
|
||||
+ type: u64
|
||||
+ doc: |
|
||||
+ The measured frequency of the input pin in millihertz (mHz).
|
||||
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY /
|
||||
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is an integer part (Hz)
|
||||
+ of a measured frequency value.
|
||||
+ Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
||||
+ DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part
|
||||
+ of a measured frequency value.
|
||||
|
||||
-
|
||||
name: pin-parent-device
|
||||
@@ -544,6 +576,7 @@ operations:
|
||||
- type
|
||||
- phase-offset-monitor
|
||||
- phase-offset-avg-factor
|
||||
+ - frequency-monitor
|
||||
|
||||
dump:
|
||||
reply: *dev-attrs
|
||||
@@ -563,6 +596,7 @@ operations:
|
||||
- mode
|
||||
- phase-offset-monitor
|
||||
- phase-offset-avg-factor
|
||||
+ - frequency-monitor
|
||||
-
|
||||
name: device-create-ntf
|
||||
doc: Notification about device appearing
|
||||
@@ -643,6 +677,7 @@ operations:
|
||||
- esync-frequency-supported
|
||||
- esync-pulse
|
||||
- reference-sync
|
||||
+ - measured-frequency
|
||||
|
||||
dump:
|
||||
request:
|
||||
diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c
|
||||
index 3fb64aab18fa..da92aa8f9bc7 100644
|
||||
--- a/drivers/dpll/dpll_nl.c
|
||||
+++ b/drivers/dpll/dpll_nl.c
|
||||
@@ -42,11 +42,12 @@ static const struct nla_policy dpll_device_get_nl_policy[DPLL_A_ID + 1] = {
|
||||
};
|
||||
|
||||
/* DPLL_CMD_DEVICE_SET - do */
|
||||
-static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_PHASE_OFFSET_AVG_FACTOR + 1] = {
|
||||
+static const struct nla_policy dpll_device_set_nl_policy[DPLL_A_FREQUENCY_MONITOR + 1] = {
|
||||
[DPLL_A_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_MODE] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
[DPLL_A_PHASE_OFFSET_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
||||
[DPLL_A_PHASE_OFFSET_AVG_FACTOR] = { .type = NLA_U32, },
|
||||
+ [DPLL_A_FREQUENCY_MONITOR] = NLA_POLICY_MAX(NLA_U32, 1),
|
||||
};
|
||||
|
||||
/* DPLL_CMD_PIN_ID_GET - do */
|
||||
@@ -114,7 +115,7 @@ static const struct genl_split_ops dpll_nl_ops[] = {
|
||||
.doit = dpll_nl_device_set_doit,
|
||||
.post_doit = dpll_post_doit,
|
||||
.policy = dpll_device_set_nl_policy,
|
||||
- .maxattr = DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
||||
+ .maxattr = DPLL_A_FREQUENCY_MONITOR,
|
||||
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
|
||||
index 603a88cb665f..93614ccd8a84 100644
|
||||
--- a/include/uapi/linux/dpll.h
|
||||
+++ b/include/uapi/linux/dpll.h
|
||||
@@ -190,7 +190,8 @@ enum dpll_pin_capabilities {
|
||||
DPLL_PIN_CAPABILITIES_STATE_CAN_CHANGE = 4,
|
||||
};
|
||||
|
||||
-#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
||||
+#define DPLL_PHASE_OFFSET_DIVIDER 1000
|
||||
+#define DPLL_PIN_MEASURED_FREQUENCY_DIVIDER 1000
|
||||
|
||||
/**
|
||||
* enum dpll_feature_state - Allow control (enable/disable) and status checking
|
||||
@@ -217,6 +218,7 @@ enum dpll_a {
|
||||
DPLL_A_CLOCK_QUALITY_LEVEL,
|
||||
DPLL_A_PHASE_OFFSET_MONITOR,
|
||||
DPLL_A_PHASE_OFFSET_AVG_FACTOR,
|
||||
+ DPLL_A_FREQUENCY_MONITOR,
|
||||
|
||||
__DPLL_A_MAX,
|
||||
DPLL_A_MAX = (__DPLL_A_MAX - 1)
|
||||
@@ -253,6 +255,7 @@ enum dpll_a_pin {
|
||||
DPLL_A_PIN_REFERENCE_SYNC,
|
||||
DPLL_A_PIN_PHASE_ADJUST_GRAN,
|
||||
DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
|
||||
+ DPLL_A_PIN_MEASURED_FREQUENCY,
|
||||
|
||||
__DPLL_A_PIN_MAX,
|
||||
DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
224
1259-dpll-add-frequency-monitoring-callback-ops.patch
Normal file
224
1259-dpll-add-frequency-monitoring-callback-ops.patch
Normal file
@ -0,0 +1,224 @@
|
||||
From c549611ebab6cedb1f8d0981320051d4dfb86ccc Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Mon, 13 Apr 2026 17:49:27 +0200
|
||||
Subject: [PATCH] dpll: add frequency monitoring callback ops
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-165161
|
||||
|
||||
commit 15ed91aa84ea7bacef3c24286d5136055b4335a8
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Thu Apr 2 20:40:56 2026 +0200
|
||||
|
||||
dpll: add frequency monitoring callback ops
|
||||
|
||||
Add new callback operations for a dpll device:
|
||||
- freq_monitor_get(..) - to obtain current state of frequency monitor
|
||||
feature from dpll device,
|
||||
- freq_monitor_set(..) - to allow feature configuration.
|
||||
|
||||
Add new callback operation for a dpll pin:
|
||||
- measured_freq_get(..) - to obtain the measured frequency in mHz.
|
||||
|
||||
Obtain the feature state value using the get callback and provide it to
|
||||
the user if the device driver implements callbacks. The measured_freq_get
|
||||
pin callback is only invoked when the frequency monitor is enabled.
|
||||
The freq_monitor_get device callback is required when measured_freq_get
|
||||
is provided by the driver.
|
||||
|
||||
Execute the set callback upon user requests.
|
||||
|
||||
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260402184057.1890514-3-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
Conflicts:
|
||||
- include/linux/dpll.h: context conflict due usage of RH_KABI_* macros
|
||||
|
||||
(cherry picked from commit 15ed91aa84ea7bacef3c24286d5136055b4335a8)
|
||||
Assisted-by: Patchpal AI
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
|
||||
index 842f7334db4d..84e1b637b4e5 100644
|
||||
--- a/drivers/dpll/dpll_core.c
|
||||
+++ b/drivers/dpll/dpll_core.c
|
||||
@@ -880,7 +880,10 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
|
||||
if (WARN_ON(!ops) ||
|
||||
WARN_ON(!ops->state_on_dpll_get) ||
|
||||
- WARN_ON(!ops->direction_get))
|
||||
+ WARN_ON(!ops->direction_get) ||
|
||||
+ WARN_ON(ops->measured_freq_get &&
|
||||
+ (!dpll_device_ops(dpll)->freq_monitor_get ||
|
||||
+ !dpll_device_ops(dpll)->freq_monitor_set)))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
|
||||
index 83cbd64abf5a..af7ce62ec55c 100644
|
||||
--- a/drivers/dpll/dpll_netlink.c
|
||||
+++ b/drivers/dpll/dpll_netlink.c
|
||||
@@ -175,6 +175,26 @@ dpll_msg_add_phase_offset_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+dpll_msg_add_freq_monitor(struct sk_buff *msg, struct dpll_device *dpll,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
||||
+ enum dpll_feature_state state;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (ops->freq_monitor_set && ops->freq_monitor_get) {
|
||||
+ ret = ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
||||
+ &state, extack);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (nla_put_u32(msg, DPLL_A_FREQUENCY_MONITOR, state))
|
||||
+ return -EMSGSIZE;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
dpll_msg_add_phase_offset_avg_factor(struct sk_buff *msg,
|
||||
struct dpll_device *dpll,
|
||||
@@ -400,6 +420,38 @@ static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
ffo);
|
||||
}
|
||||
|
||||
+static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
+ struct dpll_pin_ref *ref,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ const struct dpll_device_ops *dev_ops = dpll_device_ops(ref->dpll);
|
||||
+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
|
||||
+ struct dpll_device *dpll = ref->dpll;
|
||||
+ enum dpll_feature_state state;
|
||||
+ u64 measured_freq;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!ops->measured_freq_get)
|
||||
+ return 0;
|
||||
+ ret = dev_ops->freq_monitor_get(dpll, dpll_priv(dpll),
|
||||
+ &state, extack);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (state == DPLL_FEATURE_STATE_DISABLE)
|
||||
+ return 0;
|
||||
+ ret = ops->measured_freq_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
+ dpll, dpll_priv(dpll), &measured_freq,
|
||||
+ extack);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (nla_put_64bit(msg, DPLL_A_PIN_MEASURED_FREQUENCY,
|
||||
+ sizeof(measured_freq), &measured_freq,
|
||||
+ DPLL_A_PIN_PAD))
|
||||
+ return -EMSGSIZE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
dpll_msg_add_pin_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref, struct netlink_ext_ack *extack)
|
||||
@@ -670,6 +722,9 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_ffo(msg, pin, ref, extack);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = dpll_msg_add_measured_freq(msg, pin, ref, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_pin_esync(msg, pin, ref, extack);
|
||||
@@ -722,6 +777,9 @@ dpll_device_get_one(struct dpll_device *dpll, struct sk_buff *msg,
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_phase_offset_avg_factor(msg, dpll, extack);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ ret = dpll_msg_add_freq_monitor(msg, dpll, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -948,6 +1006,32 @@ dpll_phase_offset_avg_factor_set(struct dpll_device *dpll, struct nlattr *a,
|
||||
extack);
|
||||
}
|
||||
|
||||
+static int
|
||||
+dpll_freq_monitor_set(struct dpll_device *dpll, struct nlattr *a,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ const struct dpll_device_ops *ops = dpll_device_ops(dpll);
|
||||
+ enum dpll_feature_state state = nla_get_u32(a), old_state;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!(ops->freq_monitor_set && ops->freq_monitor_get)) {
|
||||
+ NL_SET_ERR_MSG_ATTR(extack, a,
|
||||
+ "dpll device not capable of frequency monitor");
|
||||
+ return -EOPNOTSUPP;
|
||||
+ }
|
||||
+ ret = ops->freq_monitor_get(dpll, dpll_priv(dpll), &old_state,
|
||||
+ extack);
|
||||
+ if (ret) {
|
||||
+ NL_SET_ERR_MSG(extack,
|
||||
+ "unable to get current state of frequency monitor");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ if (state == old_state)
|
||||
+ return 0;
|
||||
+
|
||||
+ return ops->freq_monitor_set(dpll, dpll_priv(dpll), state, extack);
|
||||
+}
|
||||
+
|
||||
static int
|
||||
dpll_pin_freq_set(struct dpll_pin *pin, struct nlattr *a,
|
||||
struct netlink_ext_ack *extack)
|
||||
@@ -1878,6 +1962,12 @@ dpll_set_from_nlattr(struct dpll_device *dpll, struct genl_info *info)
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
+ case DPLL_A_FREQUENCY_MONITOR:
|
||||
+ ret = dpll_freq_monitor_set(dpll, a,
|
||||
+ info->extack);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ break;
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
|
||||
index 8862389ed50a..441fe25ba4f4 100644
|
||||
--- a/include/linux/dpll.h
|
||||
+++ b/include/linux/dpll.h
|
||||
@@ -54,6 +54,12 @@ struct dpll_device_ops {
|
||||
int (*phase_offset_avg_factor_get)(const struct dpll_device *dpll,
|
||||
void *dpll_priv, u32 *factor,
|
||||
struct netlink_ext_ack *extack);
|
||||
+ int (*freq_monitor_set)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
+ enum dpll_feature_state state,
|
||||
+ struct netlink_ext_ack *extack);
|
||||
+ int (*freq_monitor_get)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
+ enum dpll_feature_state *state,
|
||||
+ struct netlink_ext_ack *extack);
|
||||
|
||||
RH_KABI_RESERVE(1)
|
||||
RH_KABI_RESERVE(2)
|
||||
@@ -123,6 +129,10 @@ struct dpll_pin_ops {
|
||||
int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
s64 *ffo, struct netlink_ext_ack *extack);
|
||||
+ int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
+ const struct dpll_device *dpll,
|
||||
+ void *dpll_priv, u64 *measured_freq,
|
||||
+ struct netlink_ext_ack *extack);
|
||||
int (*esync_set)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
u64 freq, struct netlink_ext_ack *extack);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
399
1260-dpll-zl3073x-implement-frequency-monitoring.patch
Normal file
399
1260-dpll-zl3073x-implement-frequency-monitoring.patch
Normal file
@ -0,0 +1,399 @@
|
||||
From 7f9d57090a5680a3a47f6695f886ca8feb0e5bac Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:51:52 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: implement frequency monitoring
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-165161
|
||||
|
||||
commit bfc923b642874ea6f94763d6060782072944ebd5
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Thu Apr 2 20:40:57 2026 +0200
|
||||
|
||||
dpll: zl3073x: implement frequency monitoring
|
||||
|
||||
Extract common measurement latch logic from zl3073x_ref_ffo_update()
|
||||
into a new zl3073x_ref_freq_meas_latch() helper and add
|
||||
zl3073x_ref_freq_meas_update() that uses it to latch and read absolute
|
||||
input reference frequencies in Hz.
|
||||
|
||||
Add meas_freq field to struct zl3073x_ref and the corresponding
|
||||
zl3073x_ref_meas_freq_get() accessor. The measured frequencies are
|
||||
updated periodically alongside the existing FFO measurements.
|
||||
|
||||
Add freq_monitor boolean to struct zl3073x_dpll and implement the
|
||||
freq_monitor_set/get device callbacks to enable/disable frequency
|
||||
monitoring via the DPLL netlink interface.
|
||||
|
||||
Implement measured_freq_get pin callback for input pins that returns the
|
||||
measured input frequency in mHz.
|
||||
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260402184057.1890514-4-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit bfc923b642874ea6f94763d6060782072944ebd5)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index 6363002d48d4..cb47a5db061a 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -632,22 +632,21 @@ int zl3073x_ref_phase_offsets_update(struct zl3073x_dev *zldev, int channel)
|
||||
}
|
||||
|
||||
/**
|
||||
- * zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
||||
+ * zl3073x_ref_freq_meas_latch - latch reference frequency measurements
|
||||
* @zldev: pointer to zl3073x_dev structure
|
||||
+ * @type: measurement type (ZL_REF_FREQ_MEAS_CTRL_*)
|
||||
*
|
||||
- * The function asks device to update fractional frequency offsets latch
|
||||
- * registers the latest measured values, reads and stores them into
|
||||
+ * The function waits for the previous measurement to finish, selects all
|
||||
+ * references and requests a new measurement of the given type.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
-zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
+zl3073x_ref_freq_meas_latch(struct zl3073x_dev *zldev, u8 type)
|
||||
{
|
||||
- int i, rc;
|
||||
+ int rc;
|
||||
|
||||
- /* Per datasheet we have to wait for 'ref_freq_meas_ctrl' to be zero
|
||||
- * to ensure that the measured data are coherent.
|
||||
- */
|
||||
+ /* Wait for previous measurement to finish */
|
||||
rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
ZL_REF_FREQ_MEAS_CTRL);
|
||||
if (rc)
|
||||
@@ -663,15 +662,64 @@ zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
- /* Request frequency offset measurement */
|
||||
- rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
- ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
||||
+ /* Request measurement */
|
||||
+ rc = zl3073x_write_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL, type);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Wait for finish */
|
||||
- rc = zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
- ZL_REF_FREQ_MEAS_CTRL);
|
||||
+ return zl3073x_poll_zero_u8(zldev, ZL_REG_REF_FREQ_MEAS_CTRL,
|
||||
+ ZL_REF_FREQ_MEAS_CTRL);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_ref_freq_meas_update - update measured input reference frequencies
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ *
|
||||
+ * The function asks device to latch measured input reference frequencies
|
||||
+ * and stores the results in the ref state.
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on error
|
||||
+ */
|
||||
+static int
|
||||
+zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
|
||||
+{
|
||||
+ int i, rc;
|
||||
+
|
||||
+ rc = zl3073x_ref_freq_meas_latch(zldev, ZL_REF_FREQ_MEAS_CTRL_REF_FREQ);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ /* Read measured frequencies in Hz (unsigned 32-bit, LSB = 1 Hz) */
|
||||
+ for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
||||
+ u32 value;
|
||||
+
|
||||
+ rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ zldev->ref[i].meas_freq = value;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
||||
+ * @zldev: pointer to zl3073x_dev structure
|
||||
+ *
|
||||
+ * The function asks device to latch the latest measured fractional
|
||||
+ * frequency offset values, reads and stores them into the ref state.
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on error
|
||||
+ */
|
||||
+static int
|
||||
+zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
+{
|
||||
+ int i, rc;
|
||||
+
|
||||
+ rc = zl3073x_ref_freq_meas_latch(zldev,
|
||||
+ ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@@ -714,6 +762,20 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
dev_warn(zldev->dev, "Failed to update phase offsets: %pe\n",
|
||||
ERR_PTR(rc));
|
||||
|
||||
+ /* Update measured input reference frequencies if any DPLL has
|
||||
+ * frequency monitoring enabled.
|
||||
+ */
|
||||
+ list_for_each_entry(zldpll, &zldev->dplls, list) {
|
||||
+ if (zldpll->freq_monitor) {
|
||||
+ rc = zl3073x_ref_freq_meas_update(zldev);
|
||||
+ if (rc)
|
||||
+ dev_warn(zldev->dev,
|
||||
+ "Failed to update measured frequencies: %pe\n",
|
||||
+ ERR_PTR(rc));
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* Update references' fractional frequency offsets */
|
||||
rc = zl3073x_ref_ffo_update(zldev);
|
||||
if (rc)
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index cf7a335baed5..2b307a467a7f 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -39,6 +39,7 @@
|
||||
* @pin_state: last saved pin state
|
||||
* @phase_offset: last saved pin phase offset
|
||||
* @freq_offset: last saved fractional frequency offset
|
||||
+ * @measured_freq: last saved measured frequency
|
||||
*/
|
||||
struct zl3073x_dpll_pin {
|
||||
struct list_head list;
|
||||
@@ -54,6 +55,7 @@ struct zl3073x_dpll_pin {
|
||||
enum dpll_pin_state pin_state;
|
||||
s64 phase_offset;
|
||||
s64 freq_offset;
|
||||
+ u32 measured_freq;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -202,6 +204,21 @@ zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+zl3073x_dpll_input_pin_measured_freq_get(const struct dpll_pin *dpll_pin,
|
||||
+ void *pin_priv,
|
||||
+ const struct dpll_device *dpll,
|
||||
+ void *dpll_priv, u64 *measured_freq,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
+
|
||||
+ *measured_freq = pin->measured_freq;
|
||||
+ *measured_freq *= DPLL_PIN_MEASURED_FREQUENCY_DIVIDER;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
zl3073x_dpll_input_pin_frequency_get(const struct dpll_pin *dpll_pin,
|
||||
void *pin_priv,
|
||||
@@ -1116,6 +1133,35 @@ zl3073x_dpll_phase_offset_monitor_set(const struct dpll_device *dpll,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+zl3073x_dpll_freq_monitor_get(const struct dpll_device *dpll,
|
||||
+ void *dpll_priv,
|
||||
+ enum dpll_feature_state *state,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
+
|
||||
+ if (zldpll->freq_monitor)
|
||||
+ *state = DPLL_FEATURE_STATE_ENABLE;
|
||||
+ else
|
||||
+ *state = DPLL_FEATURE_STATE_DISABLE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
|
||||
+ void *dpll_priv,
|
||||
+ enum dpll_feature_state state,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
+
|
||||
+ zldpll->freq_monitor = (state == DPLL_FEATURE_STATE_ENABLE);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
.direction_get = zl3073x_dpll_pin_direction_get,
|
||||
.esync_get = zl3073x_dpll_input_pin_esync_get,
|
||||
@@ -1123,6 +1169,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
.ffo_get = zl3073x_dpll_input_pin_ffo_get,
|
||||
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
|
||||
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
|
||||
+ .measured_freq_get = zl3073x_dpll_input_pin_measured_freq_get,
|
||||
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
|
||||
.phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get,
|
||||
.phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
|
||||
@@ -1151,6 +1198,8 @@ static const struct dpll_device_ops zl3073x_dpll_device_ops = {
|
||||
.phase_offset_avg_factor_set = zl3073x_dpll_phase_offset_avg_factor_set,
|
||||
.phase_offset_monitor_get = zl3073x_dpll_phase_offset_monitor_get,
|
||||
.phase_offset_monitor_set = zl3073x_dpll_phase_offset_monitor_set,
|
||||
+ .freq_monitor_get = zl3073x_dpll_freq_monitor_get,
|
||||
+ .freq_monitor_set = zl3073x_dpll_freq_monitor_set,
|
||||
.supported_modes_get = zl3073x_dpll_supported_modes_get,
|
||||
};
|
||||
|
||||
@@ -1572,6 +1621,7 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
const struct zl3073x_ref *ref;
|
||||
u8 ref_id;
|
||||
+ s64 ffo;
|
||||
|
||||
/* Get reference monitor status */
|
||||
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
@@ -1582,10 +1632,47 @@ zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
|
||||
return false;
|
||||
|
||||
/* Compare with previous value */
|
||||
- if (pin->freq_offset != ref->ffo) {
|
||||
+ ffo = zl3073x_ref_ffo_get(ref);
|
||||
+ if (pin->freq_offset != ffo) {
|
||||
dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n",
|
||||
- pin->label, pin->freq_offset, ref->ffo);
|
||||
- pin->freq_offset = ref->ffo;
|
||||
+ pin->label, pin->freq_offset, ffo);
|
||||
+ pin->freq_offset = ffo;
|
||||
+
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_dpll_pin_measured_freq_check - check for pin measured frequency
|
||||
+ * change
|
||||
+ * @pin: pin to check
|
||||
+ *
|
||||
+ * Check for the given pin's measured frequency change.
|
||||
+ *
|
||||
+ * Return: true on measured frequency change, false otherwise
|
||||
+ */
|
||||
+static bool
|
||||
+zl3073x_dpll_pin_measured_freq_check(struct zl3073x_dpll_pin *pin)
|
||||
+{
|
||||
+ struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
+ struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ const struct zl3073x_ref *ref;
|
||||
+ u8 ref_id;
|
||||
+ u32 freq;
|
||||
+
|
||||
+ if (!zldpll->freq_monitor)
|
||||
+ return false;
|
||||
+
|
||||
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ ref = zl3073x_ref_state_get(zldev, ref_id);
|
||||
+
|
||||
+ freq = zl3073x_ref_meas_freq_get(ref);
|
||||
+ if (pin->measured_freq != freq) {
|
||||
+ dev_dbg(zldev->dev, "%s measured freq changed: %u -> %u\n",
|
||||
+ pin->label, pin->measured_freq, freq);
|
||||
+ pin->measured_freq = freq;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1677,13 +1764,18 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
||||
pin_changed = true;
|
||||
}
|
||||
|
||||
- /* Check for phase offset and ffo change once per second */
|
||||
+ /* Check for phase offset, ffo, and measured freq change
|
||||
+ * once per second.
|
||||
+ */
|
||||
if (zldpll->check_count % 2 == 0) {
|
||||
if (zl3073x_dpll_pin_phase_offset_check(pin))
|
||||
pin_changed = true;
|
||||
|
||||
if (zl3073x_dpll_pin_ffo_check(pin))
|
||||
pin_changed = true;
|
||||
+
|
||||
+ if (zl3073x_dpll_pin_measured_freq_check(pin))
|
||||
+ pin_changed = true;
|
||||
}
|
||||
|
||||
if (pin_changed)
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.h b/drivers/dpll/zl3073x/dpll.h
|
||||
index 115ee4f67e7a..434c32a7db12 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.h
|
||||
+++ b/drivers/dpll/zl3073x/dpll.h
|
||||
@@ -15,6 +15,7 @@
|
||||
* @id: DPLL index
|
||||
* @check_count: periodic check counter
|
||||
* @phase_monitor: is phase offset monitor enabled
|
||||
+ * @freq_monitor: is frequency monitor enabled
|
||||
* @ops: DPLL device operations for this instance
|
||||
* @dpll_dev: pointer to registered DPLL device
|
||||
* @tracker: tracking object for the acquired reference
|
||||
@@ -28,6 +29,7 @@ struct zl3073x_dpll {
|
||||
u8 id;
|
||||
u8 check_count;
|
||||
bool phase_monitor;
|
||||
+ bool freq_monitor;
|
||||
struct dpll_device_ops ops;
|
||||
struct dpll_device *dpll_dev;
|
||||
dpll_tracker tracker;
|
||||
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
|
||||
index 06d8d4d97ea2..be16be20dbc7 100644
|
||||
--- a/drivers/dpll/zl3073x/ref.h
|
||||
+++ b/drivers/dpll/zl3073x/ref.h
|
||||
@@ -23,6 +23,7 @@ struct zl3073x_dev;
|
||||
* @sync_ctrl: reference sync control
|
||||
* @config: reference config
|
||||
* @ffo: current fractional frequency offset
|
||||
+ * @meas_freq: measured input frequency in Hz
|
||||
* @mon_status: reference monitor status
|
||||
*/
|
||||
struct zl3073x_ref {
|
||||
@@ -40,6 +41,7 @@ struct zl3073x_ref {
|
||||
);
|
||||
struct_group(stat, /* Status */
|
||||
s64 ffo;
|
||||
+ u32 meas_freq;
|
||||
u8 mon_status;
|
||||
);
|
||||
};
|
||||
@@ -68,6 +70,18 @@ zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
|
||||
return ref->ffo;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_ref_meas_freq_get - get measured input frequency
|
||||
+ * @ref: pointer to ref state
|
||||
+ *
|
||||
+ * Return: measured input frequency in Hz
|
||||
+ */
|
||||
+static inline u32
|
||||
+zl3073x_ref_meas_freq_get(const struct zl3073x_ref *ref)
|
||||
+{
|
||||
+ return ref->meas_freq;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_ref_freq_get - get given input reference frequency
|
||||
* @ref: pointer to ref state
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,181 @@
|
||||
From 83c5fcf1f1b2adc253e0eb10ae85ef11ce0ded24 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:55:25 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: clean up esync get/set and use
|
||||
zl3073x_out_is_ndiv()
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157828
|
||||
|
||||
commit 3c8c39768b10867e4f630080785b602245f01760
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed Apr 8 12:27:12 2026 +0200
|
||||
|
||||
dpll: zl3073x: clean up esync get/set and use zl3073x_out_is_ndiv()
|
||||
|
||||
Return -EOPNOTSUPP early in esync_get callbacks when esync is not
|
||||
supported instead of conditionally populating the range at the end.
|
||||
This simplifies the control flow by removing the finish label/goto
|
||||
in the output variant and the conditional range assignment in both
|
||||
input and output variants.
|
||||
|
||||
Replace open-coded N-div signal format switch statements with
|
||||
zl3073x_out_is_ndiv() helper in esync_get, esync_set and
|
||||
frequency_set callbacks.
|
||||
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Reviewed-by: Prathosh Satish <Prathosh.Satish@microchip.com>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260408102716.443099-2-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 3c8c39768b10867e4f630080785b602245f01760)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 2b307a467a7f..86df28415318 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -133,6 +133,12 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
ref = zl3073x_ref_state_get(zldev, ref_id);
|
||||
|
||||
+ if (!pin->esync_control || zl3073x_ref_freq_get(ref) <= 1)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ esync->range = esync_freq_ranges;
|
||||
+ esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
||||
+
|
||||
switch (FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl)) {
|
||||
case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75:
|
||||
esync->freq = ref->esync_n_div == ZL_REF_ESYNC_DIV_1HZ ? 1 : 0;
|
||||
@@ -144,17 +150,6 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
break;
|
||||
}
|
||||
|
||||
- /* If the pin supports esync control expose its range but only
|
||||
- * if the current reference frequency is > 1 Hz.
|
||||
- */
|
||||
- if (pin->esync_control && zl3073x_ref_freq_get(ref) > 1) {
|
||||
- esync->range = esync_freq_ranges;
|
||||
- esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
||||
- } else {
|
||||
- esync->range = NULL;
|
||||
- esync->range_num = 0;
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -599,8 +594,8 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
const struct zl3073x_synth *synth;
|
||||
const struct zl3073x_out *out;
|
||||
+ u32 synth_freq, out_freq;
|
||||
u8 clock_type, out_id;
|
||||
- u32 synth_freq;
|
||||
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = zl3073x_out_state_get(zldev, out_id);
|
||||
@@ -609,17 +604,19 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
* for N-division is also used for the esync divider so both cannot
|
||||
* be used.
|
||||
*/
|
||||
- switch (zl3073x_out_signal_format_get(out)) {
|
||||
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
|
||||
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
|
||||
+ if (zl3073x_out_is_ndiv(out))
|
||||
return -EOPNOTSUPP;
|
||||
- default:
|
||||
- break;
|
||||
- }
|
||||
|
||||
/* Get attached synth frequency */
|
||||
synth = zl3073x_synth_state_get(zldev, zl3073x_out_synth_get(out));
|
||||
synth_freq = zl3073x_synth_freq_get(synth);
|
||||
+ out_freq = synth_freq / out->div;
|
||||
+
|
||||
+ if (!pin->esync_control || out_freq <= 1)
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ esync->range = esync_freq_ranges;
|
||||
+ esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
||||
|
||||
clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
|
||||
if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
|
||||
@@ -627,11 +624,11 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
esync->freq = 0;
|
||||
esync->pulse = 0;
|
||||
|
||||
- goto finish;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
/* Compute esync frequency */
|
||||
- esync->freq = synth_freq / out->div / out->esync_n_period;
|
||||
+ esync->freq = out_freq / out->esync_n_period;
|
||||
|
||||
/* By comparing the esync_pulse_width to the half of the pulse width
|
||||
* the esync pulse percentage can be determined.
|
||||
@@ -640,18 +637,6 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
*/
|
||||
esync->pulse = (50 * out->esync_n_width) / out->div;
|
||||
|
||||
-finish:
|
||||
- /* Set supported esync ranges if the pin supports esync control and
|
||||
- * if the output frequency is > 1 Hz.
|
||||
- */
|
||||
- if (pin->esync_control && (synth_freq / out->div) > 1) {
|
||||
- esync->range = esync_freq_ranges;
|
||||
- esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
||||
- } else {
|
||||
- esync->range = NULL;
|
||||
- esync->range_num = 0;
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -677,13 +662,8 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
* for N-division is also used for the esync divider so both cannot
|
||||
* be used.
|
||||
*/
|
||||
- switch (zl3073x_out_signal_format_get(&out)) {
|
||||
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV:
|
||||
- case ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV:
|
||||
+ if (zl3073x_out_is_ndiv(&out))
|
||||
return -EOPNOTSUPP;
|
||||
- default:
|
||||
- break;
|
||||
- }
|
||||
|
||||
/* Select clock type */
|
||||
if (freq)
|
||||
@@ -745,9 +725,9 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
const struct zl3073x_synth *synth;
|
||||
- u8 out_id, signal_format;
|
||||
u32 new_div, synth_freq;
|
||||
struct zl3073x_out out;
|
||||
+ u8 out_id;
|
||||
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = *zl3073x_out_state_get(zldev, out_id);
|
||||
@@ -757,12 +737,8 @@ zl3073x_dpll_output_pin_frequency_set(const struct dpll_pin *dpll_pin,
|
||||
synth_freq = zl3073x_synth_freq_get(synth);
|
||||
new_div = synth_freq / (u32)frequency;
|
||||
|
||||
- /* Get used signal format for the given output */
|
||||
- signal_format = zl3073x_out_signal_format_get(&out);
|
||||
-
|
||||
/* Check signal format */
|
||||
- if (signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV &&
|
||||
- signal_format != ZL_OUTPUT_MODE_SIGNAL_FORMAT_2_NDIV_INV) {
|
||||
+ if (!zl3073x_out_is_ndiv(&out)) {
|
||||
/* For non N-divided signal formats the frequency is computed
|
||||
* as division of synth frequency and output divisor.
|
||||
*/
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,99 @@
|
||||
From f4680f5c34a8af844573d88340c8b625a0628d25 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:55:32 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: use FIELD_MODIFY() for clear-and-set patterns
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157828
|
||||
|
||||
commit 737cb6195c40acf67c876f509e209158436cf287
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed Apr 8 12:27:13 2026 +0200
|
||||
|
||||
dpll: zl3073x: use FIELD_MODIFY() for clear-and-set patterns
|
||||
|
||||
Replace open-coded clear-and-set bitfield operations with
|
||||
FIELD_MODIFY().
|
||||
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Reviewed-by: Prathosh Satish <Prathosh.Satish@microchip.com>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260408102716.443099-3-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 737cb6195c40acf67c876f509e209158436cf287)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
|
||||
index e0f02d343208..481da2133202 100644
|
||||
--- a/drivers/dpll/zl3073x/chan.h
|
||||
+++ b/drivers/dpll/zl3073x/chan.h
|
||||
@@ -66,8 +66,7 @@ static inline u8 zl3073x_chan_ref_get(const struct zl3073x_chan *chan)
|
||||
*/
|
||||
static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)
|
||||
{
|
||||
- chan->mode_refsel &= ~ZL_DPLL_MODE_REFSEL_MODE;
|
||||
- chan->mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_MODE, mode);
|
||||
+ FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_MODE, &chan->mode_refsel, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,8 +76,7 @@ static inline void zl3073x_chan_mode_set(struct zl3073x_chan *chan, u8 mode)
|
||||
*/
|
||||
static inline void zl3073x_chan_ref_set(struct zl3073x_chan *chan, u8 ref)
|
||||
{
|
||||
- chan->mode_refsel &= ~ZL_DPLL_MODE_REFSEL_REF;
|
||||
- chan->mode_refsel |= FIELD_PREP(ZL_DPLL_MODE_REFSEL_REF, ref);
|
||||
+ FIELD_MODIFY(ZL_DPLL_MODE_REFSEL_REF, &chan->mode_refsel, ref);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -110,13 +108,10 @@ zl3073x_chan_ref_prio_set(struct zl3073x_chan *chan, u8 ref, u8 prio)
|
||||
{
|
||||
u8 *val = &chan->ref_prio[ref / 2];
|
||||
|
||||
- if (!(ref & 1)) {
|
||||
- *val &= ~ZL_DPLL_REF_PRIO_REF_P;
|
||||
- *val |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_P, prio);
|
||||
- } else {
|
||||
- *val &= ~ZL_DPLL_REF_PRIO_REF_N;
|
||||
- *val |= FIELD_PREP(ZL_DPLL_REF_PRIO_REF_N, prio);
|
||||
- }
|
||||
+ if (!(ref & 1))
|
||||
+ FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_P, val, prio);
|
||||
+ else
|
||||
+ FIELD_MODIFY(ZL_DPLL_REF_PRIO_REF_N, val, prio);
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index cb47a5db061a..5f1e70f3e40a 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -805,8 +805,7 @@ int zl3073x_dev_phase_avg_factor_set(struct zl3073x_dev *zldev, u8 factor)
|
||||
value = (factor + 1) & 0x0f;
|
||||
|
||||
/* Update phase measurement control register */
|
||||
- dpll_meas_ctrl &= ~ZL_DPLL_MEAS_CTRL_AVG_FACTOR;
|
||||
- dpll_meas_ctrl |= FIELD_PREP(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, value);
|
||||
+ FIELD_MODIFY(ZL_DPLL_MEAS_CTRL_AVG_FACTOR, &dpll_meas_ctrl, value);
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_MEAS_CTRL, dpll_meas_ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
diff --git a/drivers/dpll/zl3073x/flash.c b/drivers/dpll/zl3073x/flash.c
|
||||
index 83452a77e3e9..f85535c8ad24 100644
|
||||
--- a/drivers/dpll/zl3073x/flash.c
|
||||
+++ b/drivers/dpll/zl3073x/flash.c
|
||||
@@ -194,8 +194,7 @@ zl3073x_flash_cmd_wait(struct zl3073x_dev *zldev, u32 operation,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
- value &= ~ZL_WRITE_FLASH_OP;
|
||||
- value |= FIELD_PREP(ZL_WRITE_FLASH_OP, operation);
|
||||
+ FIELD_MODIFY(ZL_WRITE_FLASH_OP, &value, operation);
|
||||
|
||||
rc = zl3073x_write_u8(zldev, ZL_REG_WRITE_FLASH, value);
|
||||
if (rc)
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,219 @@
|
||||
From ea098b764d85e9c992398b51e59f4fc69a7351ba Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:55:39 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: add ref sync and output clock type helpers
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157828
|
||||
|
||||
commit 63009eb92b0f379afddbba8dfdf8df087f6d5b62
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed Apr 8 12:27:14 2026 +0200
|
||||
|
||||
dpll: zl3073x: add ref sync and output clock type helpers
|
||||
|
||||
Add ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR and ZL_REF_SYNC_CTRL_PAIR
|
||||
register definitions.
|
||||
|
||||
Add inline helpers to get and set the sync control mode and sync pair
|
||||
fields of the reference sync control register:
|
||||
|
||||
zl3073x_ref_sync_mode_get/set() - ZL_REF_SYNC_CTRL_MODE field
|
||||
zl3073x_ref_sync_pair_get/set() - ZL_REF_SYNC_CTRL_PAIR field
|
||||
|
||||
Add inline helpers to get and set the clock type field of the output
|
||||
mode register:
|
||||
|
||||
zl3073x_out_clock_type_get/set() - ZL_OUTPUT_MODE_CLOCK_TYPE field
|
||||
|
||||
Convert existing esync callbacks to use the new helpers.
|
||||
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Reviewed-by: Prathosh Satish <Prathosh.Satish@microchip.com>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260408102716.443099-4-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 63009eb92b0f379afddbba8dfdf8df087f6d5b62)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 86df28415318..a151be700e6e 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -139,7 +139,7 @@ zl3073x_dpll_input_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
esync->range = esync_freq_ranges;
|
||||
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
||||
|
||||
- switch (FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl)) {
|
||||
+ switch (zl3073x_ref_sync_mode_get(ref)) {
|
||||
case ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75:
|
||||
esync->freq = ref->esync_n_div == ZL_REF_ESYNC_DIV_1HZ ? 1 : 0;
|
||||
esync->pulse = 25;
|
||||
@@ -175,8 +175,7 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
else
|
||||
sync_mode = ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75;
|
||||
|
||||
- ref.sync_ctrl &= ~ZL_REF_SYNC_CTRL_MODE;
|
||||
- ref.sync_ctrl |= FIELD_PREP(ZL_REF_SYNC_CTRL_MODE, sync_mode);
|
||||
+ zl3073x_ref_sync_mode_set(&ref, sync_mode);
|
||||
|
||||
if (freq) {
|
||||
/* 1 Hz is only supported frequency now */
|
||||
@@ -595,7 +594,7 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
const struct zl3073x_synth *synth;
|
||||
const struct zl3073x_out *out;
|
||||
u32 synth_freq, out_freq;
|
||||
- u8 clock_type, out_id;
|
||||
+ u8 out_id;
|
||||
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = zl3073x_out_state_get(zldev, out_id);
|
||||
@@ -618,8 +617,7 @@ zl3073x_dpll_output_pin_esync_get(const struct dpll_pin *dpll_pin,
|
||||
esync->range = esync_freq_ranges;
|
||||
esync->range_num = ARRAY_SIZE(esync_freq_ranges);
|
||||
|
||||
- clock_type = FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
|
||||
- if (clock_type != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
|
||||
+ if (zl3073x_out_clock_type_get(out) != ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC) {
|
||||
/* No need to read esync data if it is not enabled */
|
||||
esync->freq = 0;
|
||||
esync->pulse = 0;
|
||||
@@ -652,8 +650,8 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
const struct zl3073x_synth *synth;
|
||||
struct zl3073x_out out;
|
||||
- u8 clock_type, out_id;
|
||||
u32 synth_freq;
|
||||
+ u8 out_id;
|
||||
|
||||
out_id = zl3073x_output_pin_out_get(pin->id);
|
||||
out = *zl3073x_out_state_get(zldev, out_id);
|
||||
@@ -665,15 +663,13 @@ zl3073x_dpll_output_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
if (zl3073x_out_is_ndiv(&out))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
- /* Select clock type */
|
||||
+ /* Update clock type in output mode */
|
||||
if (freq)
|
||||
- clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC;
|
||||
+ zl3073x_out_clock_type_set(&out,
|
||||
+ ZL_OUTPUT_MODE_CLOCK_TYPE_ESYNC);
|
||||
else
|
||||
- clock_type = ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL;
|
||||
-
|
||||
- /* Update clock type in output mode */
|
||||
- out.mode &= ~ZL_OUTPUT_MODE_CLOCK_TYPE;
|
||||
- out.mode |= FIELD_PREP(ZL_OUTPUT_MODE_CLOCK_TYPE, clock_type);
|
||||
+ zl3073x_out_clock_type_set(&out,
|
||||
+ ZL_OUTPUT_MODE_CLOCK_TYPE_NORMAL);
|
||||
|
||||
/* If esync is being disabled just write mailbox and finish */
|
||||
if (!freq)
|
||||
diff --git a/drivers/dpll/zl3073x/out.h b/drivers/dpll/zl3073x/out.h
|
||||
index edf40432bba5..660889c57bff 100644
|
||||
--- a/drivers/dpll/zl3073x/out.h
|
||||
+++ b/drivers/dpll/zl3073x/out.h
|
||||
@@ -42,6 +42,28 @@ const struct zl3073x_out *zl3073x_out_state_get(struct zl3073x_dev *zldev,
|
||||
int zl3073x_out_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
const struct zl3073x_out *out);
|
||||
|
||||
+/**
|
||||
+ * zl3073x_out_clock_type_get - get output clock type
|
||||
+ * @out: pointer to out state
|
||||
+ *
|
||||
+ * Return: clock type of given output (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
|
||||
+ */
|
||||
+static inline u8 zl3073x_out_clock_type_get(const struct zl3073x_out *out)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_OUTPUT_MODE_CLOCK_TYPE, out->mode);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_out_clock_type_set - set output clock type
|
||||
+ * @out: pointer to out state
|
||||
+ * @type: clock type (ZL_OUTPUT_MODE_CLOCK_TYPE_*)
|
||||
+ */
|
||||
+static inline void
|
||||
+zl3073x_out_clock_type_set(struct zl3073x_out *out, u8 type)
|
||||
+{
|
||||
+ FIELD_MODIFY(ZL_OUTPUT_MODE_CLOCK_TYPE, &out->mode, type);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_out_signal_format_get - get output signal format
|
||||
* @out: pointer to out state
|
||||
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
|
||||
index be16be20dbc7..55e80e4f0873 100644
|
||||
--- a/drivers/dpll/zl3073x/ref.h
|
||||
+++ b/drivers/dpll/zl3073x/ref.h
|
||||
@@ -120,6 +120,52 @@ zl3073x_ref_freq_set(struct zl3073x_ref *ref, u32 freq)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_ref_sync_mode_get - get sync control mode
|
||||
+ * @ref: pointer to ref state
|
||||
+ *
|
||||
+ * Return: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
|
||||
+ */
|
||||
+static inline u8
|
||||
+zl3073x_ref_sync_mode_get(const struct zl3073x_ref *ref)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_REF_SYNC_CTRL_MODE, ref->sync_ctrl);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_ref_sync_mode_set - set sync control mode
|
||||
+ * @ref: pointer to ref state
|
||||
+ * @mode: sync control mode (ZL_REF_SYNC_CTRL_MODE_*)
|
||||
+ */
|
||||
+static inline void
|
||||
+zl3073x_ref_sync_mode_set(struct zl3073x_ref *ref, u8 mode)
|
||||
+{
|
||||
+ FIELD_MODIFY(ZL_REF_SYNC_CTRL_MODE, &ref->sync_ctrl, mode);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_ref_sync_pair_get - get sync pair reference index
|
||||
+ * @ref: pointer to ref state
|
||||
+ *
|
||||
+ * Return: paired reference index
|
||||
+ */
|
||||
+static inline u8
|
||||
+zl3073x_ref_sync_pair_get(const struct zl3073x_ref *ref)
|
||||
+{
|
||||
+ return FIELD_GET(ZL_REF_SYNC_CTRL_PAIR, ref->sync_ctrl);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_ref_sync_pair_set - set sync pair reference index
|
||||
+ * @ref: pointer to ref state
|
||||
+ * @pair: paired reference index
|
||||
+ */
|
||||
+static inline void
|
||||
+zl3073x_ref_sync_pair_set(struct zl3073x_ref *ref, u8 pair)
|
||||
+{
|
||||
+ FIELD_MODIFY(ZL_REF_SYNC_CTRL_PAIR, &ref->sync_ctrl, pair);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_ref_is_diff - check if the given input reference is differential
|
||||
* @ref: pointer to ref state
|
||||
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
|
||||
index 5ae50cb761a9..d425dc67250f 100644
|
||||
--- a/drivers/dpll/zl3073x/regs.h
|
||||
+++ b/drivers/dpll/zl3073x/regs.h
|
||||
@@ -213,7 +213,9 @@
|
||||
#define ZL_REG_REF_SYNC_CTRL ZL_REG(10, 0x2e, 1)
|
||||
#define ZL_REF_SYNC_CTRL_MODE GENMASK(2, 0)
|
||||
#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF 0
|
||||
+#define ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR 1
|
||||
#define ZL_REF_SYNC_CTRL_MODE_50_50_ESYNC_25_75 2
|
||||
+#define ZL_REF_SYNC_CTRL_PAIR GENMASK(7, 4)
|
||||
|
||||
#define ZL_REG_REF_ESYNC_DIV ZL_REG(10, 0x30, 4)
|
||||
#define ZL_REF_ESYNC_DIV_1HZ 0
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
325
1264-dpll-zl3073x-add-ref-sync-pair-support.patch
Normal file
325
1264-dpll-zl3073x-add-ref-sync-pair-support.patch
Normal file
@ -0,0 +1,325 @@
|
||||
From ef38c6a6c3f4e6f931e21f54c88741a1a0c84f27 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 29 Apr 2026 14:55:45 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: add ref-sync pair support
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157828
|
||||
|
||||
commit 14f269ae699869ddaca7c29c9c6c52288e3bfb73
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed Apr 8 12:27:16 2026 +0200
|
||||
|
||||
dpll: zl3073x: add ref-sync pair support
|
||||
|
||||
Add support for ref-sync pair registration using the 'ref-sync-sources'
|
||||
phandle property from device tree. A ref-sync pair consists of a clock
|
||||
reference and a low-frequency sync signal where the DPLL locks to the
|
||||
clock reference but phase-aligns to the sync reference.
|
||||
|
||||
The implementation:
|
||||
- Stores fwnode handle in zl3073x_dpll_pin during pin registration
|
||||
- Adds ref_sync_get/set callbacks to read and write the sync control
|
||||
mode and pair registers
|
||||
- Validates ref-sync frequency constraints: sync signal must be 8 kHz
|
||||
or less, clock reference must be 1 kHz or more and higher than sync
|
||||
- Excludes sync source from automatic reference selection by setting
|
||||
its priority to NONE on connect; on disconnect the priority is left
|
||||
as NONE and the user must explicitly make the pin selectable again
|
||||
- Iterates ref-sync-sources phandles to register declared pairings
|
||||
via dpll_pin_ref_sync_pair_add()
|
||||
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Reviewed-by: Prathosh Satish <Prathosh.Satish@microchip.com>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260408102716.443099-6-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 14f269ae699869ddaca7c29c9c6c52288e3bfb73)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index a151be700e6e..55195dcd21d6 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/platform_device.h>
|
||||
+#include <linux/property.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sprintf.h>
|
||||
|
||||
@@ -30,6 +31,7 @@
|
||||
* @dpll: DPLL the pin is registered to
|
||||
* @dpll_pin: pointer to registered dpll_pin
|
||||
* @tracker: tracking object for the acquired reference
|
||||
+ * @fwnode: firmware node handle
|
||||
* @label: package label
|
||||
* @dir: pin direction
|
||||
* @id: pin id
|
||||
@@ -46,6 +48,7 @@ struct zl3073x_dpll_pin {
|
||||
struct zl3073x_dpll *dpll;
|
||||
struct dpll_pin *dpll_pin;
|
||||
dpll_tracker tracker;
|
||||
+ struct fwnode_handle *fwnode;
|
||||
char label[8];
|
||||
enum dpll_pin_direction dir;
|
||||
u8 id;
|
||||
@@ -186,6 +189,109 @@ zl3073x_dpll_input_pin_esync_set(const struct dpll_pin *dpll_pin,
|
||||
return zl3073x_ref_state_set(zldev, ref_id, &ref);
|
||||
}
|
||||
|
||||
+static int
|
||||
+zl3073x_dpll_input_pin_ref_sync_get(const struct dpll_pin *dpll_pin,
|
||||
+ void *pin_priv,
|
||||
+ const struct dpll_pin *ref_sync_pin,
|
||||
+ void *ref_sync_pin_priv,
|
||||
+ enum dpll_pin_state *state,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ struct zl3073x_dpll_pin *sync_pin = ref_sync_pin_priv;
|
||||
+ struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
+ struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
+ struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ const struct zl3073x_ref *ref;
|
||||
+ u8 ref_id, mode, pair;
|
||||
+
|
||||
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ ref = zl3073x_ref_state_get(zldev, ref_id);
|
||||
+ mode = zl3073x_ref_sync_mode_get(ref);
|
||||
+ pair = zl3073x_ref_sync_pair_get(ref);
|
||||
+
|
||||
+ if (mode == ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR &&
|
||||
+ pair == zl3073x_input_pin_ref_get(sync_pin->id))
|
||||
+ *state = DPLL_PIN_STATE_CONNECTED;
|
||||
+ else
|
||||
+ *state = DPLL_PIN_STATE_DISCONNECTED;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin,
|
||||
+ void *pin_priv,
|
||||
+ const struct dpll_pin *ref_sync_pin,
|
||||
+ void *ref_sync_pin_priv,
|
||||
+ const enum dpll_pin_state state,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ struct zl3073x_dpll_pin *sync_pin = ref_sync_pin_priv;
|
||||
+ struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
+ struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
+ struct zl3073x_dev *zldev = zldpll->dev;
|
||||
+ u8 mode, ref_id, sync_ref_id;
|
||||
+ struct zl3073x_chan chan;
|
||||
+ struct zl3073x_ref ref;
|
||||
+ int rc;
|
||||
+
|
||||
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ sync_ref_id = zl3073x_input_pin_ref_get(sync_pin->id);
|
||||
+ ref = *zl3073x_ref_state_get(zldev, ref_id);
|
||||
+
|
||||
+ if (state == DPLL_PIN_STATE_CONNECTED) {
|
||||
+ const struct zl3073x_ref *sync_ref;
|
||||
+ u32 ref_freq, sync_freq;
|
||||
+
|
||||
+ sync_ref = zl3073x_ref_state_get(zldev, sync_ref_id);
|
||||
+ ref_freq = zl3073x_ref_freq_get(&ref);
|
||||
+ sync_freq = zl3073x_ref_freq_get(sync_ref);
|
||||
+
|
||||
+ /* Sync signal must be 8 kHz or less and clock reference
|
||||
+ * must be 1 kHz or more and higher than the sync signal.
|
||||
+ */
|
||||
+ if (sync_freq > 8000) {
|
||||
+ NL_SET_ERR_MSG(extack,
|
||||
+ "sync frequency must be 8 kHz or less");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (ref_freq < 1000) {
|
||||
+ NL_SET_ERR_MSG(extack,
|
||||
+ "clock frequency must be 1 kHz or more");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (ref_freq <= sync_freq) {
|
||||
+ NL_SET_ERR_MSG(extack,
|
||||
+ "clock frequency must be higher than sync frequency");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ zl3073x_ref_sync_pair_set(&ref, sync_ref_id);
|
||||
+ mode = ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR;
|
||||
+ } else {
|
||||
+ mode = ZL_REF_SYNC_CTRL_MODE_REFSYNC_PAIR_OFF;
|
||||
+ }
|
||||
+
|
||||
+ zl3073x_ref_sync_mode_set(&ref, mode);
|
||||
+
|
||||
+ rc = zl3073x_ref_state_set(zldev, ref_id, &ref);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ /* Exclude sync source from automatic reference selection by setting
|
||||
+ * its priority to NONE. On disconnect the priority is left as NONE
|
||||
+ * and the user must explicitly make the pin selectable again.
|
||||
+ */
|
||||
+ if (state == DPLL_PIN_STATE_CONNECTED) {
|
||||
+ chan = *zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
+ zl3073x_chan_ref_prio_set(&chan, sync_ref_id,
|
||||
+ ZL_DPLL_REF_PRIO_NONE);
|
||||
+ return zl3073x_chan_state_set(zldev, zldpll->id, &chan);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
@@ -1147,6 +1253,8 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
.phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
|
||||
.prio_get = zl3073x_dpll_input_pin_prio_get,
|
||||
.prio_set = zl3073x_dpll_input_pin_prio_set,
|
||||
+ .ref_sync_get = zl3073x_dpll_input_pin_ref_sync_get,
|
||||
+ .ref_sync_set = zl3073x_dpll_input_pin_ref_sync_set,
|
||||
.state_on_dpll_get = zl3073x_dpll_input_pin_state_on_dpll_get,
|
||||
.state_on_dpll_set = zl3073x_dpll_input_pin_state_on_dpll_set,
|
||||
};
|
||||
@@ -1239,8 +1347,11 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
|
||||
if (IS_ERR(props))
|
||||
return PTR_ERR(props);
|
||||
|
||||
- /* Save package label, esync capability and phase adjust granularity */
|
||||
+ /* Save package label, fwnode, esync capability and phase adjust
|
||||
+ * granularity.
|
||||
+ */
|
||||
strscpy(pin->label, props->package_label);
|
||||
+ pin->fwnode = fwnode_handle_get(props->fwnode);
|
||||
pin->esync_control = props->esync_control;
|
||||
pin->phase_gran = props->dpll_props.phase_gran;
|
||||
|
||||
@@ -1285,6 +1396,8 @@ zl3073x_dpll_pin_register(struct zl3073x_dpll_pin *pin, u32 index)
|
||||
dpll_pin_put(pin->dpll_pin, &pin->tracker);
|
||||
pin->dpll_pin = NULL;
|
||||
err_pin_get:
|
||||
+ fwnode_handle_put(pin->fwnode);
|
||||
+ pin->fwnode = NULL;
|
||||
zl3073x_pin_props_put(props);
|
||||
|
||||
return rc;
|
||||
@@ -1314,6 +1427,9 @@ zl3073x_dpll_pin_unregister(struct zl3073x_dpll_pin *pin)
|
||||
|
||||
dpll_pin_put(pin->dpll_pin, &pin->tracker);
|
||||
pin->dpll_pin = NULL;
|
||||
+
|
||||
+ fwnode_handle_put(pin->fwnode);
|
||||
+ pin->fwnode = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1827,6 +1943,88 @@ zl3073x_dpll_free(struct zl3073x_dpll *zldpll)
|
||||
kfree(zldpll);
|
||||
}
|
||||
|
||||
+/**
|
||||
+ * zl3073x_dpll_ref_sync_pair_register - register ref_sync pairs for a pin
|
||||
+ * @pin: pointer to zl3073x_dpll_pin structure
|
||||
+ *
|
||||
+ * Iterates 'ref-sync-sources' phandles in the pin's firmware node and
|
||||
+ * registers each declared pairing.
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on error
|
||||
+ */
|
||||
+static int
|
||||
+zl3073x_dpll_ref_sync_pair_register(struct zl3073x_dpll_pin *pin)
|
||||
+{
|
||||
+ struct zl3073x_dev *zldev = pin->dpll->dev;
|
||||
+ struct fwnode_handle *fwnode;
|
||||
+ struct dpll_pin *sync_pin;
|
||||
+ dpll_tracker tracker;
|
||||
+ int n, rc;
|
||||
+
|
||||
+ for (n = 0; ; n++) {
|
||||
+ /* Get n'th ref-sync source */
|
||||
+ fwnode = fwnode_find_reference(pin->fwnode, "ref-sync-sources",
|
||||
+ n);
|
||||
+ if (IS_ERR(fwnode)) {
|
||||
+ rc = PTR_ERR(fwnode);
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Find associated dpll pin */
|
||||
+ sync_pin = fwnode_dpll_pin_find(fwnode, &tracker);
|
||||
+ fwnode_handle_put(fwnode);
|
||||
+ if (!sync_pin) {
|
||||
+ dev_warn(zldev->dev, "%s: ref-sync source %d not found",
|
||||
+ pin->label, n);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ /* Register new ref-sync pair */
|
||||
+ rc = dpll_pin_ref_sync_pair_add(pin->dpll_pin, sync_pin);
|
||||
+ dpll_pin_put(sync_pin, &tracker);
|
||||
+
|
||||
+ /* -EBUSY means pairing already exists from another DPLL's
|
||||
+ * registration.
|
||||
+ */
|
||||
+ if (rc && rc != -EBUSY) {
|
||||
+ dev_err(zldev->dev,
|
||||
+ "%s: failed to add ref-sync source %d: %pe",
|
||||
+ pin->label, n, ERR_PTR(rc));
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return rc != -ENOENT ? rc : 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * zl3073x_dpll_ref_sync_pairs_register - register ref_sync pairs for a DPLL
|
||||
+ * @zldpll: pointer to zl3073x_dpll structure
|
||||
+ *
|
||||
+ * Iterates all registered input pins of the given DPLL and establishes
|
||||
+ * ref_sync pairings declared by 'ref-sync-sources' phandles in the
|
||||
+ * device tree.
|
||||
+ *
|
||||
+ * Return: 0 on success, <0 on error
|
||||
+ */
|
||||
+static int
|
||||
+zl3073x_dpll_ref_sync_pairs_register(struct zl3073x_dpll *zldpll)
|
||||
+{
|
||||
+ struct zl3073x_dpll_pin *pin;
|
||||
+ int rc;
|
||||
+
|
||||
+ list_for_each_entry(pin, &zldpll->pins, list) {
|
||||
+ if (!zl3073x_dpll_is_input_pin(pin) || !pin->fwnode)
|
||||
+ continue;
|
||||
+
|
||||
+ rc = zl3073x_dpll_ref_sync_pair_register(pin);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_dpll_register - register DPLL device and all its pins
|
||||
* @zldpll: pointer to zl3073x_dpll structure
|
||||
@@ -1850,6 +2048,13 @@ zl3073x_dpll_register(struct zl3073x_dpll *zldpll)
|
||||
return rc;
|
||||
}
|
||||
|
||||
+ rc = zl3073x_dpll_ref_sync_pairs_register(zldpll);
|
||||
+ if (rc) {
|
||||
+ zl3073x_dpll_pins_unregister(zldpll);
|
||||
+ zl3073x_dpll_device_unregister(zldpll);
|
||||
+ return rc;
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,233 @@
|
||||
From 55e372bffed1f937fe3cbac3a2afe058eb4657da Mon Sep 17 00:00:00 2001
|
||||
From: Paulo Alcantara <paalcant@redhat.com>
|
||||
Date: Sun, 10 May 2026 18:08:11 -0300
|
||||
Subject: [PATCH] smb: client: validate the whole DACL before rewriting it in
|
||||
cifsacl
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-172829
|
||||
CVE: CVE-2026-31709
|
||||
|
||||
commit 0a8cf165566ba55a39fd0f4de172119dd646d39a
|
||||
Author: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Date: Sun Apr 19 20:11:31 2026 -0400
|
||||
|
||||
smb: client: validate the whole DACL before rewriting it in cifsacl
|
||||
|
||||
build_sec_desc() and id_mode_to_cifs_acl() derive a DACL pointer from a
|
||||
server-supplied dacloffset and then use the incoming ACL to rebuild the
|
||||
chmod/chown security descriptor.
|
||||
|
||||
The original fix only checked that the struct smb_acl header fits before
|
||||
reading dacl_ptr->size or dacl_ptr->num_aces. That avoids the immediate
|
||||
header-field OOB read, but the rewrite helpers still walk ACEs based on
|
||||
pdacl->num_aces with no structural validation of the incoming DACL body.
|
||||
|
||||
A malicious server can return a truncated DACL that still contains a
|
||||
header, claims one or more ACEs, and then drive
|
||||
replace_sids_and_copy_aces() or set_chmod_dacl() past the validated
|
||||
extent while they compare or copy attacker-controlled ACEs.
|
||||
|
||||
Factor the DACL structural checks into validate_dacl(), extend them to
|
||||
validate each ACE against the DACL bounds, and use the shared validator
|
||||
before the chmod/chown rebuild paths. parse_dacl() reuses the same
|
||||
validator so the read-side parser and write-side rewrite paths agree on
|
||||
what constitutes a well-formed incoming DACL.
|
||||
|
||||
Fixes: bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.")
|
||||
Cc: stable@vger.kernel.org
|
||||
Assisted-by: Claude:claude-opus-4-6
|
||||
Assisted-by: Codex:gpt-5-4
|
||||
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
|
||||
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
|
||||
|
||||
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
|
||||
index 7e6e473bd4a0..74c320ad5c3a 100644
|
||||
--- a/fs/smb/client/cifsacl.c
|
||||
+++ b/fs/smb/client/cifsacl.c
|
||||
@@ -759,6 +759,77 @@ static void dump_ace(struct smb_ace *pace, char *end_of_acl)
|
||||
}
|
||||
#endif
|
||||
|
||||
+static int validate_dacl(struct smb_acl *pdacl, char *end_of_acl)
|
||||
+{
|
||||
+ int i, ace_hdr_size, ace_size, min_ace_size;
|
||||
+ u16 dacl_size, num_aces;
|
||||
+ char *acl_base, *end_of_dacl;
|
||||
+ struct smb_ace *pace;
|
||||
+
|
||||
+ if (!pdacl)
|
||||
+ return 0;
|
||||
+
|
||||
+ if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl)) {
|
||||
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ dacl_size = le16_to_cpu(pdacl->size);
|
||||
+ if (dacl_size < sizeof(struct smb_acl) ||
|
||||
+ end_of_acl < (char *)pdacl + dacl_size) {
|
||||
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ num_aces = le16_to_cpu(pdacl->num_aces);
|
||||
+ if (!num_aces)
|
||||
+ return 0;
|
||||
+
|
||||
+ ace_hdr_size = offsetof(struct smb_ace, sid) +
|
||||
+ offsetof(struct smb_sid, sub_auth);
|
||||
+ min_ace_size = ace_hdr_size + sizeof(__le32);
|
||||
+ if (num_aces > (dacl_size - sizeof(struct smb_acl)) / min_ace_size) {
|
||||
+ cifs_dbg(VFS, "ACL too small to parse DACL\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ end_of_dacl = (char *)pdacl + dacl_size;
|
||||
+ acl_base = (char *)pdacl;
|
||||
+ ace_size = sizeof(struct smb_acl);
|
||||
+
|
||||
+ for (i = 0; i < num_aces; ++i) {
|
||||
+ if (end_of_dacl - acl_base < ace_size) {
|
||||
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ pace = (struct smb_ace *)(acl_base + ace_size);
|
||||
+ acl_base = (char *)pace;
|
||||
+
|
||||
+ if (end_of_dacl - acl_base < ace_hdr_size ||
|
||||
+ pace->sid.num_subauth == 0 ||
|
||||
+ pace->sid.num_subauth > SID_MAX_SUB_AUTHORITIES) {
|
||||
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ ace_size = ace_hdr_size + sizeof(__le32) * pace->sid.num_subauth;
|
||||
+ if (end_of_dacl - acl_base < ace_size ||
|
||||
+ le16_to_cpu(pace->size) < ace_size) {
|
||||
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ ace_size = le16_to_cpu(pace->size);
|
||||
+ if (end_of_dacl - acl_base < ace_size) {
|
||||
+ cifs_dbg(VFS, "ACL too small to parse ACE\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
struct smb_sid *pownersid, struct smb_sid *pgrpsid,
|
||||
struct cifs_fattr *fattr, bool mode_from_special_sid)
|
||||
@@ -766,7 +837,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
int i;
|
||||
u16 num_aces = 0;
|
||||
int acl_size;
|
||||
- char *acl_base;
|
||||
+ char *acl_base, *end_of_dacl;
|
||||
struct smb_ace **ppace;
|
||||
|
||||
/* BB need to add parm so we can store the SID BB */
|
||||
@@ -778,12 +849,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
return;
|
||||
}
|
||||
|
||||
- /* validate that we do not go past end of acl */
|
||||
- if (end_of_acl < (char *)pdacl + sizeof(struct smb_acl) ||
|
||||
- end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
|
||||
- cifs_dbg(VFS, "ACL too small to parse DACL\n");
|
||||
+ if (validate_dacl(pdacl, end_of_acl))
|
||||
return;
|
||||
- }
|
||||
|
||||
cifs_dbg(NOISY, "DACL revision %d size %d num aces %d\n",
|
||||
le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
|
||||
@@ -794,6 +861,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
user/group/other have no permissions */
|
||||
fattr->cf_mode &= ~(0777);
|
||||
|
||||
+ end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size);
|
||||
acl_base = (char *)pdacl;
|
||||
acl_size = sizeof(struct smb_acl);
|
||||
|
||||
@@ -801,36 +869,16 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
if (num_aces > 0) {
|
||||
umode_t denied_mode = 0;
|
||||
|
||||
- if (num_aces > (le16_to_cpu(pdacl->size) - sizeof(struct smb_acl)) /
|
||||
- (offsetof(struct smb_ace, sid) +
|
||||
- offsetof(struct smb_sid, sub_auth) + sizeof(__le16)))
|
||||
- return;
|
||||
-
|
||||
ppace = kmalloc_array(num_aces, sizeof(struct smb_ace *),
|
||||
GFP_KERNEL);
|
||||
if (!ppace)
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_aces; ++i) {
|
||||
- if (end_of_acl - acl_base < acl_size)
|
||||
- break;
|
||||
-
|
||||
ppace[i] = (struct smb_ace *) (acl_base + acl_size);
|
||||
- acl_base = (char *)ppace[i];
|
||||
- acl_size = offsetof(struct smb_ace, sid) +
|
||||
- offsetof(struct smb_sid, sub_auth);
|
||||
-
|
||||
- if (end_of_acl - acl_base < acl_size ||
|
||||
- ppace[i]->sid.num_subauth == 0 ||
|
||||
- ppace[i]->sid.num_subauth > SID_MAX_SUB_AUTHORITIES ||
|
||||
- (end_of_acl - acl_base <
|
||||
- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth) ||
|
||||
- (le16_to_cpu(ppace[i]->size) <
|
||||
- acl_size + sizeof(__le32) * ppace[i]->sid.num_subauth))
|
||||
- break;
|
||||
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
- dump_ace(ppace[i], end_of_acl);
|
||||
+ dump_ace(ppace[i], end_of_dacl);
|
||||
#endif
|
||||
if (mode_from_special_sid &&
|
||||
(compare_sids(&(ppace[i]->sid),
|
||||
@@ -872,6 +920,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
(void *)ppace[i],
|
||||
sizeof(struct smb_ace)); */
|
||||
|
||||
+ acl_base = (char *)ppace[i];
|
||||
acl_size = le16_to_cpu(ppace[i]->size);
|
||||
}
|
||||
|
||||
@@ -1295,10 +1344,9 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
if (dacloffset) {
|
||||
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
|
||||
- if (end_of_acl < (char *)dacl_ptr + le16_to_cpu(dacl_ptr->size)) {
|
||||
- cifs_dbg(VFS, "Server returned illegal ACL size\n");
|
||||
- return -EINVAL;
|
||||
- }
|
||||
+ rc = validate_dacl(dacl_ptr, end_of_acl);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
}
|
||||
|
||||
owner_sid_ptr = (struct smb_sid *)((char *)pntsd +
|
||||
@@ -1671,6 +1719,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
if (dacloffset) {
|
||||
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
|
||||
+ rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
|
||||
+ if (rc) {
|
||||
+ kfree(pntsd);
|
||||
+ cifs_put_tlink(tlink);
|
||||
+ return rc;
|
||||
+ }
|
||||
if (mode_from_sid)
|
||||
nsecdesclen +=
|
||||
le16_to_cpu(dacl_ptr->num_aces) * sizeof(struct smb_ace);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
From d8a6ff81310035796f62a3786a33b7ba906091b0 Mon Sep 17 00:00:00 2001
|
||||
From: Paulo Alcantara <paalcant@redhat.com>
|
||||
Date: Sun, 10 May 2026 18:08:12 -0300
|
||||
Subject: [PATCH] smb: client: require a full NFS mode SID before reading mode
|
||||
bits
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-172829
|
||||
|
||||
commit 2757ad3e4b6f9e0fed4c7739594e702abc5cab21
|
||||
Author: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Date: Mon Apr 20 09:50:58 2026 -0400
|
||||
|
||||
smb: client: require a full NFS mode SID before reading mode bits
|
||||
|
||||
parse_dacl() treats an ACE SID matching sid_unix_NFS_mode as an NFS
|
||||
mode SID and reads sid.sub_auth[2] to recover the mode bits.
|
||||
|
||||
That assumes the ACE carries three subauthorities, but compare_sids()
|
||||
only compares min(a, b) subauthorities. A malicious server can return
|
||||
an ACE with num_subauth = 2 and sub_auth[] = {88, 3}, which still
|
||||
matches sid_unix_NFS_mode and then drives the sub_auth[2] read four
|
||||
bytes past the end of the ACE.
|
||||
|
||||
Require num_subauth >= 3 before treating the ACE as an NFS mode SID.
|
||||
This keeps the fix local to the special-SID mode path without changing
|
||||
compare_sids() semantics for the rest of cifsacl.
|
||||
|
||||
Fixes: e2f8fbfb8d09 ("cifs: get mode bits from special sid on stat")
|
||||
Cc: stable@vger.kernel.org
|
||||
Assisted-by: Claude:claude-opus-4-6
|
||||
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
|
||||
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
|
||||
|
||||
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
|
||||
index 74c320ad5c3a..e6690f8e46a8 100644
|
||||
--- a/fs/smb/client/cifsacl.c
|
||||
+++ b/fs/smb/client/cifsacl.c
|
||||
@@ -881,6 +881,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
dump_ace(ppace[i], end_of_dacl);
|
||||
#endif
|
||||
if (mode_from_special_sid &&
|
||||
+ ppace[i]->sid.num_subauth >= 3 &&
|
||||
(compare_sids(&(ppace[i]->sid),
|
||||
&sid_unix_NFS_mode) == 0)) {
|
||||
/*
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
From 59a296878f29e33031dad8c186168e8e5b50e95a Mon Sep 17 00:00:00 2001
|
||||
From: Paulo Alcantara <paalcant@redhat.com>
|
||||
Date: Sun, 10 May 2026 18:08:12 -0300
|
||||
Subject: [PATCH] smb: client: scope end_of_dacl to CIFS_DEBUG2 use in
|
||||
parse_dacl
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-172829
|
||||
|
||||
commit a55a60886e612bedb0e9a402ba0dca544c4c6a51
|
||||
Author: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Date: Tue Apr 21 19:40:22 2026 -0400
|
||||
|
||||
smb: client: scope end_of_dacl to CIFS_DEBUG2 use in parse_dacl
|
||||
|
||||
After validate_dacl() was factored out in commit 149822e5541c, the
|
||||
local end_of_dacl in parse_dacl() is only read by the dump_ace()
|
||||
call under #ifdef CONFIG_CIFS_DEBUG2. With CIFS_DEBUG2 off the
|
||||
variable is assigned but never used, which gcc -W=1 flags as
|
||||
-Wunused-but-set-variable.
|
||||
|
||||
Remove the local and compute the end-of-dacl pointer inline at the
|
||||
single call site inside the existing CIFS_DEBUG2 guard. No
|
||||
functional change: when CIFS_DEBUG2 is enabled the argument value
|
||||
is identical to what the removed local carried; when CIFS_DEBUG2
|
||||
is disabled the code was already dead.
|
||||
|
||||
Reported-by: kernel test robot <lkp@intel.com>
|
||||
Closes: https://lore.kernel.org/oe-kbuild-all/202604220046.tGkRxVtS-lkp@intel.com/
|
||||
Fixes: 149822e5541c ("smb: client: validate the whole DACL before rewriting it in cifsacl")
|
||||
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Assisted-by: Claude:claude-opus-4-7
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
|
||||
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
|
||||
|
||||
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
|
||||
index e6690f8e46a8..717a40fa84aa 100644
|
||||
--- a/fs/smb/client/cifsacl.c
|
||||
+++ b/fs/smb/client/cifsacl.c
|
||||
@@ -837,7 +837,7 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
int i;
|
||||
u16 num_aces = 0;
|
||||
int acl_size;
|
||||
- char *acl_base, *end_of_dacl;
|
||||
+ char *acl_base;
|
||||
struct smb_ace **ppace;
|
||||
|
||||
/* BB need to add parm so we can store the SID BB */
|
||||
@@ -861,7 +861,6 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
user/group/other have no permissions */
|
||||
fattr->cf_mode &= ~(0777);
|
||||
|
||||
- end_of_dacl = (char *)pdacl + le16_to_cpu(pdacl->size);
|
||||
acl_base = (char *)pdacl;
|
||||
acl_size = sizeof(struct smb_acl);
|
||||
|
||||
@@ -878,7 +877,8 @@ static void parse_dacl(struct smb_acl *pdacl, char *end_of_acl,
|
||||
ppace[i] = (struct smb_ace *) (acl_base + acl_size);
|
||||
|
||||
#ifdef CONFIG_CIFS_DEBUG2
|
||||
- dump_ace(ppace[i], end_of_dacl);
|
||||
+ dump_ace(ppace[i],
|
||||
+ (char *)pdacl + le16_to_cpu(pdacl->size));
|
||||
#endif
|
||||
if (mode_from_special_sid &&
|
||||
ppace[i]->sid.num_subauth >= 3 &&
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,57 @@
|
||||
From f1579c843ecda9218102033597e2bafe1ce8ec74 Mon Sep 17 00:00:00 2001
|
||||
From: Paulo Alcantara <paalcant@redhat.com>
|
||||
Date: Sun, 10 May 2026 18:08:12 -0300
|
||||
Subject: [PATCH] smb: client: use kzalloc to zero-initialize security
|
||||
descriptor buffer
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-172829
|
||||
|
||||
commit 5e489c6c47a2ac15edbaca153b9348e42c1eacab
|
||||
Author: Bjoern Doebel <doebel@amazon.de>
|
||||
Date: Thu Apr 30 08:57:17 2026 +0000
|
||||
|
||||
smb: client: use kzalloc to zero-initialize security descriptor buffer
|
||||
|
||||
Commit 62e7dd0a39c2d ("smb: common: change the data type of num_aces
|
||||
to le16") split struct smb_acl's __le32 num_aces field into __le16
|
||||
num_aces and __le16 reserved. The reserved field corresponds to Sbz2
|
||||
in the MS-DTYP ACL wire format, which must be zero [1].
|
||||
|
||||
When building an ACL descriptor in build_sec_desc(), we are using a
|
||||
kmalloc()'ed descriptor buffer and writing the fields explicitly using
|
||||
le16() writes now. This never writes to the 2 byte reserved field,
|
||||
leaving it as uninitialized heap data.
|
||||
|
||||
When the reserved field happens to contain non-zero slab garbage,
|
||||
Samba rejects the security descriptor with "ndr_pull_security_descriptor
|
||||
failed: Range Error", causing chmod to fail with EINVAL.
|
||||
|
||||
Change kmalloc() to kzalloc() to ensure the entire buffer is
|
||||
zero-initialized.
|
||||
|
||||
Fixes: 62e7dd0a39c2d ("smb: common: change the data type of num_aces to le16")
|
||||
Cc: stable@vger.kernel.org
|
||||
|
||||
Signed-off-by: Bjoern Doebel <doebel@amazon.de>
|
||||
Assisted-by: Kiro:claude-opus-4.6
|
||||
[1] https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/20233ed8-a6c6-4097-aafa-dd545ed24428
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
|
||||
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
|
||||
|
||||
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
|
||||
index 717a40fa84aa..03bad2885e96 100644
|
||||
--- a/fs/smb/client/cifsacl.c
|
||||
+++ b/fs/smb/client/cifsacl.c
|
||||
@@ -1741,7 +1741,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
* descriptor parameters, and security descriptor itself
|
||||
*/
|
||||
nsecdesclen = max_t(u32, nsecdesclen, DEFAULT_SEC_DESC_LEN);
|
||||
- pnntsd = kmalloc(nsecdesclen, GFP_KERNEL);
|
||||
+ pnntsd = kzalloc(nsecdesclen, GFP_KERNEL);
|
||||
if (!pnntsd) {
|
||||
kfree(pntsd);
|
||||
cifs_put_tlink(tlink);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
From eff4c8b51d7c82746c2efa0e4ba30576d1082169 Mon Sep 17 00:00:00 2001
|
||||
From: Paulo Alcantara <paalcant@redhat.com>
|
||||
Date: Sun, 10 May 2026 18:08:12 -0300
|
||||
Subject: [PATCH] smb: client: validate dacloffset before building DACL
|
||||
pointers
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-172829
|
||||
|
||||
commit f98b48151cc502ada59d9778f0112d21f2586ca3
|
||||
Author: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Date: Mon Apr 20 10:47:47 2026 -0400
|
||||
|
||||
smb: client: validate dacloffset before building DACL pointers
|
||||
|
||||
parse_sec_desc(), build_sec_desc(), and the chown path in
|
||||
id_mode_to_cifs_acl() all add the server-supplied dacloffset to pntsd
|
||||
before proving a DACL header fits inside the returned security
|
||||
descriptor.
|
||||
|
||||
On 32-bit builds a malicious server can return dacloffset near
|
||||
U32_MAX, wrap the derived DACL pointer below end_of_acl, and then slip
|
||||
past the later pointer-based bounds checks. build_sec_desc() and
|
||||
id_mode_to_cifs_acl() can then dereference DACL fields from the wrapped
|
||||
pointer in the chmod/chown rewrite paths.
|
||||
|
||||
Validate dacloffset numerically before building any DACL pointer and
|
||||
reuse the same helper at the three DACL entry points.
|
||||
|
||||
Fixes: bc3e9dd9d104 ("cifs: Change SIDs in ACEs while transferring file ownership.")
|
||||
Cc: stable@vger.kernel.org
|
||||
Assisted-by: Claude:claude-opus-4-6
|
||||
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
|
||||
Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
|
||||
|
||||
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
|
||||
index 03bad2885e96..7ed526d23d14 100644
|
||||
--- a/fs/smb/client/cifsacl.c
|
||||
+++ b/fs/smb/client/cifsacl.c
|
||||
@@ -1266,6 +1266,17 @@ static int parse_sid(struct smb_sid *psid, char *end_of_acl)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static bool dacl_offset_valid(unsigned int acl_len, __u32 dacloffset)
|
||||
+{
|
||||
+ if (acl_len < sizeof(struct smb_acl))
|
||||
+ return false;
|
||||
+
|
||||
+ if (dacloffset < sizeof(struct smb_ntsd))
|
||||
+ return false;
|
||||
+
|
||||
+ return dacloffset <= acl_len - sizeof(struct smb_acl);
|
||||
+}
|
||||
+
|
||||
|
||||
/* Convert CIFS ACL to POSIX form */
|
||||
static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
|
||||
@@ -1286,7 +1297,6 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
|
||||
group_sid_ptr = (struct smb_sid *)((char *)pntsd +
|
||||
le32_to_cpu(pntsd->gsidoffset));
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
- dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
|
||||
cifs_dbg(NOISY, "revision %d type 0x%x ooffset 0x%x goffset 0x%x sacloffset 0x%x dacloffset 0x%x\n",
|
||||
pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
|
||||
le32_to_cpu(pntsd->gsidoffset),
|
||||
@@ -1317,11 +1327,18 @@ static int parse_sec_desc(struct cifs_sb_info *cifs_sb,
|
||||
return rc;
|
||||
}
|
||||
|
||||
- if (dacloffset)
|
||||
+ if (dacloffset) {
|
||||
+ if (!dacl_offset_valid(acl_len, dacloffset)) {
|
||||
+ cifs_dbg(VFS, "Server returned illegal DACL offset\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
|
||||
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
|
||||
group_sid_ptr, fattr, get_mode_from_special_sid);
|
||||
- else
|
||||
+ } else {
|
||||
cifs_dbg(FYI, "no ACL\n"); /* BB grant all or default perms? */
|
||||
+ }
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -1344,6 +1361,11 @@ static int build_sec_desc(struct smb_ntsd *pntsd, struct smb_ntsd *pnntsd,
|
||||
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
if (dacloffset) {
|
||||
+ if (!dacl_offset_valid(secdesclen, dacloffset)) {
|
||||
+ cifs_dbg(VFS, "Server returned illegal DACL offset\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
|
||||
rc = validate_dacl(dacl_ptr, end_of_acl);
|
||||
if (rc)
|
||||
@@ -1719,6 +1741,12 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
nsecdesclen = sizeof(struct smb_ntsd) + (sizeof(struct smb_sid) * 2);
|
||||
dacloffset = le32_to_cpu(pntsd->dacloffset);
|
||||
if (dacloffset) {
|
||||
+ if (!dacl_offset_valid(secdesclen, dacloffset)) {
|
||||
+ cifs_dbg(VFS, "Server returned illegal DACL offset\n");
|
||||
+ rc = -EINVAL;
|
||||
+ goto id_mode_to_cifs_acl_exit;
|
||||
+ }
|
||||
+
|
||||
dacl_ptr = (struct smb_acl *)((char *)pntsd + dacloffset);
|
||||
rc = validate_dacl(dacl_ptr, (char *)pntsd + secdesclen);
|
||||
if (rc) {
|
||||
@@ -1761,6 +1789,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 *pnmode,
|
||||
rc = ops->set_acl(pnntsd, nsecdesclen, inode, path, aclflag);
|
||||
cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);
|
||||
}
|
||||
+id_mode_to_cifs_acl_exit:
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
kfree(pnntsd);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
294
1270-dpll-add-pin-operational-state.patch
Normal file
294
1270-dpll-add-pin-operational-state.patch
Normal file
@ -0,0 +1,294 @@
|
||||
From 05ce58c7fda9c607fb135c297cbef74db41489c2 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 13 May 2026 11:47:07 +0200
|
||||
Subject: [PATCH] dpll: add pin operational state
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-173188
|
||||
|
||||
commit 781c8893a5da8522ae4ded93e5ef3adbe9559300
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Tue Apr 28 17:49:06 2026 +0200
|
||||
|
||||
dpll: add pin operational state
|
||||
|
||||
Add pin-operstate enum and operstate_on_dpll_get callback to report
|
||||
the actual hardware status of a pin with respect to its parent DPLL
|
||||
device. Unlike pin-state (which reflects administrative intent set
|
||||
by the user), operstate reflects what the hardware is actually doing.
|
||||
|
||||
Defined operational states:
|
||||
- active: pin is qualified and actively used by the DPLL
|
||||
- standby: pin is qualified but not actively used by the DPLL
|
||||
- no-signal: pin does not have a valid signal
|
||||
- qual-failed: pin signal failed qualification
|
||||
|
||||
The operstate is reported inside the pin-parent-device nested
|
||||
attribute alongside the existing state and phase-offset attributes.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
|
||||
Reviewed-by: Vadim Fedorenko <vadim.fedorenko@linux.dev>
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Link: https://patch.msgid.link/20260428154907.2820654-2-ivecera@redhat.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
(cherry picked from commit 781c8893a5da8522ae4ded93e5ef3adbe9559300)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
|
||||
index 93c191b2d089..37eaef785e30 100644
|
||||
--- a/Documentation/driver-api/dpll.rst
|
||||
+++ b/Documentation/driver-api/dpll.rst
|
||||
@@ -65,35 +65,43 @@ request, where user provides attributes that result in single pin match.
|
||||
Pin selection
|
||||
=============
|
||||
|
||||
-In general, selected pin (the one which signal is driving the dpll
|
||||
-device) can be obtained from ``DPLL_A_PIN_STATE`` attribute, and only
|
||||
-one pin shall be in ``DPLL_PIN_STATE_CONNECTED`` state for any dpll
|
||||
-device.
|
||||
+Pin state (``DPLL_A_PIN_STATE``) reflects the administrative intent set
|
||||
+by the user. Pin operational state (``DPLL_A_PIN_OPERSTATE``) reflects
|
||||
+what the hardware is actually doing with the pin.
|
||||
|
||||
Pin selection can be done either manually or automatically, depending
|
||||
on hardware capabilities and active dpll device work mode
|
||||
(``DPLL_A_MODE`` attribute). The consequence is that there are
|
||||
-differences for each mode in terms of available pin states, as well as
|
||||
-for the states the user can request for a dpll device.
|
||||
+differences for each mode in terms of available pin states the user can
|
||||
+request for a dpll device.
|
||||
|
||||
-In manual mode (``DPLL_MODE_MANUAL``) the user can request or receive
|
||||
-one of following pin states:
|
||||
+In manual mode (``DPLL_MODE_MANUAL``) the user can request one of
|
||||
+following pin states:
|
||||
|
||||
-- ``DPLL_PIN_STATE_CONNECTED`` - the pin is used to drive dpll device
|
||||
-- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not used to drive dpll
|
||||
+- ``DPLL_PIN_STATE_CONNECTED`` - the pin is selected to drive dpll
|
||||
device
|
||||
+- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin is not selected to drive
|
||||
+ dpll device
|
||||
|
||||
-In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request or
|
||||
-receive one of following pin states:
|
||||
+In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can request one of
|
||||
+following pin states:
|
||||
|
||||
- ``DPLL_PIN_STATE_SELECTABLE`` - the pin shall be considered as valid
|
||||
input for automatic selection algorithm
|
||||
- ``DPLL_PIN_STATE_DISCONNECTED`` - the pin shall be not considered as
|
||||
a valid input for automatic selection algorithm
|
||||
|
||||
-In automatic mode (``DPLL_MODE_AUTOMATIC``) the user can only receive
|
||||
-pin state ``DPLL_PIN_STATE_CONNECTED`` once automatic selection
|
||||
-algorithm locks a dpll device with one of the inputs.
|
||||
+The actual hardware status of a pin is reported via the operational
|
||||
+state (``DPLL_A_PIN_OPERSTATE``) attribute nested under the parent
|
||||
+device:
|
||||
+
|
||||
+- ``DPLL_PIN_OPERSTATE_ACTIVE`` - pin is qualified and actively used
|
||||
+ by the DPLL
|
||||
+- ``DPLL_PIN_OPERSTATE_STANDBY`` - pin is qualified but not actively
|
||||
+ used by the DPLL
|
||||
+- ``DPLL_PIN_OPERSTATE_NO_SIGNAL`` - pin does not have a valid signal
|
||||
+- ``DPLL_PIN_OPERSTATE_QUAL_FAILED`` - pin signal failed qualification
|
||||
+ checks
|
||||
|
||||
Shared pins
|
||||
===========
|
||||
diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
|
||||
index cb5da356bb64..a47d7da54da5 100644
|
||||
--- a/Documentation/netlink/specs/dpll.yaml
|
||||
+++ b/Documentation/netlink/specs/dpll.yaml
|
||||
@@ -212,6 +212,27 @@ definitions:
|
||||
name: selectable
|
||||
doc: pin enabled for automatic input selection
|
||||
render-max: true
|
||||
+ -
|
||||
+ type: enum
|
||||
+ name: pin-operstate
|
||||
+ doc: |
|
||||
+ defines possible operational states of a pin with respect to its
|
||||
+ parent DPLL device, valid values for DPLL_A_PIN_OPERSTATE attribute
|
||||
+ entries:
|
||||
+ -
|
||||
+ name: active
|
||||
+ doc: pin is qualified and actively used by the DPLL
|
||||
+ value: 1
|
||||
+ -
|
||||
+ name: standby
|
||||
+ doc: pin is qualified but not actively used by the DPLL
|
||||
+ -
|
||||
+ name: no-signal
|
||||
+ doc: pin does not have a valid signal
|
||||
+ -
|
||||
+ name: qual-failed
|
||||
+ doc: pin signal failed qualification (e.g. frequency or phase monitor)
|
||||
+ render-max: true
|
||||
-
|
||||
type: flags
|
||||
name: pin-capabilities
|
||||
@@ -488,6 +509,14 @@ attribute-sets:
|
||||
Value of (DPLL_A_PIN_MEASURED_FREQUENCY %
|
||||
DPLL_PIN_MEASURED_FREQUENCY_DIVIDER) is a fractional part
|
||||
of a measured frequency value.
|
||||
+ -
|
||||
+ name: operstate
|
||||
+ type: u32
|
||||
+ enum: pin-operstate
|
||||
+ doc: |
|
||||
+ Operational state of the pin with respect to its parent DPLL
|
||||
+ device. Unlike state (which reflects the administrative intent),
|
||||
+ operstate reflects the actual hardware status.
|
||||
|
||||
-
|
||||
name: pin-parent-device
|
||||
@@ -501,6 +530,8 @@ attribute-sets:
|
||||
name: prio
|
||||
-
|
||||
name: state
|
||||
+ -
|
||||
+ name: operstate
|
||||
-
|
||||
name: phase-offset
|
||||
-
|
||||
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
|
||||
index af7ce62ec55c..05cf946b4be5 100644
|
||||
--- a/drivers/dpll/dpll_netlink.c
|
||||
+++ b/drivers/dpll/dpll_netlink.c
|
||||
@@ -324,6 +324,30 @@ dpll_msg_add_pin_on_dpll_state(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int
|
||||
+dpll_msg_add_pin_operstate(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
+ struct dpll_pin_ref *ref,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
|
||||
+ struct dpll_device *dpll = ref->dpll;
|
||||
+ enum dpll_pin_operstate operstate;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (!ops->operstate_on_dpll_get)
|
||||
+ return 0;
|
||||
+ ret = ops->operstate_on_dpll_get(pin,
|
||||
+ dpll_pin_on_dpll_priv(dpll, pin),
|
||||
+ dpll, dpll_priv(dpll),
|
||||
+ &operstate, extack);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ if (nla_put_u32(msg, DPLL_A_PIN_OPERSTATE, operstate))
|
||||
+ return -EMSGSIZE;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int
|
||||
dpll_msg_add_pin_direction(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref,
|
||||
@@ -650,6 +674,9 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
if (ret)
|
||||
goto nest_cancel;
|
||||
ret = dpll_msg_add_pin_on_dpll_state(msg, pin, ref, extack);
|
||||
+ if (ret)
|
||||
+ goto nest_cancel;
|
||||
+ ret = dpll_msg_add_pin_operstate(msg, pin, ref, extack);
|
||||
if (ret)
|
||||
goto nest_cancel;
|
||||
ret = dpll_msg_add_pin_prio(msg, pin, ref, extack);
|
||||
diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c
|
||||
index da92aa8f9bc7..64398841cdcb 100644
|
||||
--- a/drivers/dpll/dpll_nl.c
|
||||
+++ b/drivers/dpll/dpll_nl.c
|
||||
@@ -11,11 +11,12 @@
|
||||
#include <uapi/linux/dpll.h>
|
||||
|
||||
/* Common nested types */
|
||||
-const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1] = {
|
||||
+const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_OPERSTATE + 1] = {
|
||||
[DPLL_A_PIN_PARENT_ID] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_DIRECTION] = NLA_POLICY_RANGE(NLA_U32, 1, 2),
|
||||
[DPLL_A_PIN_PRIO] = { .type = NLA_U32, },
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
|
||||
+ [DPLL_A_PIN_OPERSTATE] = NLA_POLICY_RANGE(NLA_U32, 1, 4),
|
||||
[DPLL_A_PIN_PHASE_OFFSET] = { .type = NLA_S64, },
|
||||
};
|
||||
|
||||
diff --git a/drivers/dpll/dpll_nl.h b/drivers/dpll/dpll_nl.h
|
||||
index 3da10cfe9a6e..8cf3b73c2013 100644
|
||||
--- a/drivers/dpll/dpll_nl.h
|
||||
+++ b/drivers/dpll/dpll_nl.h
|
||||
@@ -12,7 +12,7 @@
|
||||
#include <uapi/linux/dpll.h>
|
||||
|
||||
/* Common nested types */
|
||||
-extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_PHASE_OFFSET + 1];
|
||||
+extern const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_OPERSTATE + 1];
|
||||
extern const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1];
|
||||
extern const struct nla_policy dpll_reference_sync_nl_policy[DPLL_A_PIN_STATE + 1];
|
||||
|
||||
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
|
||||
index 441fe25ba4f4..90249937008c 100644
|
||||
--- a/include/linux/dpll.h
|
||||
+++ b/include/linux/dpll.h
|
||||
@@ -98,6 +98,12 @@ struct dpll_pin_ops {
|
||||
const struct dpll_device *dpll,
|
||||
void *dpll_priv, enum dpll_pin_state *state,
|
||||
struct netlink_ext_ack *extack);
|
||||
+ int (*operstate_on_dpll_get)(const struct dpll_pin *pin,
|
||||
+ void *pin_priv,
|
||||
+ const struct dpll_device *dpll,
|
||||
+ void *dpll_priv,
|
||||
+ enum dpll_pin_operstate *operstate,
|
||||
+ struct netlink_ext_ack *extack);
|
||||
int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_pin *parent_pin,
|
||||
void *parent_pin_priv,
|
||||
diff --git a/include/uapi/linux/dpll.h b/include/uapi/linux/dpll.h
|
||||
index 93614ccd8a84..aa45b79473de 100644
|
||||
--- a/include/uapi/linux/dpll.h
|
||||
+++ b/include/uapi/linux/dpll.h
|
||||
@@ -177,6 +177,28 @@ enum dpll_pin_state {
|
||||
DPLL_PIN_STATE_MAX = (__DPLL_PIN_STATE_MAX - 1)
|
||||
};
|
||||
|
||||
+/**
|
||||
+ * enum dpll_pin_operstate - defines possible operational states of a pin with
|
||||
+ * respect to its parent DPLL device, valid values for DPLL_A_PIN_OPERSTATE
|
||||
+ * attribute
|
||||
+ * @DPLL_PIN_OPERSTATE_ACTIVE: pin is qualified and actively used by the DPLL
|
||||
+ * @DPLL_PIN_OPERSTATE_STANDBY: pin is qualified but not actively used by the
|
||||
+ * DPLL
|
||||
+ * @DPLL_PIN_OPERSTATE_NO_SIGNAL: pin does not have a valid signal
|
||||
+ * @DPLL_PIN_OPERSTATE_QUAL_FAILED: pin signal failed qualification (e.g.
|
||||
+ * frequency or phase monitor)
|
||||
+ */
|
||||
+enum dpll_pin_operstate {
|
||||
+ DPLL_PIN_OPERSTATE_ACTIVE = 1,
|
||||
+ DPLL_PIN_OPERSTATE_STANDBY,
|
||||
+ DPLL_PIN_OPERSTATE_NO_SIGNAL,
|
||||
+ DPLL_PIN_OPERSTATE_QUAL_FAILED,
|
||||
+
|
||||
+ /* private: */
|
||||
+ __DPLL_PIN_OPERSTATE_MAX,
|
||||
+ DPLL_PIN_OPERSTATE_MAX = (__DPLL_PIN_OPERSTATE_MAX - 1)
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* enum dpll_pin_capabilities - defines possible capabilities of a pin, valid
|
||||
* flags on DPLL_A_PIN_CAPABILITIES attribute
|
||||
@@ -256,6 +278,7 @@ enum dpll_a_pin {
|
||||
DPLL_A_PIN_PHASE_ADJUST_GRAN,
|
||||
DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
|
||||
DPLL_A_PIN_MEASURED_FREQUENCY,
|
||||
+ DPLL_A_PIN_OPERSTATE,
|
||||
|
||||
__DPLL_A_PIN_MAX,
|
||||
DPLL_A_PIN_MAX = (__DPLL_A_PIN_MAX - 1)
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,254 @@
|
||||
From 80d47d653c6ca35a7dbfc6c8abc9486445c1e068 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 13 May 2026 11:48:51 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: implement pin operational state reporting
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-173188
|
||||
|
||||
commit c53f8f8dce776e032b1a11fb4d9b6e12bb63d958
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Tue Apr 28 17:49:07 2026 +0200
|
||||
|
||||
dpll: zl3073x: implement pin operational state reporting
|
||||
|
||||
Implement operstate_on_dpll_get callback for input pins to report
|
||||
the actual hardware status:
|
||||
|
||||
- active: pin is the currently locked reference
|
||||
- standby: signal is valid but pin is not actively used
|
||||
- no-signal: reference monitor reports Loss of Signal (LOS)
|
||||
- qual-failed: reference monitor reports a qualification failure
|
||||
(SCM, CFM, GST, PFM, eSync or Split-XO)
|
||||
|
||||
Separate administrative state (state_on_dpll_get) from operational
|
||||
state: admin state now reports purely the user-requested intent
|
||||
(connected in reflock mode, selectable in auto mode).
|
||||
|
||||
Switch periodic monitoring to track operstate changes instead of
|
||||
the mixed admin/oper state that was previously reported.
|
||||
|
||||
Add ref_mon_status bit definitions to regs.h.
|
||||
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Link: https://patch.msgid.link/20260428154907.2820654-3-ivecera@redhat.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
|
||||
(cherry picked from commit c53f8f8dce776e032b1a11fb4d9b6e12bb63d958)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 55195dcd21d6..2c8bd211e31e 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -38,7 +38,7 @@
|
||||
* @prio: pin priority <0, 14>
|
||||
* @esync_control: embedded sync is controllable
|
||||
* @phase_gran: phase adjustment granularity
|
||||
- * @pin_state: last saved pin state
|
||||
+ * @operstate: last saved operational state
|
||||
* @phase_offset: last saved pin phase offset
|
||||
* @freq_offset: last saved fractional frequency offset
|
||||
* @measured_freq: last saved measured frequency
|
||||
@@ -55,7 +55,7 @@ struct zl3073x_dpll_pin {
|
||||
u8 prio;
|
||||
bool esync_control;
|
||||
s32 phase_gran;
|
||||
- enum dpll_pin_state pin_state;
|
||||
+ enum dpll_pin_operstate operstate;
|
||||
s64 phase_offset;
|
||||
s64 freq_offset;
|
||||
u32 measured_freq;
|
||||
@@ -500,46 +500,41 @@ zl3073x_dpll_input_pin_phase_adjust_set(const struct dpll_pin *dpll_pin,
|
||||
}
|
||||
|
||||
/**
|
||||
- * zl3073x_dpll_ref_state_get - get status for given input pin
|
||||
+ * zl3073x_dpll_ref_operstate_get - get operational state for input pin
|
||||
* @pin: pointer to pin
|
||||
- * @state: place to store status
|
||||
+ * @operstate: place to store operational state
|
||||
*
|
||||
- * Checks current status for the given input pin and stores the value
|
||||
- * to @state.
|
||||
+ * Returns the actual hardware state of the pin: whether it is actively
|
||||
+ * used by the DPLL, has no signal, failed qualification, or is simply
|
||||
+ * not in use.
|
||||
*
|
||||
* Return: 0 on success, <0 on error
|
||||
*/
|
||||
static int
|
||||
-zl3073x_dpll_ref_state_get(struct zl3073x_dpll_pin *pin,
|
||||
- enum dpll_pin_state *state)
|
||||
+zl3073x_dpll_ref_operstate_get(struct zl3073x_dpll_pin *pin,
|
||||
+ enum dpll_pin_operstate *operstate)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- const struct zl3073x_chan *chan;
|
||||
- u8 ref;
|
||||
-
|
||||
- chan = zl3073x_chan_state_get(zldev, zldpll->id);
|
||||
- ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ const struct zl3073x_ref *ref;
|
||||
+ u8 ref_id;
|
||||
|
||||
- /* Check if the pin reference is connected */
|
||||
- if (ref == zl3073x_dpll_connected_ref_get(zldpll)) {
|
||||
- *state = DPLL_PIN_STATE_CONNECTED;
|
||||
- return 0;
|
||||
- }
|
||||
+ ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
|
||||
- /* If the DPLL is running in automatic mode and the reference is
|
||||
- * selectable and its monitor does not report any error then report
|
||||
- * pin as selectable.
|
||||
- */
|
||||
- if (zl3073x_chan_mode_get(chan) == ZL_DPLL_MODE_REFSEL_MODE_AUTO &&
|
||||
- zl3073x_dev_ref_is_status_ok(zldev, ref) &&
|
||||
- zl3073x_chan_ref_is_selectable(chan, ref)) {
|
||||
- *state = DPLL_PIN_STATE_SELECTABLE;
|
||||
+ /* Check if this pin is the currently locked reference */
|
||||
+ if (ref_id == zl3073x_dpll_connected_ref_get(zldpll)) {
|
||||
+ *operstate = DPLL_PIN_OPERSTATE_ACTIVE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
- /* Otherwise report the pin as disconnected */
|
||||
- *state = DPLL_PIN_STATE_DISCONNECTED;
|
||||
+ /* Check reference monitor status */
|
||||
+ ref = zl3073x_ref_state_get(zldev, ref_id);
|
||||
+ if (ref->mon_status & ZL_REF_MON_STATUS_LOS)
|
||||
+ *operstate = DPLL_PIN_OPERSTATE_NO_SIGNAL;
|
||||
+ else if (!zl3073x_ref_is_status_ok(ref))
|
||||
+ *operstate = DPLL_PIN_OPERSTATE_QUAL_FAILED;
|
||||
+ else
|
||||
+ *operstate = DPLL_PIN_OPERSTATE_STANDBY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -551,10 +546,48 @@ zl3073x_dpll_input_pin_state_on_dpll_get(const struct dpll_pin *dpll_pin,
|
||||
void *dpll_priv,
|
||||
enum dpll_pin_state *state,
|
||||
struct netlink_ext_ack *extack)
|
||||
+{
|
||||
+ struct zl3073x_dpll *zldpll = dpll_priv;
|
||||
+ struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
+ u8 mode, ref;
|
||||
+
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ ref = zl3073x_input_pin_ref_get(pin->id);
|
||||
+ mode = zl3073x_chan_mode_get(chan);
|
||||
+
|
||||
+ switch (mode) {
|
||||
+ case ZL_DPLL_MODE_REFSEL_MODE_REFLOCK:
|
||||
+ if (ref == zl3073x_chan_ref_get(chan))
|
||||
+ *state = DPLL_PIN_STATE_CONNECTED;
|
||||
+ else
|
||||
+ *state = DPLL_PIN_STATE_DISCONNECTED;
|
||||
+ break;
|
||||
+ case ZL_DPLL_MODE_REFSEL_MODE_AUTO:
|
||||
+ if (zl3073x_chan_ref_is_selectable(chan, ref))
|
||||
+ *state = DPLL_PIN_STATE_SELECTABLE;
|
||||
+ else
|
||||
+ *state = DPLL_PIN_STATE_DISCONNECTED;
|
||||
+ break;
|
||||
+ default:
|
||||
+ *state = DPLL_PIN_STATE_DISCONNECTED;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+zl3073x_dpll_input_pin_operstate_on_dpll_get(const struct dpll_pin *dpll_pin,
|
||||
+ void *pin_priv,
|
||||
+ const struct dpll_device *dpll,
|
||||
+ void *dpll_priv,
|
||||
+ enum dpll_pin_operstate *operstate,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
|
||||
- return zl3073x_dpll_ref_state_get(pin, state);
|
||||
+ return zl3073x_dpll_ref_operstate_get(pin, operstate);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -1248,6 +1281,7 @@ static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
.frequency_get = zl3073x_dpll_input_pin_frequency_get,
|
||||
.frequency_set = zl3073x_dpll_input_pin_frequency_set,
|
||||
.measured_freq_get = zl3073x_dpll_input_pin_measured_freq_get,
|
||||
+ .operstate_on_dpll_get = zl3073x_dpll_input_pin_operstate_on_dpll_get,
|
||||
.phase_offset_get = zl3073x_dpll_input_pin_phase_offset_get,
|
||||
.phase_adjust_get = zl3073x_dpll_input_pin_phase_adjust_get,
|
||||
.phase_adjust_set = zl3073x_dpll_input_pin_phase_adjust_set,
|
||||
@@ -1663,7 +1697,7 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin)
|
||||
* 2) For other pins use appropriate ref_phase register if the phase
|
||||
* monitor feature is enabled.
|
||||
*/
|
||||
- if (pin->pin_state == DPLL_PIN_STATE_CONNECTED)
|
||||
+ if (pin->operstate == DPLL_PIN_OPERSTATE_ACTIVE)
|
||||
reg = ZL_REG_DPLL_PHASE_ERR_DATA(zldpll->id);
|
||||
else if (zldpll->phase_monitor)
|
||||
reg = ZL_REG_REF_PHASE(ref_id);
|
||||
@@ -1828,7 +1862,7 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
||||
}
|
||||
|
||||
list_for_each_entry(pin, &zldpll->pins, list) {
|
||||
- enum dpll_pin_state state;
|
||||
+ enum dpll_pin_operstate operstate;
|
||||
bool pin_changed = false;
|
||||
|
||||
/* Output pins change checks are not necessary because output
|
||||
@@ -1837,18 +1871,18 @@ zl3073x_dpll_changes_check(struct zl3073x_dpll *zldpll)
|
||||
if (!zl3073x_dpll_is_input_pin(pin))
|
||||
continue;
|
||||
|
||||
- rc = zl3073x_dpll_ref_state_get(pin, &state);
|
||||
+ rc = zl3073x_dpll_ref_operstate_get(pin, &operstate);
|
||||
if (rc) {
|
||||
dev_err(dev,
|
||||
- "Failed to get %s on DPLL%u state: %pe\n",
|
||||
+ "Failed to get %s on DPLL%u oper state: %pe\n",
|
||||
pin->label, zldpll->id, ERR_PTR(rc));
|
||||
return;
|
||||
}
|
||||
|
||||
- if (state != pin->pin_state) {
|
||||
- dev_dbg(dev, "%s state changed: %u->%u\n", pin->label,
|
||||
- pin->pin_state, state);
|
||||
- pin->pin_state = state;
|
||||
+ if (operstate != pin->operstate) {
|
||||
+ dev_dbg(dev, "%s oper state changed: %u->%u\n",
|
||||
+ pin->label, pin->operstate, operstate);
|
||||
+ pin->operstate = operstate;
|
||||
pin_changed = true;
|
||||
}
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
|
||||
index d425dc67250f..8015808bdf54 100644
|
||||
--- a/drivers/dpll/zl3073x/regs.h
|
||||
+++ b/drivers/dpll/zl3073x/regs.h
|
||||
@@ -98,7 +98,14 @@
|
||||
|
||||
#define ZL_REG_REF_MON_STATUS(_idx) \
|
||||
ZL_REG_IDX(_idx, 2, 0x02, 1, ZL3073X_NUM_REFS, 1)
|
||||
-#define ZL_REF_MON_STATUS_OK 0 /* all bits zeroed */
|
||||
+#define ZL_REF_MON_STATUS_OK 0
|
||||
+#define ZL_REF_MON_STATUS_LOS BIT(0)
|
||||
+#define ZL_REF_MON_STATUS_SCM BIT(1)
|
||||
+#define ZL_REF_MON_STATUS_CFM BIT(2)
|
||||
+#define ZL_REF_MON_STATUS_GST BIT(3)
|
||||
+#define ZL_REF_MON_STATUS_PFM BIT(4)
|
||||
+#define ZL_REF_MON_STATUS_ESYNC BIT(6)
|
||||
+#define ZL_REF_MON_STATUS_SPLIT_XO BIT(7)
|
||||
|
||||
#define ZL_REG_DPLL_MON_STATUS(_idx) \
|
||||
ZL_REG_IDX(_idx, 2, 0x10, 1, ZL3073X_MAX_CHANNELS, 1)
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,325 @@
|
||||
From 071d49d03d957fe6ab483b8447328dc278cbf6f1 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 13 May 2026 11:50:25 +0200
|
||||
Subject: [PATCH] dpll: add fractional frequency offset to pin-parent-device
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-173193
|
||||
|
||||
commit 9c11fcb2e9a54d0f1467380831e2e4bb68f7498d
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Mon May 11 17:58:15 2026 +0200
|
||||
|
||||
dpll: add fractional frequency offset to pin-parent-device
|
||||
|
||||
Add both fractional-frequency-offset (PPM) and
|
||||
fractional-frequency-offset-ppt (PPT) attributes to the
|
||||
pin-parent-device nested attribute set, alongside the existing
|
||||
top-level pin attributes. Both carry the same measurement at
|
||||
different precisions.
|
||||
|
||||
Introduce enum dpll_ffo_type and struct dpll_ffo_param to
|
||||
distinguish FFO contexts: DPLL_FFO_PORT_RXTX_RATE for the RX vs
|
||||
TX symbol rate offset reported at the top level, and
|
||||
DPLL_FFO_PIN_DEVICE for the pin vs parent DPLL offset reported
|
||||
in the pin-parent-device nest.
|
||||
|
||||
Add a supported_ffo bitmask to struct dpll_pin_ops so drivers
|
||||
declare which FFO types they support. The core only calls ffo_get
|
||||
for types the driver has opted into, eliminating the need for
|
||||
per-driver NULL pointer guards. Validate at pin registration time
|
||||
that supported_ffo is not set without an ffo_get callback.
|
||||
|
||||
Update mlx5 (DPLL_FFO_PORT_RXTX_RATE) and zl3073x
|
||||
(DPLL_FFO_PORT_RXTX_RATE) drivers to use the new API.
|
||||
|
||||
Add documentation for both FFO types to dpll.rst.
|
||||
|
||||
Changes v3 -> v4:
|
||||
- Replace dpll=NULL overloading with enum dpll_ffo_type and
|
||||
struct dpll_ffo_param (Jakub Kicinski)
|
||||
- Add supported_ffo opt-in bitmask in dpll_pin_ops for fail-close
|
||||
driver validation (Jakub Kicinski)
|
||||
- Add WARN_ON in dpll_pin_register for supported_ffo without
|
||||
ffo_get callback
|
||||
|
||||
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260511155816.99936-2-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 9c11fcb2e9a54d0f1467380831e2e4bb68f7498d)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/Documentation/driver-api/dpll.rst b/Documentation/driver-api/dpll.rst
|
||||
index 37eaef785e30..bae14766d4f7 100644
|
||||
--- a/Documentation/driver-api/dpll.rst
|
||||
+++ b/Documentation/driver-api/dpll.rst
|
||||
@@ -258,6 +258,26 @@ in the ``DPLL_A_PIN_PHASE_OFFSET`` attribute.
|
||||
``DPLL_A_PHASE_OFFSET_MONITOR`` attr state of a feature
|
||||
=============================== ========================
|
||||
|
||||
+Fractional frequency offset
|
||||
+===========================
|
||||
+
|
||||
+The fractional frequency offset (FFO) is reported through two attributes
|
||||
+that carry the same measurement at different precisions:
|
||||
+
|
||||
+- ``DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET`` in PPM (parts per million)
|
||||
+- ``DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT`` in PPT (parts per trillion)
|
||||
+
|
||||
+Both attributes appear at the top level of a pin and inside each
|
||||
+``pin-parent-device`` nest. Two FFO types are defined:
|
||||
+
|
||||
+- ``DPLL_FFO_PORT_RXTX_RATE`` - RX vs TX symbol rate offset (top-level)
|
||||
+- ``DPLL_FFO_PIN_DEVICE`` - pin vs parent DPLL offset (per-parent)
|
||||
+
|
||||
+The driver declares which types it supports via the ``supported_ffo``
|
||||
+bitmask in ``struct dpll_pin_ops``. The core only calls the ``ffo_get``
|
||||
+callback for types the driver has opted into. The requested type is
|
||||
+passed to the driver in the ``struct dpll_ffo_param``.
|
||||
+
|
||||
Frequency monitor
|
||||
=================
|
||||
|
||||
diff --git a/Documentation/netlink/specs/dpll.yaml b/Documentation/netlink/specs/dpll.yaml
|
||||
index a47d7da54da5..4ba8adcf823c 100644
|
||||
--- a/Documentation/netlink/specs/dpll.yaml
|
||||
+++ b/Documentation/netlink/specs/dpll.yaml
|
||||
@@ -448,12 +448,14 @@ attribute-sets:
|
||||
name: fractional-frequency-offset
|
||||
type: sint
|
||||
doc: |
|
||||
- The FFO (Fractional Frequency Offset) between the RX and TX
|
||||
- symbol rate on the media associated with the pin:
|
||||
- (rx_frequency-tx_frequency)/rx_frequency
|
||||
+ The FFO (Fractional Frequency Offset) of the pin.
|
||||
+ At top level this represents the RX vs TX symbol rate
|
||||
+ offset on the media associated with the pin. Inside
|
||||
+ the pin-parent-device nest it represents the frequency
|
||||
+ offset between the pin and its parent DPLL device.
|
||||
Value is in PPM (parts per million).
|
||||
- This may be implemented for example for pin of type
|
||||
- PIN_TYPE_SYNCE_ETH_PORT.
|
||||
+ This is a lower-precision version of
|
||||
+ fractional-frequency-offset-ppt.
|
||||
-
|
||||
name: esync-frequency
|
||||
type: u64
|
||||
@@ -492,12 +494,14 @@ attribute-sets:
|
||||
name: fractional-frequency-offset-ppt
|
||||
type: sint
|
||||
doc: |
|
||||
- The FFO (Fractional Frequency Offset) of the pin with respect to
|
||||
- the nominal frequency.
|
||||
- Value = (frequency_measured - frequency_nominal) / frequency_nominal
|
||||
+ The FFO (Fractional Frequency Offset) of the pin.
|
||||
+ At top level this represents the RX vs TX symbol rate
|
||||
+ offset on the media associated with the pin. Inside
|
||||
+ the pin-parent-device nest it represents the frequency
|
||||
+ offset between the pin and its parent DPLL device.
|
||||
Value is in PPT (parts per trillion, 10^-12).
|
||||
- Note: This attribute provides higher resolution than the standard
|
||||
- fractional-frequency-offset (which is in PPM).
|
||||
+ This is a higher-precision version of
|
||||
+ fractional-frequency-offset.
|
||||
-
|
||||
name: measured-frequency
|
||||
type: u64
|
||||
@@ -534,6 +538,10 @@ attribute-sets:
|
||||
name: operstate
|
||||
-
|
||||
name: phase-offset
|
||||
+ -
|
||||
+ name: fractional-frequency-offset
|
||||
+ -
|
||||
+ name: fractional-frequency-offset-ppt
|
||||
-
|
||||
name: pin-parent-pin
|
||||
subset-of: pin
|
||||
diff --git a/drivers/dpll/dpll_core.c b/drivers/dpll/dpll_core.c
|
||||
index 84e1b637b4e5..8b260a31aa58 100644
|
||||
--- a/drivers/dpll/dpll_core.c
|
||||
+++ b/drivers/dpll/dpll_core.c
|
||||
@@ -883,7 +883,8 @@ dpll_pin_register(struct dpll_device *dpll, struct dpll_pin *pin,
|
||||
WARN_ON(!ops->direction_get) ||
|
||||
WARN_ON(ops->measured_freq_get &&
|
||||
(!dpll_device_ops(dpll)->freq_monitor_get ||
|
||||
- !dpll_device_ops(dpll)->freq_monitor_set)))
|
||||
+ !dpll_device_ops(dpll)->freq_monitor_set)) ||
|
||||
+ WARN_ON(ops->supported_ffo && !ops->ffo_get))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dpll_lock);
|
||||
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
|
||||
index 05cf946b4be5..00e8696cb267 100644
|
||||
--- a/drivers/dpll/dpll_netlink.c
|
||||
+++ b/drivers/dpll/dpll_netlink.c
|
||||
@@ -417,31 +417,28 @@ dpll_msg_add_phase_offset(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
|
||||
static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_pin_ref *ref,
|
||||
+ enum dpll_ffo_type type,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
const struct dpll_pin_ops *ops = dpll_pin_ops(ref);
|
||||
- struct dpll_device *dpll = ref->dpll;
|
||||
- s64 ffo;
|
||||
+ struct dpll_ffo_param ffo = { .type = type };
|
||||
int ret;
|
||||
|
||||
- if (!ops->ffo_get)
|
||||
+ if (!ops->ffo_get || !(ops->supported_ffo & BIT(type)))
|
||||
return 0;
|
||||
- ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(dpll, pin),
|
||||
- dpll, dpll_priv(dpll), &ffo, extack);
|
||||
+ ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(ref->dpll, pin),
|
||||
+ ref->dpll, dpll_priv(ref->dpll), &ffo, extack);
|
||||
if (ret) {
|
||||
if (ret == -ENODATA)
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
- /* Put the FFO value in PPM to preserve compatibility with older
|
||||
- * programs.
|
||||
- */
|
||||
- ret = nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET,
|
||||
- div_s64(ffo, 1000000));
|
||||
- if (ret)
|
||||
+ if (nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET,
|
||||
+ div_s64(ffo.ffo, 1000000)))
|
||||
return -EMSGSIZE;
|
||||
- return nla_put_sint(msg, DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
|
||||
- ffo);
|
||||
+ return nla_put_sint(msg,
|
||||
+ DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT,
|
||||
+ ffo.ffo);
|
||||
}
|
||||
|
||||
static int dpll_msg_add_measured_freq(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
@@ -686,6 +683,10 @@ dpll_msg_add_pin_dplls(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
if (ret)
|
||||
goto nest_cancel;
|
||||
ret = dpll_msg_add_phase_offset(msg, pin, ref, extack);
|
||||
+ if (ret)
|
||||
+ goto nest_cancel;
|
||||
+ ret = dpll_msg_add_ffo(msg, pin, ref,
|
||||
+ DPLL_FFO_PIN_DEVICE, extack);
|
||||
if (ret)
|
||||
goto nest_cancel;
|
||||
nla_nest_end(msg, attr);
|
||||
@@ -748,7 +749,8 @@ dpll_cmd_pin_get_one(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
ret = dpll_msg_add_pin_phase_adjust(msg, pin, ref, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
- ret = dpll_msg_add_ffo(msg, pin, ref, extack);
|
||||
+ ret = dpll_msg_add_ffo(msg, pin, ref,
|
||||
+ DPLL_FFO_PORT_RXTX_RATE, extack);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = dpll_msg_add_measured_freq(msg, pin, ref, extack);
|
||||
diff --git a/drivers/dpll/dpll_nl.c b/drivers/dpll/dpll_nl.c
|
||||
index 64398841cdcb..268999ddbcb6 100644
|
||||
--- a/drivers/dpll/dpll_nl.c
|
||||
+++ b/drivers/dpll/dpll_nl.c
|
||||
@@ -18,6 +18,8 @@ const struct nla_policy dpll_pin_parent_device_nl_policy[DPLL_A_PIN_OPERSTATE +
|
||||
[DPLL_A_PIN_STATE] = NLA_POLICY_RANGE(NLA_U32, 1, 3),
|
||||
[DPLL_A_PIN_OPERSTATE] = NLA_POLICY_RANGE(NLA_U32, 1, 4),
|
||||
[DPLL_A_PIN_PHASE_OFFSET] = { .type = NLA_S64, },
|
||||
+ [DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET] = { .type = NLA_SINT, },
|
||||
+ [DPLL_A_PIN_FRACTIONAL_FREQUENCY_OFFSET_PPT] = { .type = NLA_SINT, },
|
||||
};
|
||||
|
||||
const struct nla_policy dpll_pin_parent_pin_nl_policy[DPLL_A_PIN_STATE + 1] = {
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index 2c8bd211e31e..b159dff2985e 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -295,11 +295,12 @@ zl3073x_dpll_input_pin_ref_sync_set(const struct dpll_pin *dpll_pin,
|
||||
static int
|
||||
zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
- s64 *ffo, struct netlink_ext_ack *extack)
|
||||
+ struct dpll_ffo_param *ffo,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
|
||||
- *ffo = pin->freq_offset;
|
||||
+ ffo->ffo = pin->freq_offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1274,6 +1275,7 @@ zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
|
||||
}
|
||||
|
||||
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
+ .supported_ffo = BIT(DPLL_FFO_PORT_RXTX_RATE),
|
||||
.direction_get = zl3073x_dpll_pin_direction_get,
|
||||
.esync_get = zl3073x_dpll_input_pin_esync_get,
|
||||
.esync_set = zl3073x_dpll_input_pin_esync_set,
|
||||
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
|
||||
index 3981dd81d4c1..3756c0278122 100644
|
||||
--- a/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
|
||||
+++ b/drivers/net/ethernet/mellanox/mlx5/core/dpll.c
|
||||
@@ -300,7 +300,8 @@ static int mlx5_dpll_state_on_dpll_set(const struct dpll_pin *pin,
|
||||
|
||||
static int mlx5_dpll_ffo_get(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
- s64 *ffo, struct netlink_ext_ack *extack)
|
||||
+ struct dpll_ffo_param *ffo,
|
||||
+ struct netlink_ext_ack *extack)
|
||||
{
|
||||
struct mlx5_dpll_synce_status synce_status;
|
||||
struct mlx5_dpll *mdpll = pin_priv;
|
||||
@@ -309,10 +310,11 @@ static int mlx5_dpll_ffo_get(const struct dpll_pin *pin, void *pin_priv,
|
||||
err = mlx5_dpll_synce_status_get(mdpll->mdev, &synce_status);
|
||||
if (err)
|
||||
return err;
|
||||
- return mlx5_dpll_pin_ffo_get(&synce_status, ffo);
|
||||
+ return mlx5_dpll_pin_ffo_get(&synce_status, &ffo->ffo);
|
||||
}
|
||||
|
||||
static const struct dpll_pin_ops mlx5_dpll_pins_ops = {
|
||||
+ .supported_ffo = BIT(DPLL_FFO_PORT_RXTX_RATE),
|
||||
.direction_get = mlx5_dpll_pin_direction_get,
|
||||
.state_on_dpll_get = mlx5_dpll_state_on_dpll_get,
|
||||
.state_on_dpll_set = mlx5_dpll_state_on_dpll_set,
|
||||
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
|
||||
index 90249937008c..cd21b1f3dff1 100644
|
||||
--- a/include/linux/dpll.h
|
||||
+++ b/include/linux/dpll.h
|
||||
@@ -73,7 +73,20 @@ struct dpll_device_ops {
|
||||
RH_KABI_RESERVE(10)
|
||||
};
|
||||
|
||||
+enum dpll_ffo_type {
|
||||
+ DPLL_FFO_PORT_RXTX_RATE,
|
||||
+ DPLL_FFO_PIN_DEVICE,
|
||||
+
|
||||
+ __DPLL_FFO_TYPE_MAX,
|
||||
+};
|
||||
+
|
||||
+struct dpll_ffo_param {
|
||||
+ enum dpll_ffo_type type;
|
||||
+ s64 ffo;
|
||||
+};
|
||||
+
|
||||
struct dpll_pin_ops {
|
||||
+ unsigned long supported_ffo;
|
||||
int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
const u64 frequency,
|
||||
@@ -134,7 +147,8 @@ struct dpll_pin_ops {
|
||||
struct netlink_ext_ack *extack);
|
||||
int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
- s64 *ffo, struct netlink_ext_ack *extack);
|
||||
+ struct dpll_ffo_param *ffo,
|
||||
+ struct netlink_ext_ack *extack);
|
||||
int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll,
|
||||
void *dpll_priv, u64 *measured_freq,
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,362 @@
|
||||
From ba5cbd1742d290baabec9f9d37a9a8f3a10ab860 Mon Sep 17 00:00:00 2001
|
||||
From: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Wed, 13 May 2026 11:52:05 +0200
|
||||
Subject: [PATCH] dpll: zl3073x: report FFO as DPLL vs input reference offset
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-173193
|
||||
|
||||
commit 54e65df8cf18a741745645aed7ae91514d437b43
|
||||
Author: Ivan Vecera <ivecera@redhat.com>
|
||||
Date: Mon May 11 17:58:16 2026 +0200
|
||||
|
||||
dpll: zl3073x: report FFO as DPLL vs input reference offset
|
||||
|
||||
Replace the per-reference frequency offset measurement (which was
|
||||
redundant with measured-frequency) with a direct read of the DPLL's
|
||||
delta frequency offset vs its tracked input reference.
|
||||
|
||||
The new implementation uses the dpll_df_offset_x register with
|
||||
ref_ofst=1 via the dpll_df_read_x semaphore mechanism. This
|
||||
provides 2^-48 resolution (~3.5 fE) and reports the actual
|
||||
frequency difference between the DPLL and its active input.
|
||||
|
||||
Switch supported_ffo from DPLL_FFO_PORT_RXTX_RATE to
|
||||
DPLL_FFO_PIN_DEVICE so FFO is reported only in the per-parent
|
||||
context for the active input pin.
|
||||
|
||||
Use atomic64_t for freq_offset to prevent torn reads on 32-bit
|
||||
architectures between the periodic worker and netlink callbacks.
|
||||
|
||||
Rewrite ffo_check to compare the cached df_offset converted to PPT
|
||||
instead of using the old per-reference measurement. Remove the
|
||||
ref_ffo_update periodic measurement and the ref ffo field since
|
||||
they are no longer needed.
|
||||
|
||||
Changes v3 -> v4:
|
||||
- Switch to DPLL_FFO_PIN_DEVICE, remove dpll=NULL guard
|
||||
- Use atomic64_t for freq_offset (torn read on 32-bit)
|
||||
|
||||
Reviewed-by: Petr Oros <poros@redhat.com>
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
Link: https://patch.msgid.link/20260511155816.99936-3-ivecera@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
|
||||
(cherry picked from commit 54e65df8cf18a741745645aed7ae91514d437b43)
|
||||
Assisted-by: Patchpal
|
||||
Signed-off-by: Ivan Vecera <ivecera@redhat.com>
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/chan.c b/drivers/dpll/zl3073x/chan.c
|
||||
index 2f48ca239149..2fe3c3da84bb 100644
|
||||
--- a/drivers/dpll/zl3073x/chan.c
|
||||
+++ b/drivers/dpll/zl3073x/chan.c
|
||||
@@ -18,6 +18,7 @@
|
||||
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
|
||||
{
|
||||
struct zl3073x_chan *chan = &zldev->chan[index];
|
||||
+ u64 val;
|
||||
int rc;
|
||||
|
||||
rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_MON_STATUS(index),
|
||||
@@ -25,8 +26,34 @@ int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
- return zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),
|
||||
- &chan->refsel_status);
|
||||
+ rc = zl3073x_read_u8(zldev, ZL_REG_DPLL_REFSEL_STATUS(index),
|
||||
+ &chan->refsel_status);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ /* Read df_offset vs tracked reference */
|
||||
+ rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index),
|
||||
+ ZL_DPLL_DF_READ_SEM);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ rc = zl3073x_write_u8(zldev, ZL_REG_DPLL_DF_READ(index),
|
||||
+ ZL_DPLL_DF_READ_SEM | ZL_DPLL_DF_READ_REF_OFST);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ rc = zl3073x_poll_zero_u8(zldev, ZL_REG_DPLL_DF_READ(index),
|
||||
+ ZL_DPLL_DF_READ_SEM);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ rc = zl3073x_read_u48(zldev, ZL_REG_DPLL_DF_OFFSET(index), &val);
|
||||
+ if (rc)
|
||||
+ return rc;
|
||||
+
|
||||
+ chan->df_offset = sign_extend64(val, 47);
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
diff --git a/drivers/dpll/zl3073x/chan.h b/drivers/dpll/zl3073x/chan.h
|
||||
index 481da2133202..4353809c6912 100644
|
||||
--- a/drivers/dpll/zl3073x/chan.h
|
||||
+++ b/drivers/dpll/zl3073x/chan.h
|
||||
@@ -17,6 +17,7 @@ struct zl3073x_dev;
|
||||
* @ref_prio: reference priority registers (4 bits per ref, P/N packed)
|
||||
* @mon_status: monitor status register value
|
||||
* @refsel_status: reference selection status register value
|
||||
+ * @df_offset: frequency offset vs tracked reference in 2^-48 steps
|
||||
*/
|
||||
struct zl3073x_chan {
|
||||
struct_group(cfg,
|
||||
@@ -26,6 +27,7 @@ struct zl3073x_chan {
|
||||
struct_group(stat,
|
||||
u8 mon_status;
|
||||
u8 refsel_status;
|
||||
+ s64 df_offset;
|
||||
);
|
||||
};
|
||||
|
||||
@@ -37,6 +39,18 @@ int zl3073x_chan_state_set(struct zl3073x_dev *zldev, u8 index,
|
||||
|
||||
int zl3073x_chan_state_update(struct zl3073x_dev *zldev, u8 index);
|
||||
|
||||
+/**
|
||||
+ * zl3073x_chan_df_offset_get - get cached df_offset vs tracked reference
|
||||
+ * @chan: pointer to channel state
|
||||
+ *
|
||||
+ * Return: frequency offset in 2^-48 steps
|
||||
+ */
|
||||
+static inline s64
|
||||
+zl3073x_chan_df_offset_get(const struct zl3073x_chan *chan)
|
||||
+{
|
||||
+ return chan->df_offset;
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* zl3073x_chan_mode_get - get DPLL channel operating mode
|
||||
* @chan: pointer to channel state
|
||||
diff --git a/drivers/dpll/zl3073x/core.c b/drivers/dpll/zl3073x/core.c
|
||||
index 5f1e70f3e40a..b3345060490d 100644
|
||||
--- a/drivers/dpll/zl3073x/core.c
|
||||
+++ b/drivers/dpll/zl3073x/core.c
|
||||
@@ -704,44 +704,6 @@ zl3073x_ref_freq_meas_update(struct zl3073x_dev *zldev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-/**
|
||||
- * zl3073x_ref_ffo_update - update reference fractional frequency offsets
|
||||
- * @zldev: pointer to zl3073x_dev structure
|
||||
- *
|
||||
- * The function asks device to latch the latest measured fractional
|
||||
- * frequency offset values, reads and stores them into the ref state.
|
||||
- *
|
||||
- * Return: 0 on success, <0 on error
|
||||
- */
|
||||
-static int
|
||||
-zl3073x_ref_ffo_update(struct zl3073x_dev *zldev)
|
||||
-{
|
||||
- int i, rc;
|
||||
-
|
||||
- rc = zl3073x_ref_freq_meas_latch(zldev,
|
||||
- ZL_REF_FREQ_MEAS_CTRL_REF_FREQ_OFF);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Read DPLL-to-REFx frequency offset measurements */
|
||||
- for (i = 0; i < ZL3073X_NUM_REFS; i++) {
|
||||
- s32 value;
|
||||
-
|
||||
- /* Read value stored in units of 2^-32 signed */
|
||||
- rc = zl3073x_read_u32(zldev, ZL_REG_REF_FREQ(i), &value);
|
||||
- if (rc)
|
||||
- return rc;
|
||||
-
|
||||
- /* Convert to ppt
|
||||
- * ffo = (10^12 * value) / 2^32
|
||||
- * ffo = ( 5^12 * value) / 2^20
|
||||
- */
|
||||
- zldev->ref[i].ffo = mul_s64_u64_shr(value, 244140625, 20);
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static void
|
||||
zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
{
|
||||
@@ -776,13 +738,6 @@ zl3073x_dev_periodic_work(struct kthread_work *work)
|
||||
}
|
||||
}
|
||||
|
||||
- /* Update references' fractional frequency offsets */
|
||||
- rc = zl3073x_ref_ffo_update(zldev);
|
||||
- if (rc)
|
||||
- dev_warn(zldev->dev,
|
||||
- "Failed to update fractional frequency offsets: %pe\n",
|
||||
- ERR_PTR(rc));
|
||||
-
|
||||
list_for_each_entry(zldpll, &zldev->dplls, list)
|
||||
zl3073x_dpll_changes_check(zldpll);
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/dpll.c b/drivers/dpll/zl3073x/dpll.c
|
||||
index b159dff2985e..6b714ec3b3c9 100644
|
||||
--- a/drivers/dpll/zl3073x/dpll.c
|
||||
+++ b/drivers/dpll/zl3073x/dpll.c
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
+#include <linux/atomic.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bug.h>
|
||||
@@ -57,7 +58,7 @@ struct zl3073x_dpll_pin {
|
||||
s32 phase_gran;
|
||||
enum dpll_pin_operstate operstate;
|
||||
s64 phase_offset;
|
||||
- s64 freq_offset;
|
||||
+ atomic64_t freq_offset;
|
||||
u32 measured_freq;
|
||||
};
|
||||
|
||||
@@ -300,7 +301,10 @@ zl3073x_dpll_input_pin_ffo_get(const struct dpll_pin *dpll_pin, void *pin_priv,
|
||||
{
|
||||
struct zl3073x_dpll_pin *pin = pin_priv;
|
||||
|
||||
- ffo->ffo = pin->freq_offset;
|
||||
+ if (pin->operstate != DPLL_PIN_OPERSTATE_ACTIVE)
|
||||
+ return -ENODATA;
|
||||
+
|
||||
+ ffo->ffo = atomic64_read(&pin->freq_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1275,7 +1279,7 @@ zl3073x_dpll_freq_monitor_set(const struct dpll_device *dpll,
|
||||
}
|
||||
|
||||
static const struct dpll_pin_ops zl3073x_dpll_input_pin_ops = {
|
||||
- .supported_ffo = BIT(DPLL_FFO_PORT_RXTX_RATE),
|
||||
+ .supported_ffo = BIT(DPLL_FFO_PIN_DEVICE),
|
||||
.direction_get = zl3073x_dpll_pin_direction_get,
|
||||
.esync_get = zl3073x_dpll_input_pin_esync_get,
|
||||
.esync_set = zl3073x_dpll_input_pin_esync_set,
|
||||
@@ -1731,37 +1735,29 @@ zl3073x_dpll_pin_phase_offset_check(struct zl3073x_dpll_pin *pin)
|
||||
}
|
||||
|
||||
/**
|
||||
- * zl3073x_dpll_pin_ffo_check - check for pin fractional frequency offset change
|
||||
+ * zl3073x_dpll_pin_ffo_check - check for FFO change on active pin
|
||||
* @pin: pin to check
|
||||
*
|
||||
- * Check for the given pin's fractional frequency change.
|
||||
- *
|
||||
- * Return: true on fractional frequency offset change, false otherwise
|
||||
+ * Return: true on change, false otherwise
|
||||
*/
|
||||
static bool
|
||||
zl3073x_dpll_pin_ffo_check(struct zl3073x_dpll_pin *pin)
|
||||
{
|
||||
struct zl3073x_dpll *zldpll = pin->dpll;
|
||||
struct zl3073x_dev *zldev = zldpll->dev;
|
||||
- const struct zl3073x_ref *ref;
|
||||
- u8 ref_id;
|
||||
+ const struct zl3073x_chan *chan;
|
||||
s64 ffo;
|
||||
|
||||
- /* Get reference monitor status */
|
||||
- ref_id = zl3073x_input_pin_ref_get(pin->id);
|
||||
- ref = zl3073x_ref_state_get(zldev, ref_id);
|
||||
-
|
||||
- /* Do not report ffo changes if the reference monitor report errors */
|
||||
- if (!zl3073x_ref_is_status_ok(ref))
|
||||
+ if (pin->operstate != DPLL_PIN_OPERSTATE_ACTIVE)
|
||||
return false;
|
||||
|
||||
- /* Compare with previous value */
|
||||
- ffo = zl3073x_ref_ffo_get(ref);
|
||||
- if (pin->freq_offset != ffo) {
|
||||
- dev_dbg(zldev->dev, "%s freq offset changed: %lld -> %lld\n",
|
||||
- pin->label, pin->freq_offset, ffo);
|
||||
- pin->freq_offset = ffo;
|
||||
+ chan = zl3073x_chan_state_get(zldpll->dev, zldpll->id);
|
||||
+ ffo = mul_s64_u64_shr(zl3073x_chan_df_offset_get(chan),
|
||||
+ 244140625, 36);
|
||||
|
||||
+ if (atomic64_xchg(&pin->freq_offset, ffo) != ffo) {
|
||||
+ dev_dbg(zldev->dev, "%s freq offset changed to: %lld\n",
|
||||
+ pin->label, ffo);
|
||||
return true;
|
||||
}
|
||||
|
||||
diff --git a/drivers/dpll/zl3073x/ref.h b/drivers/dpll/zl3073x/ref.h
|
||||
index 55e80e4f0873..e140ca3ea17d 100644
|
||||
--- a/drivers/dpll/zl3073x/ref.h
|
||||
+++ b/drivers/dpll/zl3073x/ref.h
|
||||
@@ -22,7 +22,6 @@ struct zl3073x_dev;
|
||||
* @freq_ratio_n: FEC mode divisor
|
||||
* @sync_ctrl: reference sync control
|
||||
* @config: reference config
|
||||
- * @ffo: current fractional frequency offset
|
||||
* @meas_freq: measured input frequency in Hz
|
||||
* @mon_status: reference monitor status
|
||||
*/
|
||||
@@ -40,7 +39,6 @@ struct zl3073x_ref {
|
||||
u8 config;
|
||||
);
|
||||
struct_group(stat, /* Status */
|
||||
- s64 ffo;
|
||||
u32 meas_freq;
|
||||
u8 mon_status;
|
||||
);
|
||||
@@ -58,18 +56,6 @@ int zl3073x_ref_state_update(struct zl3073x_dev *zldev, u8 index);
|
||||
|
||||
int zl3073x_ref_freq_factorize(u32 freq, u16 *base, u16 *mult);
|
||||
|
||||
-/**
|
||||
- * zl3073x_ref_ffo_get - get current fractional frequency offset
|
||||
- * @ref: pointer to ref state
|
||||
- *
|
||||
- * Return: the latest measured fractional frequency offset
|
||||
- */
|
||||
-static inline s64
|
||||
-zl3073x_ref_ffo_get(const struct zl3073x_ref *ref)
|
||||
-{
|
||||
- return ref->ffo;
|
||||
-}
|
||||
-
|
||||
/**
|
||||
* zl3073x_ref_meas_freq_get - get measured input frequency
|
||||
* @ref: pointer to ref state
|
||||
diff --git a/drivers/dpll/zl3073x/regs.h b/drivers/dpll/zl3073x/regs.h
|
||||
index 8015808bdf54..9578f0009528 100644
|
||||
--- a/drivers/dpll/zl3073x/regs.h
|
||||
+++ b/drivers/dpll/zl3073x/regs.h
|
||||
@@ -164,6 +164,11 @@
|
||||
#define ZL_DPLL_MODE_REFSEL_MODE_NCO 4
|
||||
#define ZL_DPLL_MODE_REFSEL_REF GENMASK(7, 4)
|
||||
|
||||
+#define ZL_REG_DPLL_DF_READ(_idx) \
|
||||
+ ZL_REG_IDX(_idx, 5, 0x28, 1, ZL3073X_MAX_CHANNELS, 1)
|
||||
+#define ZL_DPLL_DF_READ_SEM BIT(4)
|
||||
+#define ZL_DPLL_DF_READ_REF_OFST BIT(3)
|
||||
+
|
||||
#define ZL_REG_DPLL_MEAS_CTRL ZL_REG(5, 0x50, 1)
|
||||
#define ZL_DPLL_MEAS_CTRL_EN BIT(0)
|
||||
#define ZL_DPLL_MEAS_CTRL_AVG_FACTOR GENMASK(7, 4)
|
||||
@@ -176,6 +181,16 @@
|
||||
#define ZL_REG_DPLL_PHASE_ERR_DATA(_idx) \
|
||||
ZL_REG_IDX(_idx, 5, 0x55, 6, ZL3073X_MAX_CHANNELS, 6)
|
||||
|
||||
+/*******************************
|
||||
+ * Register Pages 6-7, DPLL Data
|
||||
+ *******************************/
|
||||
+
|
||||
+#define ZL_REG_DPLL_DF_OFFSET_03(_idx) \
|
||||
+ ZL_REG_IDX(_idx, 6, 0x00, 6, 4, 0x20)
|
||||
+#define ZL_REG_DPLL_DF_OFFSET_4 ZL_REG(7, 0x00, 6)
|
||||
+#define ZL_REG_DPLL_DF_OFFSET(_idx) \
|
||||
+ ((_idx) < 4 ? ZL_REG_DPLL_DF_OFFSET_03(_idx) : ZL_REG_DPLL_DF_OFFSET_4)
|
||||
+
|
||||
/***********************************
|
||||
* Register Page 9, Synth and Output
|
||||
***********************************/
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,500 @@
|
||||
From 57c78bd2e2dd08897acd35b2bf8bcef322e36f5e Mon Sep 17 00:00:00 2001
|
||||
From: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Date: Thu, 26 Mar 2026 00:17:09 +0100
|
||||
Subject: [PATCH] netfilter: flowtable: strictly check for maximum number of
|
||||
actions
|
||||
|
||||
[ Upstream commit 76522fcdbc3a02b568f5d957f7e66fc194abb893 ]
|
||||
|
||||
The maximum number of flowtable hardware offload actions in IPv6 is:
|
||||
|
||||
* ethernet mangling (4 payload actions, 2 for each ethernet address)
|
||||
* SNAT (4 payload actions)
|
||||
* DNAT (4 payload actions)
|
||||
* Double VLAN (4 vlan actions, 2 for popping vlan, and 2 for pushing)
|
||||
for QinQ.
|
||||
* Redirect (1 action)
|
||||
|
||||
Which makes 17, while the maximum is 16. But act_ct supports for tunnels
|
||||
actions too. Note that payload action operates at 32-bit word level, so
|
||||
mangling an IPv6 address takes 4 payload actions.
|
||||
|
||||
Update flow_action_entry_next() calls to check for the maximum number of
|
||||
supported actions.
|
||||
|
||||
While at it, rise the maximum number of actions per flow from 16 to 24
|
||||
so this works fine with IPv6 setups.
|
||||
|
||||
Fixes: c29f74e0df7a ("netfilter: nf_flow_table: hardware offload support")
|
||||
Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
|
||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
|
||||
Signed-off-by: Sasha Levin <sashal@kernel.org>
|
||||
|
||||
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
|
||||
index e06bc36f49fe..4f346f51d7d7 100644
|
||||
--- a/net/netfilter/nf_flow_table_offload.c
|
||||
+++ b/net/netfilter/nf_flow_table_offload.c
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <net/netfilter/nf_conntrack_core.h>
|
||||
#include <net/netfilter/nf_conntrack_tuple.h>
|
||||
|
||||
+#define NF_FLOW_RULE_ACTION_MAX 24
|
||||
+
|
||||
static struct workqueue_struct *nf_flow_offload_add_wq;
|
||||
static struct workqueue_struct *nf_flow_offload_del_wq;
|
||||
static struct workqueue_struct *nf_flow_offload_stats_wq;
|
||||
@@ -215,7 +217,12 @@ static void flow_offload_mangle(struct flow_action_entry *entry,
|
||||
static inline struct flow_action_entry *
|
||||
flow_action_entry_next(struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
- int i = flow_rule->rule->action.num_entries++;
|
||||
+ int i;
|
||||
+
|
||||
+ if (unlikely(flow_rule->rule->action.num_entries >= NF_FLOW_RULE_ACTION_MAX))
|
||||
+ return NULL;
|
||||
+
|
||||
+ i = flow_rule->rule->action.num_entries++;
|
||||
|
||||
return &flow_rule->rule->action.entries[i];
|
||||
}
|
||||
@@ -233,6 +240,9 @@ static int flow_offload_eth_src(struct net *net,
|
||||
u32 mask, val;
|
||||
u16 val16;
|
||||
|
||||
+ if (!entry0 || !entry1)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
this_tuple = &flow->tuplehash[dir].tuple;
|
||||
|
||||
switch (this_tuple->xmit_type) {
|
||||
@@ -283,6 +293,9 @@ static int flow_offload_eth_dst(struct net *net,
|
||||
u8 nud_state;
|
||||
u16 val16;
|
||||
|
||||
+ if (!entry0 || !entry1)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
this_tuple = &flow->tuplehash[dir].tuple;
|
||||
|
||||
switch (this_tuple->xmit_type) {
|
||||
@@ -324,16 +337,19 @@ static int flow_offload_eth_dst(struct net *net,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_ipv4_snat(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_ipv4_snat(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
__be32 addr;
|
||||
u32 offset;
|
||||
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_v4.s_addr;
|
||||
@@ -344,23 +360,27 @@ static void flow_offload_ipv4_snat(struct net *net,
|
||||
offset = offsetof(struct iphdr, daddr);
|
||||
break;
|
||||
default:
|
||||
- return;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset,
|
||||
&addr, &mask);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_ipv4_dnat(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_ipv4_dnat(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
__be32 addr;
|
||||
u32 offset;
|
||||
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
addr = flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_v4.s_addr;
|
||||
@@ -371,14 +391,15 @@ static void flow_offload_ipv4_dnat(struct net *net,
|
||||
offset = offsetof(struct iphdr, saddr);
|
||||
break;
|
||||
default:
|
||||
- return;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP4, offset,
|
||||
&addr, &mask);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
|
||||
+static int flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
|
||||
unsigned int offset,
|
||||
const __be32 *addr, const __be32 *mask)
|
||||
{
|
||||
@@ -387,15 +408,20 @@ static void flow_offload_ipv6_mangle(struct nf_flow_rule *flow_rule,
|
||||
|
||||
for (i = 0; i < sizeof(struct in6_addr) / sizeof(u32); i++) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
flow_offload_mangle(entry, FLOW_ACT_MANGLE_HDR_TYPE_IP6,
|
||||
offset + i * sizeof(u32), &addr[i], mask);
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_ipv6_snat(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_ipv6_snat(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
const __be32 *addr;
|
||||
@@ -411,16 +437,16 @@ static void flow_offload_ipv6_snat(struct net *net,
|
||||
offset = offsetof(struct ipv6hdr, daddr);
|
||||
break;
|
||||
default:
|
||||
- return;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
- flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
+ return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
}
|
||||
|
||||
-static void flow_offload_ipv6_dnat(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_ipv6_dnat(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
u32 mask = ~htonl(0xffffffff);
|
||||
const __be32 *addr;
|
||||
@@ -436,10 +462,10 @@ static void flow_offload_ipv6_dnat(struct net *net,
|
||||
offset = offsetof(struct ipv6hdr, saddr);
|
||||
break;
|
||||
default:
|
||||
- return;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
- flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
+ return flow_offload_ipv6_mangle(flow_rule, offset, addr, &mask);
|
||||
}
|
||||
|
||||
static int flow_offload_l4proto(const struct flow_offload *flow)
|
||||
@@ -461,15 +487,18 @@ static int flow_offload_l4proto(const struct flow_offload *flow)
|
||||
return type;
|
||||
}
|
||||
|
||||
-static void flow_offload_port_snat(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_port_snat(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask, port;
|
||||
u32 offset;
|
||||
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.dst_port);
|
||||
@@ -484,22 +513,26 @@ static void flow_offload_port_snat(struct net *net,
|
||||
mask = ~htonl(0xffff);
|
||||
break;
|
||||
default:
|
||||
- return;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, flow_offload_l4proto(flow), offset,
|
||||
&port, &mask);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_port_dnat(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_port_dnat(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
u32 mask, port;
|
||||
u32 offset;
|
||||
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
switch (dir) {
|
||||
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
||||
port = ntohs(flow->tuplehash[FLOW_OFFLOAD_DIR_REPLY].tuple.src_port);
|
||||
@@ -514,20 +547,24 @@ static void flow_offload_port_dnat(struct net *net,
|
||||
mask = ~htonl(0xffff0000);
|
||||
break;
|
||||
default:
|
||||
- return;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
flow_offload_mangle(entry, flow_offload_l4proto(flow), offset,
|
||||
&port, &mask);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_ipv4_checksum(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_ipv4_checksum(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
u8 protonum = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.l4proto;
|
||||
struct flow_action_entry *entry = flow_action_entry_next(flow_rule);
|
||||
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
+
|
||||
entry->id = FLOW_ACTION_CSUM;
|
||||
entry->csum_flags = TCA_CSUM_UPDATE_FLAG_IPV4HDR;
|
||||
|
||||
@@ -539,12 +576,14 @@ static void flow_offload_ipv4_checksum(struct net *net,
|
||||
entry->csum_flags |= TCA_CSUM_UPDATE_FLAG_UDP;
|
||||
break;
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_redirect(struct net *net,
|
||||
- const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_redirect(struct net *net,
|
||||
+ const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
const struct flow_offload_tuple *this_tuple, *other_tuple;
|
||||
struct flow_action_entry *entry;
|
||||
@@ -562,21 +601,28 @@ static void flow_offload_redirect(struct net *net,
|
||||
ifindex = other_tuple->iifidx;
|
||||
break;
|
||||
default:
|
||||
- return;
|
||||
+ return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
dev = dev_get_by_index(net, ifindex);
|
||||
if (!dev)
|
||||
- return;
|
||||
+ return -ENODEV;
|
||||
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
+ if (!entry) {
|
||||
+ dev_put(dev);
|
||||
+ return -E2BIG;
|
||||
+ }
|
||||
+
|
||||
entry->id = FLOW_ACTION_REDIRECT;
|
||||
entry->dev = dev;
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
const struct flow_offload_tuple *this_tuple;
|
||||
struct flow_action_entry *entry;
|
||||
@@ -584,7 +630,7 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
|
||||
this_tuple = &flow->tuplehash[dir].tuple;
|
||||
if (this_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
|
||||
- return;
|
||||
+ return 0;
|
||||
|
||||
dst = this_tuple->dst_cache;
|
||||
if (dst && dst->lwtstate) {
|
||||
@@ -593,15 +639,19 @@ static void flow_offload_encap_tunnel(const struct flow_offload *flow,
|
||||
tun_info = lwt_tun_info(dst->lwtstate);
|
||||
if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
entry->id = FLOW_ACTION_TUNNEL_ENCAP;
|
||||
entry->tunnel = tun_info;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static void flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
- enum flow_offload_tuple_dir dir,
|
||||
- struct nf_flow_rule *flow_rule)
|
||||
+static int flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
+ enum flow_offload_tuple_dir dir,
|
||||
+ struct nf_flow_rule *flow_rule)
|
||||
{
|
||||
const struct flow_offload_tuple *other_tuple;
|
||||
struct flow_action_entry *entry;
|
||||
@@ -609,7 +659,7 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
|
||||
other_tuple = &flow->tuplehash[!dir].tuple;
|
||||
if (other_tuple->xmit_type == FLOW_OFFLOAD_XMIT_DIRECT)
|
||||
- return;
|
||||
+ return 0;
|
||||
|
||||
dst = other_tuple->dst_cache;
|
||||
if (dst && dst->lwtstate) {
|
||||
@@ -618,9 +668,13 @@ static void flow_offload_decap_tunnel(const struct flow_offload *flow,
|
||||
tun_info = lwt_tun_info(dst->lwtstate);
|
||||
if (tun_info && (tun_info->mode & IP_TUNNEL_INFO_TX)) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
+ if (!entry)
|
||||
+ return -E2BIG;
|
||||
entry->id = FLOW_ACTION_TUNNEL_DECAP;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -632,8 +686,9 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
|
||||
const struct flow_offload_tuple *tuple;
|
||||
int i;
|
||||
|
||||
- flow_offload_decap_tunnel(flow, dir, flow_rule);
|
||||
- flow_offload_encap_tunnel(flow, dir, flow_rule);
|
||||
+ if (flow_offload_decap_tunnel(flow, dir, flow_rule) < 0 ||
|
||||
+ flow_offload_encap_tunnel(flow, dir, flow_rule) < 0)
|
||||
+ return -1;
|
||||
|
||||
if (flow_offload_eth_src(net, flow, dir, flow_rule) < 0 ||
|
||||
flow_offload_eth_dst(net, flow, dir, flow_rule) < 0)
|
||||
@@ -649,6 +704,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
|
||||
|
||||
if (tuple->encap[i].proto == htons(ETH_P_8021Q)) {
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
+ if (!entry)
|
||||
+ return -1;
|
||||
entry->id = FLOW_ACTION_VLAN_POP;
|
||||
}
|
||||
}
|
||||
@@ -662,6 +719,8 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
|
||||
continue;
|
||||
|
||||
entry = flow_action_entry_next(flow_rule);
|
||||
+ if (!entry)
|
||||
+ return -1;
|
||||
|
||||
switch (other_tuple->encap[i].proto) {
|
||||
case htons(ETH_P_PPP_SES):
|
||||
@@ -687,18 +746,22 @@ int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
|
||||
return -1;
|
||||
|
||||
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
|
||||
- flow_offload_ipv4_snat(net, flow, dir, flow_rule);
|
||||
- flow_offload_port_snat(net, flow, dir, flow_rule);
|
||||
+ if (flow_offload_ipv4_snat(net, flow, dir, flow_rule) < 0 ||
|
||||
+ flow_offload_port_snat(net, flow, dir, flow_rule) < 0)
|
||||
+ return -1;
|
||||
}
|
||||
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
|
||||
- flow_offload_ipv4_dnat(net, flow, dir, flow_rule);
|
||||
- flow_offload_port_dnat(net, flow, dir, flow_rule);
|
||||
+ if (flow_offload_ipv4_dnat(net, flow, dir, flow_rule) < 0 ||
|
||||
+ flow_offload_port_dnat(net, flow, dir, flow_rule) < 0)
|
||||
+ return -1;
|
||||
}
|
||||
if (test_bit(NF_FLOW_SNAT, &flow->flags) ||
|
||||
test_bit(NF_FLOW_DNAT, &flow->flags))
|
||||
- flow_offload_ipv4_checksum(net, flow, flow_rule);
|
||||
+ if (flow_offload_ipv4_checksum(net, flow, flow_rule) < 0)
|
||||
+ return -1;
|
||||
|
||||
- flow_offload_redirect(net, flow, dir, flow_rule);
|
||||
+ if (flow_offload_redirect(net, flow, dir, flow_rule) < 0)
|
||||
+ return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -712,22 +775,23 @@ int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow,
|
||||
return -1;
|
||||
|
||||
if (test_bit(NF_FLOW_SNAT, &flow->flags)) {
|
||||
- flow_offload_ipv6_snat(net, flow, dir, flow_rule);
|
||||
- flow_offload_port_snat(net, flow, dir, flow_rule);
|
||||
+ if (flow_offload_ipv6_snat(net, flow, dir, flow_rule) < 0 ||
|
||||
+ flow_offload_port_snat(net, flow, dir, flow_rule) < 0)
|
||||
+ return -1;
|
||||
}
|
||||
if (test_bit(NF_FLOW_DNAT, &flow->flags)) {
|
||||
- flow_offload_ipv6_dnat(net, flow, dir, flow_rule);
|
||||
- flow_offload_port_dnat(net, flow, dir, flow_rule);
|
||||
+ if (flow_offload_ipv6_dnat(net, flow, dir, flow_rule) < 0 ||
|
||||
+ flow_offload_port_dnat(net, flow, dir, flow_rule) < 0)
|
||||
+ return -1;
|
||||
}
|
||||
|
||||
- flow_offload_redirect(net, flow, dir, flow_rule);
|
||||
+ if (flow_offload_redirect(net, flow, dir, flow_rule) < 0)
|
||||
+ return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv6);
|
||||
|
||||
-#define NF_FLOW_RULE_ACTION_MAX 16
|
||||
-
|
||||
static struct nf_flow_rule *
|
||||
nf_flow_offload_rule_alloc(struct net *net,
|
||||
const struct flow_offload_work *offload,
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
355
1275-bluetooth-hci-add-initial-support-for-past.patch
Normal file
355
1275-bluetooth-hci-add-initial-support-for-past.patch
Normal file
@ -0,0 +1,355 @@
|
||||
From c33ae38cff3d15c40384b4ecbaccc4c88d3cb741 Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Wed, 15 Apr 2026 16:59:13 -0500
|
||||
Subject: [PATCH] Bluetooth: HCI: Add initial support for PAST
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit 33b2835f0b7e2a458473b0e3a23b54b92108b6b0
|
||||
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
Date: Tue Sep 2 11:11:40 2025 -0400
|
||||
|
||||
Bluetooth: HCI: Add initial support for PAST
|
||||
|
||||
This adds PAST related commands (HCI_OP_LE_PAST,
|
||||
HCI_OP_LE_PAST_SET_INFO and HCI_OP_LE_PAST_PARAMS) and events
|
||||
(HCI_EV_LE_PAST_RECEIVED) along with handling of PAST sender and
|
||||
receiver features bits including new MGMG settings (
|
||||
HCI_EV_LE_PAST_RECEIVED and MGMT_SETTING_PAST_RECEIVER) which
|
||||
userspace can use to determine if PAST is supported by the
|
||||
controller.
|
||||
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
|
||||
index cb4c02d00759..d883ad233ebc 100644
|
||||
--- a/include/net/bluetooth/hci.h
|
||||
+++ b/include/net/bluetooth/hci.h
|
||||
@@ -647,6 +647,8 @@ enum {
|
||||
#define HCI_LE_EXT_ADV 0x10
|
||||
#define HCI_LE_PERIODIC_ADV 0x20
|
||||
#define HCI_LE_CHAN_SEL_ALG2 0x40
|
||||
+#define HCI_LE_PAST_SENDER 0x01
|
||||
+#define HCI_LE_PAST_RECEIVER 0x02
|
||||
#define HCI_LE_CIS_CENTRAL 0x10
|
||||
#define HCI_LE_CIS_PERIPHERAL 0x20
|
||||
#define HCI_LE_ISO_BROADCASTER 0x40
|
||||
@@ -2068,6 +2070,44 @@ struct hci_cp_le_set_privacy_mode {
|
||||
__u8 mode;
|
||||
} __packed;
|
||||
|
||||
+#define HCI_OP_LE_PAST 0x205a
|
||||
+struct hci_cp_le_past {
|
||||
+ __le16 handle;
|
||||
+ __le16 service_data;
|
||||
+ __le16 sync_handle;
|
||||
+} __packed;
|
||||
+
|
||||
+struct hci_rp_le_past {
|
||||
+ __u8 status;
|
||||
+ __le16 handle;
|
||||
+} __packed;
|
||||
+
|
||||
+#define HCI_OP_LE_PAST_SET_INFO 0x205b
|
||||
+struct hci_cp_le_past_set_info {
|
||||
+ __le16 handle;
|
||||
+ __le16 service_data;
|
||||
+ __u8 adv_handle;
|
||||
+} __packed;
|
||||
+
|
||||
+struct hci_rp_le_past_set_info {
|
||||
+ __u8 status;
|
||||
+ __le16 handle;
|
||||
+} __packed;
|
||||
+
|
||||
+#define HCI_OP_LE_PAST_PARAMS 0x205c
|
||||
+struct hci_cp_le_past_params {
|
||||
+ __le16 handle;
|
||||
+ __u8 mode;
|
||||
+ __le16 skip;
|
||||
+ __le16 sync_timeout;
|
||||
+ __u8 cte_type;
|
||||
+} __packed;
|
||||
+
|
||||
+struct hci_rp_le_past_params {
|
||||
+ __u8 status;
|
||||
+ __le16 handle;
|
||||
+} __packed;
|
||||
+
|
||||
#define HCI_OP_LE_READ_BUFFER_SIZE_V2 0x2060
|
||||
struct hci_rp_le_read_buffer_size_v2 {
|
||||
__u8 status;
|
||||
@@ -2800,6 +2840,20 @@ struct hci_evt_le_ext_adv_set_term {
|
||||
__u8 num_evts;
|
||||
} __packed;
|
||||
|
||||
+#define HCI_EV_LE_PAST_RECEIVED 0x18
|
||||
+struct hci_ev_le_past_received {
|
||||
+ __u8 status;
|
||||
+ __le16 handle;
|
||||
+ __le16 service_data;
|
||||
+ __le16 sync_handle;
|
||||
+ __u8 sid;
|
||||
+ __u8 bdaddr_type;
|
||||
+ bdaddr_t bdaddr;
|
||||
+ __u8 phy;
|
||||
+ __le16 interval;
|
||||
+ __u8 clock_accuracy;
|
||||
+} __packed;
|
||||
+
|
||||
#define HCI_EVT_LE_CIS_ESTABLISHED 0x19
|
||||
struct hci_evt_le_cis_established {
|
||||
__u8 status;
|
||||
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
|
||||
index 0cb87687837f..1bd12c303e25 100644
|
||||
--- a/include/net/bluetooth/hci_core.h
|
||||
+++ b/include/net/bluetooth/hci_core.h
|
||||
@@ -2053,6 +2053,18 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||
#define sync_recv_capable(dev) \
|
||||
((dev)->le_features[3] & HCI_LE_ISO_SYNC_RECEIVER)
|
||||
#define sync_recv_enabled(dev) (le_enabled(dev) && sync_recv_capable(dev))
|
||||
+#define past_sender_capable(dev) \
|
||||
+ ((dev)->le_features[3] & HCI_LE_PAST_SENDER)
|
||||
+#define past_receiver_capable(dev) \
|
||||
+ ((dev)->le_features[3] & HCI_LE_PAST_RECEIVER)
|
||||
+#define past_capable(dev) \
|
||||
+ (past_sender_capable(dev) || past_receiver_capable(dev))
|
||||
+#define past_sender_enabled(dev) \
|
||||
+ (le_enabled(dev) && past_sender_capable(dev))
|
||||
+#define past_receiver_enabled(dev) \
|
||||
+ (le_enabled(dev) && past_receiver_capable(dev))
|
||||
+#define past_enabled(dev) \
|
||||
+ (past_sender_enabled(dev) || past_receiver_enabled(dev))
|
||||
|
||||
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
|
||||
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
|
||||
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
|
||||
index f5be96f08b9d..8234915854b6 100644
|
||||
--- a/include/net/bluetooth/mgmt.h
|
||||
+++ b/include/net/bluetooth/mgmt.h
|
||||
@@ -119,6 +119,8 @@ struct mgmt_rp_read_index_list {
|
||||
#define MGMT_SETTING_ISO_BROADCASTER BIT(20)
|
||||
#define MGMT_SETTING_ISO_SYNC_RECEIVER BIT(21)
|
||||
#define MGMT_SETTING_LL_PRIVACY BIT(22)
|
||||
+#define MGMT_SETTING_PAST_SENDER BIT(23)
|
||||
+#define MGMT_SETTING_PAST_RECEIVER BIT(24)
|
||||
|
||||
#define MGMT_OP_READ_INFO 0x0004
|
||||
#define MGMT_READ_INFO_SIZE 0
|
||||
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
|
||||
index 3838b90343d9..af34c9938509 100644
|
||||
--- a/net/bluetooth/hci_event.c
|
||||
+++ b/net/bluetooth/hci_event.c
|
||||
@@ -5936,6 +5936,71 @@ static void hci_le_ext_adv_term_evt(struct hci_dev *hdev, void *data,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
+static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
|
||||
+{
|
||||
+ struct hci_cp_le_pa_term_sync cp;
|
||||
+
|
||||
+ memset(&cp, 0, sizeof(cp));
|
||||
+ cp.handle = handle;
|
||||
+
|
||||
+ return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
|
||||
+}
|
||||
+
|
||||
+static void hci_le_past_received_evt(struct hci_dev *hdev, void *data,
|
||||
+ struct sk_buff *skb)
|
||||
+{
|
||||
+ struct hci_ev_le_past_received *ev = data;
|
||||
+ int mask = hdev->link_mode;
|
||||
+ __u8 flags = 0;
|
||||
+ struct hci_conn *pa_sync, *conn;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
+
|
||||
+ hci_dev_lock(hdev);
|
||||
+
|
||||
+ hci_dev_clear_flag(hdev, HCI_PA_SYNC);
|
||||
+
|
||||
+ conn = hci_conn_hash_lookup_create_pa_sync(hdev);
|
||||
+ if (!conn) {
|
||||
+ bt_dev_err(hdev,
|
||||
+ "Unable to find connection for dst %pMR sid 0x%2.2x",
|
||||
+ &ev->bdaddr, ev->sid);
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ conn->sync_handle = le16_to_cpu(ev->sync_handle);
|
||||
+ conn->sid = HCI_SID_INVALID;
|
||||
+
|
||||
+ mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, PA_LINK,
|
||||
+ &flags);
|
||||
+ if (!(mask & HCI_LM_ACCEPT)) {
|
||||
+ hci_le_pa_term_sync(hdev, ev->sync_handle);
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ if (!(flags & HCI_PROTO_DEFER))
|
||||
+ goto unlock;
|
||||
+
|
||||
+ /* Add connection to indicate PA sync event */
|
||||
+ pa_sync = hci_conn_add_unset(hdev, PA_LINK, BDADDR_ANY,
|
||||
+ HCI_ROLE_SLAVE);
|
||||
+
|
||||
+ if (IS_ERR(pa_sync))
|
||||
+ goto unlock;
|
||||
+
|
||||
+ pa_sync->sync_handle = le16_to_cpu(ev->sync_handle);
|
||||
+
|
||||
+ if (ev->status) {
|
||||
+ set_bit(HCI_CONN_PA_SYNC_FAILED, &pa_sync->flags);
|
||||
+
|
||||
+ /* Notify iso layer */
|
||||
+ hci_connect_cfm(pa_sync, ev->status);
|
||||
+ }
|
||||
+
|
||||
+unlock:
|
||||
+ hci_dev_unlock(hdev);
|
||||
+}
|
||||
+
|
||||
static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@@ -6412,16 +6477,6 @@ static void hci_le_ext_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
-static int hci_le_pa_term_sync(struct hci_dev *hdev, __le16 handle)
|
||||
-{
|
||||
- struct hci_cp_le_pa_term_sync cp;
|
||||
-
|
||||
- memset(&cp, 0, sizeof(cp));
|
||||
- cp.handle = handle;
|
||||
-
|
||||
- return hci_send_cmd(hdev, HCI_OP_LE_PA_TERM_SYNC, sizeof(cp), &cp);
|
||||
-}
|
||||
-
|
||||
static void hci_le_pa_sync_established_evt(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@@ -7206,6 +7261,10 @@ static const struct hci_le_ev {
|
||||
/* [0x12 = HCI_EV_LE_EXT_ADV_SET_TERM] */
|
||||
HCI_LE_EV(HCI_EV_LE_EXT_ADV_SET_TERM, hci_le_ext_adv_term_evt,
|
||||
sizeof(struct hci_evt_le_ext_adv_set_term)),
|
||||
+ /* [0x18 = HCI_EVT_LE_PAST_RECEIVED] */
|
||||
+ HCI_LE_EV(HCI_EV_LE_PAST_RECEIVED,
|
||||
+ hci_le_past_received_evt,
|
||||
+ sizeof(struct hci_ev_le_past_received)),
|
||||
/* [0x19 = HCI_EVT_LE_CIS_ESTABLISHED] */
|
||||
HCI_LE_EV(HCI_EVT_LE_CIS_ESTABLISHED, hci_le_cis_established_evt,
|
||||
sizeof(struct hci_evt_le_cis_established)),
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index e302afc781a5..d181c89a7b0e 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -4393,6 +4393,9 @@ static int hci_le_set_event_mask_sync(struct hci_dev *hdev)
|
||||
if (ext_adv_capable(hdev))
|
||||
events[2] |= 0x02; /* LE Advertising Set Terminated */
|
||||
|
||||
+ if (past_receiver_capable(hdev))
|
||||
+ events[2] |= 0x80; /* LE PAST Received */
|
||||
+
|
||||
if (cis_capable(hdev)) {
|
||||
events[3] |= 0x01; /* LE CIS Established */
|
||||
if (cis_peripheral_capable(hdev))
|
||||
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
|
||||
index c0ae6c6fbe94..3c568d3d8ac9 100644
|
||||
--- a/net/bluetooth/iso.c
|
||||
+++ b/net/bluetooth/iso.c
|
||||
@@ -80,6 +80,7 @@ static struct bt_iso_qos default_qos;
|
||||
static bool check_ucast_qos(struct bt_iso_qos *qos);
|
||||
static bool check_bcast_qos(struct bt_iso_qos *qos);
|
||||
static bool iso_match_sid(struct sock *sk, void *data);
|
||||
+static bool iso_match_sid_past(struct sock *sk, void *data);
|
||||
static bool iso_match_sync_handle(struct sock *sk, void *data);
|
||||
static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data);
|
||||
static void iso_sock_disconn(struct sock *sk);
|
||||
@@ -2090,6 +2091,16 @@ static bool iso_match_sid(struct sock *sk, void *data)
|
||||
return ev->sid == iso_pi(sk)->bc_sid;
|
||||
}
|
||||
|
||||
+static bool iso_match_sid_past(struct sock *sk, void *data)
|
||||
+{
|
||||
+ struct hci_ev_le_past_received *ev = data;
|
||||
+
|
||||
+ if (iso_pi(sk)->bc_sid == HCI_SID_INVALID)
|
||||
+ return true;
|
||||
+
|
||||
+ return ev->sid == iso_pi(sk)->bc_sid;
|
||||
+}
|
||||
+
|
||||
static bool iso_match_sync_handle(struct sock *sk, void *data)
|
||||
{
|
||||
struct hci_evt_le_big_info_adv_report *ev = data;
|
||||
@@ -2109,6 +2120,7 @@ static bool iso_match_sync_handle_pa_report(struct sock *sk, void *data)
|
||||
int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
{
|
||||
struct hci_ev_le_pa_sync_established *ev1;
|
||||
+ struct hci_ev_le_past_received *ev1a;
|
||||
struct hci_evt_le_big_info_adv_report *ev2;
|
||||
struct hci_ev_le_per_adv_report *ev3;
|
||||
struct sock *sk;
|
||||
@@ -2122,6 +2134,7 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
* SID to listen to and once sync is established its handle needs to
|
||||
* be stored in iso_pi(sk)->sync_handle so it can be matched once
|
||||
* receiving the BIG Info.
|
||||
+ * 1a. HCI_EV_LE_PAST_RECEIVED: alternative to 1.
|
||||
* 2. HCI_EVT_LE_BIG_INFO_ADV_REPORT: When connect_ind is triggered by a
|
||||
* a BIG Info it attempts to check if there any listening socket with
|
||||
* the same sync_handle and if it does then attempt to create a sync.
|
||||
@@ -2141,6 +2154,18 @@ int iso_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags)
|
||||
goto done;
|
||||
}
|
||||
|
||||
+ ev1a = hci_recv_event_data(hdev, HCI_EV_LE_PAST_RECEIVED);
|
||||
+ if (ev1a) {
|
||||
+ sk = iso_get_sock(&hdev->bdaddr, bdaddr, BT_LISTEN,
|
||||
+ iso_match_sid_past, ev1a);
|
||||
+ if (sk && !ev1a->status) {
|
||||
+ iso_pi(sk)->sync_handle = le16_to_cpu(ev1a->sync_handle);
|
||||
+ iso_pi(sk)->bc_sid = ev1a->sid;
|
||||
+ }
|
||||
+
|
||||
+ goto done;
|
||||
+ }
|
||||
+
|
||||
ev2 = hci_recv_event_data(hdev, HCI_EVT_LE_BIG_INFO_ADV_REPORT);
|
||||
if (ev2) {
|
||||
/* Check if BIGInfo report has already been handled */
|
||||
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
|
||||
index 6fd13a39f48a..7cca24f6ef49 100644
|
||||
--- a/net/bluetooth/mgmt.c
|
||||
+++ b/net/bluetooth/mgmt.c
|
||||
@@ -852,6 +852,12 @@ static u32 get_supported_settings(struct hci_dev *hdev)
|
||||
if (ll_privacy_capable(hdev))
|
||||
settings |= MGMT_SETTING_LL_PRIVACY;
|
||||
|
||||
+ if (past_sender_capable(hdev))
|
||||
+ settings |= MGMT_SETTING_PAST_SENDER;
|
||||
+
|
||||
+ if (past_receiver_capable(hdev))
|
||||
+ settings |= MGMT_SETTING_PAST_RECEIVER;
|
||||
+
|
||||
settings |= MGMT_SETTING_PHY_CONFIGURATION;
|
||||
|
||||
return settings;
|
||||
@@ -937,6 +943,12 @@ static u32 get_current_settings(struct hci_dev *hdev)
|
||||
if (ll_privacy_enabled(hdev))
|
||||
settings |= MGMT_SETTING_LL_PRIVACY;
|
||||
|
||||
+ if (past_sender_enabled(hdev))
|
||||
+ settings |= MGMT_SETTING_PAST_SENDER;
|
||||
+
|
||||
+ if (past_receiver_enabled(hdev))
|
||||
+ settings |= MGMT_SETTING_PAST_RECEIVER;
|
||||
+
|
||||
return settings;
|
||||
}
|
||||
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
300
1276-bluetooth-iso-add-support-to-bind-to-trigger-past.patch
Normal file
300
1276-bluetooth-iso-add-support-to-bind-to-trigger-past.patch
Normal file
@ -0,0 +1,300 @@
|
||||
From 414511072bcdf8a60a29bf43cca47260006df84b Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Wed, 15 Apr 2026 17:00:08 -0500
|
||||
Subject: [PATCH] Bluetooth: ISO: Add support to bind to trigger PAST
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit d3413703d5f8b7d1e6f514f9440ed5da1bc30796
|
||||
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
Date: Fri Sep 5 11:34:44 2025 -0400
|
||||
|
||||
Bluetooth: ISO: Add support to bind to trigger PAST
|
||||
|
||||
This makes it possible to bind to a different destination address
|
||||
after being connected (BT_CONNECTED, BT_CONNECT2) which then triggers
|
||||
PAST Sender proceedure to transfer the PA Sync to the destination
|
||||
address.
|
||||
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
|
||||
Conflicts:
|
||||
Context difference due to missing network commit:
|
||||
commit 0e50474fa514 net: Convert proto_ops bind() callbacks to use sockaddr_unsized
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
|
||||
index 8c2235444808..1f74722f3f4d 100644
|
||||
--- a/include/net/bluetooth/hci_core.h
|
||||
+++ b/include/net/bluetooth/hci_core.h
|
||||
@@ -1602,6 +1602,7 @@ struct hci_conn *hci_bind_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
|
||||
struct bt_iso_qos *qos,
|
||||
__u8 base_len, __u8 *base, u16 timeout);
|
||||
+int hci_past_bis(struct hci_conn *conn, bdaddr_t *dst, __u8 dst_type);
|
||||
struct hci_conn *hci_connect_cis(struct hci_dev *hdev, bdaddr_t *dst,
|
||||
__u8 dst_type, struct bt_iso_qos *qos,
|
||||
u16 timeout);
|
||||
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
|
||||
index e352a4e0ef8d..3133f40fa9f9 100644
|
||||
--- a/include/net/bluetooth/hci_sync.h
|
||||
+++ b/include/net/bluetooth/hci_sync.h
|
||||
@@ -188,3 +188,4 @@ int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
|
||||
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
+int hci_past_sync(struct hci_conn *conn, struct hci_conn *le);
|
||||
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
|
||||
index 6fc0692abf05..4f9dc1435ccc 100644
|
||||
--- a/net/bluetooth/hci_conn.c
|
||||
+++ b/net/bluetooth/hci_conn.c
|
||||
@@ -2245,6 +2245,18 @@ struct hci_conn *hci_bind_bis(struct hci_dev *hdev, bdaddr_t *dst, __u8 sid,
|
||||
return conn;
|
||||
}
|
||||
|
||||
+int hci_past_bis(struct hci_conn *conn, bdaddr_t *dst, __u8 dst_type)
|
||||
+{
|
||||
+ struct hci_conn *le;
|
||||
+
|
||||
+ /* Lookup existing LE connection to rebind to */
|
||||
+ le = hci_conn_hash_lookup_le(conn->hdev, dst, dst_type);
|
||||
+ if (!le)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return hci_past_sync(conn, le);
|
||||
+}
|
||||
+
|
||||
static void bis_mark_per_adv(struct hci_conn *conn, void *data)
|
||||
{
|
||||
struct iso_list_data *d = data;
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index cb573b88505f..13d34c97b4d3 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -7229,3 +7229,95 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
|
||||
create_big_complete);
|
||||
}
|
||||
+
|
||||
+struct past_data {
|
||||
+ struct hci_conn *conn;
|
||||
+ struct hci_conn *le;
|
||||
+};
|
||||
+
|
||||
+static void past_complete(struct hci_dev *hdev, void *data, int err)
|
||||
+{
|
||||
+ struct past_data *past = data;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "err %d", err);
|
||||
+
|
||||
+ kfree(past);
|
||||
+}
|
||||
+
|
||||
+static int hci_le_past_set_info_sync(struct hci_dev *hdev, void *data)
|
||||
+{
|
||||
+ struct past_data *past = data;
|
||||
+ struct hci_cp_le_past_set_info cp;
|
||||
+
|
||||
+ hci_dev_lock(hdev);
|
||||
+
|
||||
+ if (!hci_conn_valid(hdev, past->conn) ||
|
||||
+ !hci_conn_valid(hdev, past->le)) {
|
||||
+ hci_dev_unlock(hdev);
|
||||
+ return -ECANCELED;
|
||||
+ }
|
||||
+
|
||||
+ memset(&cp, 0, sizeof(cp));
|
||||
+ cp.handle = cpu_to_le16(past->le->handle);
|
||||
+ cp.adv_handle = past->conn->iso_qos.bcast.bis;
|
||||
+
|
||||
+ hci_dev_unlock(hdev);
|
||||
+
|
||||
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST_SET_INFO,
|
||||
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
+}
|
||||
+
|
||||
+static int hci_le_past_sync(struct hci_dev *hdev, void *data)
|
||||
+{
|
||||
+ struct past_data *past = data;
|
||||
+ struct hci_cp_le_past cp;
|
||||
+
|
||||
+ hci_dev_lock(hdev);
|
||||
+
|
||||
+ if (!hci_conn_valid(hdev, past->conn) ||
|
||||
+ !hci_conn_valid(hdev, past->le)) {
|
||||
+ hci_dev_unlock(hdev);
|
||||
+ return -ECANCELED;
|
||||
+ }
|
||||
+
|
||||
+ memset(&cp, 0, sizeof(cp));
|
||||
+ cp.handle = cpu_to_le16(past->le->handle);
|
||||
+ cp.sync_handle = cpu_to_le16(past->conn->sync_handle);
|
||||
+
|
||||
+ hci_dev_unlock(hdev);
|
||||
+
|
||||
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_PAST,
|
||||
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
+}
|
||||
+
|
||||
+int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
||||
+{
|
||||
+ struct past_data *data;
|
||||
+ int err;
|
||||
+
|
||||
+ if (conn->type != BIS_LINK && conn->type != PA_LINK)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (!past_sender_capable(conn->hdev))
|
||||
+ return -EOPNOTSUPP;
|
||||
+
|
||||
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
|
||||
+ if (!data)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ data->conn = conn;
|
||||
+ data->le = le;
|
||||
+
|
||||
+ if (conn->role == HCI_ROLE_MASTER)
|
||||
+ err = hci_cmd_sync_queue_once(conn->hdev,
|
||||
+ hci_le_past_set_info_sync, data,
|
||||
+ past_complete);
|
||||
+ else
|
||||
+ err = hci_cmd_sync_queue_once(conn->hdev, hci_le_past_sync,
|
||||
+ data, past_complete);
|
||||
+
|
||||
+ if (err)
|
||||
+ kfree(data);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
|
||||
index 3c568d3d8ac9..607172b35267 100644
|
||||
--- a/net/bluetooth/iso.c
|
||||
+++ b/net/bluetooth/iso.c
|
||||
@@ -987,20 +987,14 @@ static int iso_sock_bind_bc(struct socket *sock, struct sockaddr *addr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa,
|
||||
+/* Must be called on the locked socket. */
|
||||
+static int iso_sock_rebind_bis(struct sock *sk, struct sockaddr_iso *sa,
|
||||
int addr_len)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
- if (sk->sk_type != SOCK_SEQPACKET) {
|
||||
- err = -EINVAL;
|
||||
- goto done;
|
||||
- }
|
||||
-
|
||||
- if (addr_len != sizeof(*sa) + sizeof(*sa->iso_bc)) {
|
||||
- err = -EINVAL;
|
||||
- goto done;
|
||||
- }
|
||||
+ if (!test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags))
|
||||
+ return -EBADFD;
|
||||
|
||||
if (sa->iso_bc->bc_num_bis > ISO_MAX_NUM_BIS) {
|
||||
err = -EINVAL;
|
||||
@@ -1023,6 +1017,77 @@ static int iso_sock_bind_pa_sk(struct sock *sk, struct sockaddr_iso *sa,
|
||||
return err;
|
||||
}
|
||||
|
||||
+static struct hci_dev *iso_conn_get_hdev(struct iso_conn *conn)
|
||||
+{
|
||||
+ struct hci_dev *hdev = NULL;
|
||||
+
|
||||
+ iso_conn_lock(conn);
|
||||
+ if (conn->hcon)
|
||||
+ hdev = hci_dev_hold(conn->hcon->hdev);
|
||||
+ iso_conn_unlock(conn);
|
||||
+
|
||||
+ return hdev;
|
||||
+}
|
||||
+
|
||||
+/* Must be called on the locked socket. */
|
||||
+static int iso_sock_rebind_bc(struct sock *sk, struct sockaddr_iso *sa,
|
||||
+ int addr_len)
|
||||
+{
|
||||
+ struct hci_dev *hdev;
|
||||
+ struct hci_conn *bis;
|
||||
+ int err;
|
||||
+
|
||||
+ if (sk->sk_type != SOCK_SEQPACKET || !iso_pi(sk)->conn)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Check if it is really a Broadcast address being requested */
|
||||
+ if (addr_len != sizeof(*sa) + sizeof(*sa->iso_bc))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ /* Check if the address hasn't changed then perhaps only the number of
|
||||
+ * bis has changed.
|
||||
+ */
|
||||
+ if (!bacmp(&iso_pi(sk)->dst, &sa->iso_bc->bc_bdaddr) ||
|
||||
+ !bacmp(&sa->iso_bc->bc_bdaddr, BDADDR_ANY))
|
||||
+ return iso_sock_rebind_bis(sk, sa, addr_len);
|
||||
+
|
||||
+ /* Check if the address type is of LE type */
|
||||
+ if (!bdaddr_type_is_le(sa->iso_bc->bc_bdaddr_type))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ hdev = iso_conn_get_hdev(iso_pi(sk)->conn);
|
||||
+ if (!hdev)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ bis = iso_pi(sk)->conn->hcon;
|
||||
+
|
||||
+ /* Release the socket before lookups since that requires hci_dev_lock
|
||||
+ * which shall not be acquired while holding sock_lock for proper
|
||||
+ * ordering.
|
||||
+ */
|
||||
+ release_sock(sk);
|
||||
+ hci_dev_lock(bis->hdev);
|
||||
+ lock_sock(sk);
|
||||
+
|
||||
+ if (!iso_pi(sk)->conn || iso_pi(sk)->conn->hcon != bis) {
|
||||
+ /* raced with iso_conn_del() or iso_disconn_sock() */
|
||||
+ err = -ENOTCONN;
|
||||
+ goto unlock;
|
||||
+ }
|
||||
+
|
||||
+ BT_DBG("sk %p %pMR type %u", sk, &sa->iso_bc->bc_bdaddr,
|
||||
+ sa->iso_bc->bc_bdaddr_type);
|
||||
+
|
||||
+ err = hci_past_bis(bis, &sa->iso_bc->bc_bdaddr,
|
||||
+ le_addr_type(sa->iso_bc->bc_bdaddr_type));
|
||||
+
|
||||
+unlock:
|
||||
+ hci_dev_unlock(hdev);
|
||||
+ hci_dev_put(hdev);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+
|
||||
static int iso_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||
int addr_len)
|
||||
{
|
||||
@@ -1038,13 +1103,12 @@ static int iso_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
- /* Allow the user to bind a PA sync socket to a number
|
||||
- * of BISes to sync to.
|
||||
- */
|
||||
- if ((sk->sk_state == BT_CONNECT2 ||
|
||||
- sk->sk_state == BT_CONNECTED) &&
|
||||
- test_bit(BT_SK_PA_SYNC, &iso_pi(sk)->flags)) {
|
||||
- err = iso_sock_bind_pa_sk(sk, sa, addr_len);
|
||||
+ if ((sk->sk_state == BT_CONNECT2 || sk->sk_state == BT_CONNECTED) &&
|
||||
+ addr_len > sizeof(*sa)) {
|
||||
+ /* Allow the user to rebind to a different address using
|
||||
+ * PAST procedures.
|
||||
+ */
|
||||
+ err = iso_sock_rebind_bc(sk, sa, addr_len);
|
||||
goto done;
|
||||
}
|
||||
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
437
1277-bluetooth-hci-add-support-for-ll-extended-feature-set.patch
Normal file
437
1277-bluetooth-hci-add-support-for-ll-extended-feature-set.patch
Normal file
@ -0,0 +1,437 @@
|
||||
From 390744e4031283550f4dd646cff099d5952d3683 Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Wed, 15 Apr 2026 17:01:26 -0500
|
||||
Subject: [PATCH] Bluetooth: HCI: Add support for LL Extended Feature Set
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit a106e50be74b0896583f4d010a69f9806e4194f4
|
||||
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
Date: Fri Nov 14 09:29:28 2025 -0500
|
||||
|
||||
Bluetooth: HCI: Add support for LL Extended Feature Set
|
||||
|
||||
This adds support for emulating LL Extended Feature Set introduced in 6.0
|
||||
that adds the following:
|
||||
|
||||
Commands:
|
||||
|
||||
- HCI_LE_Read_All_Local_Supported_Features(0x2087)(Feature:47,1)
|
||||
- HCI_LE_Read_All_Remote_Features(0x2088)(Feature:47,2)
|
||||
|
||||
Events:
|
||||
|
||||
- HCI_LE_Read_All_Remote_Features_Complete(0x2b)(Mask bit:42)
|
||||
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
|
||||
index d883ad233ebc..a27cd3626b87 100644
|
||||
--- a/include/net/bluetooth/hci.h
|
||||
+++ b/include/net/bluetooth/hci.h
|
||||
@@ -653,6 +653,7 @@ enum {
|
||||
#define HCI_LE_CIS_PERIPHERAL 0x20
|
||||
#define HCI_LE_ISO_BROADCASTER 0x40
|
||||
#define HCI_LE_ISO_SYNC_RECEIVER 0x80
|
||||
+#define HCI_LE_LL_EXT_FEATURE 0x80
|
||||
|
||||
/* Connection modes */
|
||||
#define HCI_CM_ACTIVE 0x0000
|
||||
@@ -2255,6 +2256,19 @@ struct hci_cp_le_set_host_feature {
|
||||
__u8 bit_value;
|
||||
} __packed;
|
||||
|
||||
+#define HCI_OP_LE_READ_ALL_LOCAL_FEATURES 0x2087
|
||||
+struct hci_rp_le_read_all_local_features {
|
||||
+ __u8 status;
|
||||
+ __u8 page;
|
||||
+ __u8 features[248];
|
||||
+} __packed;
|
||||
+
|
||||
+#define HCI_OP_LE_READ_ALL_REMOTE_FEATURES 0x2088
|
||||
+struct hci_cp_le_read_all_remote_features {
|
||||
+ __le16 handle;
|
||||
+ __u8 pages;
|
||||
+} __packed;
|
||||
+
|
||||
/* ---- HCI Events ---- */
|
||||
struct hci_ev_status {
|
||||
__u8 status;
|
||||
@@ -2937,6 +2951,15 @@ struct hci_evt_le_big_info_adv_report {
|
||||
__u8 encryption;
|
||||
} __packed;
|
||||
|
||||
+#define HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE 0x2b
|
||||
+struct hci_evt_le_read_all_remote_features_complete {
|
||||
+ __u8 status;
|
||||
+ __le16 handle;
|
||||
+ __u8 max_pages;
|
||||
+ __u8 valid_pages;
|
||||
+ __u8 features[248];
|
||||
+} __packed;
|
||||
+
|
||||
#define HCI_EV_VENDOR 0xff
|
||||
|
||||
/* Internal events generated by Bluetooth stack */
|
||||
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
|
||||
index 858b58206e80..4263e71a23ef 100644
|
||||
--- a/include/net/bluetooth/hci_core.h
|
||||
+++ b/include/net/bluetooth/hci_core.h
|
||||
@@ -378,7 +378,7 @@ struct hci_dev {
|
||||
__u8 minor_class;
|
||||
__u8 max_page;
|
||||
__u8 features[HCI_MAX_PAGES][8];
|
||||
- __u8 le_features[8];
|
||||
+ __u8 le_features[248];
|
||||
__u8 le_accept_list_size;
|
||||
__u8 le_resolv_list_size;
|
||||
__u8 le_num_of_adv_sets;
|
||||
@@ -702,6 +702,7 @@ struct hci_conn {
|
||||
__u8 attempt;
|
||||
__u8 dev_class[3];
|
||||
__u8 features[HCI_MAX_PAGES][8];
|
||||
+ __u8 le_features[248];
|
||||
__u16 pkt_type;
|
||||
__u16 link_policy;
|
||||
__u8 key_type;
|
||||
@@ -2067,6 +2068,8 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
|
||||
(le_enabled(dev) && past_receiver_capable(dev))
|
||||
#define past_enabled(dev) \
|
||||
(past_sender_enabled(dev) || past_receiver_enabled(dev))
|
||||
+#define ll_ext_feature_capable(dev) \
|
||||
+ ((dev)->le_features[7] & HCI_LE_LL_EXT_FEATURE)
|
||||
|
||||
#define mws_transport_config_capable(dev) (((dev)->commands[30] & 0x08) && \
|
||||
(!hci_test_quirk((dev), HCI_QUIRK_BROKEN_MWS_TRANSPORT_CONFIG)))
|
||||
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
|
||||
index 3133f40fa9f9..56076bbc981d 100644
|
||||
--- a/include/net/bluetooth/hci_sync.h
|
||||
+++ b/include/net/bluetooth/hci_sync.h
|
||||
@@ -189,3 +189,5 @@ int hci_le_conn_update_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_past_sync(struct hci_conn *conn, struct hci_conn *le);
|
||||
+
|
||||
+int hci_le_read_remote_features(struct hci_conn *conn);
|
||||
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
|
||||
index 7c4ca14f13e5..a9868f17ef40 100644
|
||||
--- a/net/bluetooth/hci_event.c
|
||||
+++ b/net/bluetooth/hci_event.c
|
||||
@@ -2886,12 +2886,8 @@ static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
- if (conn) {
|
||||
- if (conn->state == BT_CONFIG) {
|
||||
- hci_connect_cfm(conn, status);
|
||||
- hci_conn_drop(conn);
|
||||
- }
|
||||
- }
|
||||
+ if (conn && conn->state == BT_CONFIG)
|
||||
+ hci_connect_cfm(conn, status);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
@@ -3915,11 +3911,49 @@ static u8 hci_cc_le_setup_iso_path(struct hci_dev *hdev, void *data,
|
||||
return rp->status;
|
||||
}
|
||||
|
||||
+static u8 hci_cc_le_read_all_local_features(struct hci_dev *hdev, void *data,
|
||||
+ struct sk_buff *skb)
|
||||
+{
|
||||
+ struct hci_rp_le_read_all_local_features *rp = data;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "status 0x%2.2x", rp->status);
|
||||
+
|
||||
+ if (rp->status)
|
||||
+ return rp->status;
|
||||
+
|
||||
+ memcpy(hdev->le_features, rp->features, 248);
|
||||
+
|
||||
+ return rp->status;
|
||||
+}
|
||||
+
|
||||
static void hci_cs_le_create_big(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
}
|
||||
|
||||
+static void hci_cs_le_read_all_remote_features(struct hci_dev *hdev, u8 status)
|
||||
+{
|
||||
+ struct hci_cp_le_read_remote_features *cp;
|
||||
+ struct hci_conn *conn;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
+
|
||||
+ if (!status)
|
||||
+ return;
|
||||
+
|
||||
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES);
|
||||
+ if (!cp)
|
||||
+ return;
|
||||
+
|
||||
+ hci_dev_lock(hdev);
|
||||
+
|
||||
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
+ if (conn && conn->state == BT_CONFIG)
|
||||
+ hci_connect_cfm(conn, status);
|
||||
+
|
||||
+ hci_dev_unlock(hdev);
|
||||
+}
|
||||
+
|
||||
static u8 hci_cc_set_per_adv_param(struct hci_dev *hdev, void *data,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@@ -4171,6 +4205,9 @@ static const struct hci_cc {
|
||||
sizeof(struct hci_rp_le_set_cig_params), HCI_MAX_EVENT_SIZE),
|
||||
HCI_CC(HCI_OP_LE_SETUP_ISO_PATH, hci_cc_le_setup_iso_path,
|
||||
sizeof(struct hci_rp_le_setup_iso_path)),
|
||||
+ HCI_CC(HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
|
||||
+ hci_cc_le_read_all_local_features,
|
||||
+ sizeof(struct hci_rp_le_read_all_local_features)),
|
||||
};
|
||||
|
||||
static u8 hci_cc_func(struct hci_dev *hdev, const struct hci_cc *cc,
|
||||
@@ -4325,6 +4362,8 @@ static const struct hci_cs {
|
||||
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
|
||||
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
|
||||
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
|
||||
+ HCI_CS(HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
|
||||
+ hci_cs_le_read_all_remote_features),
|
||||
};
|
||||
|
||||
static void hci_cmd_status_evt(struct hci_dev *hdev, void *data,
|
||||
@@ -5645,6 +5684,7 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||
struct hci_conn *conn;
|
||||
struct smp_irk *irk;
|
||||
u8 addr_type;
|
||||
+ int err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
@@ -5775,26 +5815,8 @@ static void le_conn_complete_evt(struct hci_dev *hdev, u8 status,
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
|
||||
- /* The remote features procedure is defined for central
|
||||
- * role only. So only in case of an initiated connection
|
||||
- * request the remote features.
|
||||
- *
|
||||
- * If the local controller supports peripheral-initiated features
|
||||
- * exchange, then requesting the remote features in peripheral
|
||||
- * role is possible. Otherwise just transition into the
|
||||
- * connected state without requesting the remote features.
|
||||
- */
|
||||
- if (conn->out ||
|
||||
- (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
|
||||
- struct hci_cp_le_read_remote_features cp;
|
||||
-
|
||||
- cp.handle = __cpu_to_le16(conn->handle);
|
||||
-
|
||||
- hci_send_cmd(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
|
||||
- sizeof(cp), &cp);
|
||||
-
|
||||
- hci_conn_hold(conn);
|
||||
- } else {
|
||||
+ err = hci_le_read_remote_features(conn);
|
||||
+ if (err) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_connect_cfm(conn, status);
|
||||
}
|
||||
@@ -6608,7 +6630,6 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_connect_cfm(conn, status);
|
||||
- hci_conn_drop(conn);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7186,6 +7207,50 @@ static void hci_le_big_info_adv_report_evt(struct hci_dev *hdev, void *data,
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
+static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
|
||||
+ void *data, struct sk_buff *skb)
|
||||
+{
|
||||
+ struct hci_evt_le_read_all_remote_features_complete *ev = data;
|
||||
+ struct hci_conn *conn;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "status 0x%2.2x", ev->status);
|
||||
+
|
||||
+ hci_dev_lock(hdev);
|
||||
+
|
||||
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
+ if (!conn)
|
||||
+ goto unlock;
|
||||
+
|
||||
+ if (!ev->status)
|
||||
+ memcpy(conn->le_features, ev->features, 248);
|
||||
+
|
||||
+ if (conn->state == BT_CONFIG) {
|
||||
+ __u8 status;
|
||||
+
|
||||
+ /* If the local controller supports peripheral-initiated
|
||||
+ * features exchange, but the remote controller does
|
||||
+ * not, then it is possible that the error code 0x1a
|
||||
+ * for unsupported remote feature gets returned.
|
||||
+ *
|
||||
+ * In this specific case, allow the connection to
|
||||
+ * transition into connected state and mark it as
|
||||
+ * successful.
|
||||
+ */
|
||||
+ if (!conn->out &&
|
||||
+ ev->status == HCI_ERROR_UNSUPPORTED_REMOTE_FEATURE &&
|
||||
+ (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
|
||||
+ status = 0x00;
|
||||
+ else
|
||||
+ status = ev->status;
|
||||
+
|
||||
+ conn->state = BT_CONNECTED;
|
||||
+ hci_connect_cfm(conn, status);
|
||||
+ }
|
||||
+
|
||||
+unlock:
|
||||
+ hci_dev_unlock(hdev);
|
||||
+}
|
||||
+
|
||||
#define HCI_LE_EV_VL(_op, _func, _min_len, _max_len) \
|
||||
[_op] = { \
|
||||
.func = _func, \
|
||||
@@ -7291,6 +7356,12 @@ static const struct hci_le_ev {
|
||||
hci_le_big_info_adv_report_evt,
|
||||
sizeof(struct hci_evt_le_big_info_adv_report),
|
||||
HCI_MAX_EVENT_SIZE),
|
||||
+ /* [0x2b = HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE] */
|
||||
+ HCI_LE_EV_VL(HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
|
||||
+ hci_le_read_all_remote_features_evt,
|
||||
+ sizeof(struct
|
||||
+ hci_evt_le_read_all_remote_features_complete),
|
||||
+ HCI_MAX_EVENT_SIZE),
|
||||
};
|
||||
|
||||
static void hci_le_meta_evt(struct hci_dev *hdev, void *data,
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index 742ac6ab9755..13b2e9608fdb 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -4011,8 +4011,19 @@ static int hci_le_read_buffer_size_sync(struct hci_dev *hdev)
|
||||
/* Read LE Local Supported Features */
|
||||
static int hci_le_read_local_features_sync(struct hci_dev *hdev)
|
||||
{
|
||||
- return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
|
||||
- 0, NULL, HCI_CMD_TIMEOUT);
|
||||
+ int err;
|
||||
+
|
||||
+ err = __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_LOCAL_FEATURES,
|
||||
+ 0, NULL, HCI_CMD_TIMEOUT);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(2))
|
||||
+ return __hci_cmd_sync_status(hdev,
|
||||
+ HCI_OP_LE_READ_ALL_LOCAL_FEATURES,
|
||||
+ 0, NULL, HCI_CMD_TIMEOUT);
|
||||
+
|
||||
+ return err;
|
||||
}
|
||||
|
||||
/* Read LE Supported States */
|
||||
@@ -7321,3 +7332,90 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
||||
|
||||
return err;
|
||||
}
|
||||
+
|
||||
+static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
|
||||
+{
|
||||
+ struct hci_conn *conn = data;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "err %d", err);
|
||||
+
|
||||
+ if (err == -ECANCELED)
|
||||
+ return;
|
||||
+
|
||||
+ hci_conn_drop(conn);
|
||||
+}
|
||||
+
|
||||
+static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev,
|
||||
+ void *data)
|
||||
+{
|
||||
+ struct hci_conn *conn = data;
|
||||
+ struct hci_cp_le_read_all_remote_features cp;
|
||||
+
|
||||
+ memset(&cp, 0, sizeof(cp));
|
||||
+ cp.handle = cpu_to_le16(conn->handle);
|
||||
+ cp.pages = 10; /* Attempt to read all pages */
|
||||
+
|
||||
+ /* Wait for HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE event otherwise
|
||||
+ * hci_conn_drop may run prematurely causing a disconnection.
|
||||
+ */
|
||||
+ return __hci_cmd_sync_status_sk(hdev,
|
||||
+ HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
|
||||
+ sizeof(cp), &cp,
|
||||
+ HCI_EVT_LE_ALL_REMOTE_FEATURES_COMPLETE,
|
||||
+ HCI_CMD_TIMEOUT, NULL);
|
||||
+
|
||||
+ return __hci_cmd_sync_status(hdev, HCI_OP_LE_READ_ALL_REMOTE_FEATURES,
|
||||
+ sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||
+}
|
||||
+
|
||||
+static int hci_le_read_remote_features_sync(struct hci_dev *hdev, void *data)
|
||||
+{
|
||||
+ struct hci_conn *conn = data;
|
||||
+ struct hci_cp_le_read_remote_features cp;
|
||||
+
|
||||
+ if (!hci_conn_valid(hdev, conn))
|
||||
+ return -ECANCELED;
|
||||
+
|
||||
+ /* Check if LL Extended Feature Set is supported and
|
||||
+ * HCI_OP_LE_READ_ALL_REMOTE_FEATURES is supported then use that to read
|
||||
+ * all features.
|
||||
+ */
|
||||
+ if (ll_ext_feature_capable(hdev) && hdev->commands[47] & BIT(3))
|
||||
+ return hci_le_read_all_remote_features_sync(hdev, data);
|
||||
+
|
||||
+ memset(&cp, 0, sizeof(cp));
|
||||
+ cp.handle = cpu_to_le16(conn->handle);
|
||||
+
|
||||
+ /* Wait for HCI_EV_LE_REMOTE_FEAT_COMPLETE event otherwise
|
||||
+ * hci_conn_drop may run prematurely causing a disconnection.
|
||||
+ */
|
||||
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_READ_REMOTE_FEATURES,
|
||||
+ sizeof(cp), &cp,
|
||||
+ HCI_EV_LE_REMOTE_FEAT_COMPLETE,
|
||||
+ HCI_CMD_TIMEOUT, NULL);
|
||||
+}
|
||||
+
|
||||
+int hci_le_read_remote_features(struct hci_conn *conn)
|
||||
+{
|
||||
+ struct hci_dev *hdev = conn->hdev;
|
||||
+ int err;
|
||||
+
|
||||
+ /* The remote features procedure is defined for central
|
||||
+ * role only. So only in case of an initiated connection
|
||||
+ * request the remote features.
|
||||
+ *
|
||||
+ * If the local controller supports peripheral-initiated features
|
||||
+ * exchange, then requesting the remote features in peripheral
|
||||
+ * role is possible. Otherwise just transition into the
|
||||
+ * connected state without requesting the remote features.
|
||||
+ */
|
||||
+ if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
|
||||
+ err = hci_cmd_sync_queue_once(hdev,
|
||||
+ hci_le_read_remote_features_sync,
|
||||
+ hci_conn_hold(conn),
|
||||
+ le_read_features_complete);
|
||||
+ else
|
||||
+ err = -EOPNOTSUPP;
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,136 @@
|
||||
From 8c647364b6263e4d076a7c96f55989b5c0be9598 Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Wed, 15 Apr 2026 19:43:05 -0500
|
||||
Subject: [PATCH] Bluetooth: hci_conn: Fix using conn->le_{tx,rx}_phy as
|
||||
supported PHYs
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit 129d1ef3c5e60d51678e6359beaba85771a49e46
|
||||
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
Date: Wed Dec 10 11:38:08 2025 -0500
|
||||
|
||||
Bluetooth: hci_conn: Fix using conn->le_{tx,rx}_phy as supported PHYs
|
||||
|
||||
conn->le_{tx,rx}_phy is not actually a bitfield as it set by
|
||||
HCI_EV_LE_PHY_UPDATE_COMPLETE it is actually correspond to the current
|
||||
PHY in use not what is supported by the controller, so this introduces
|
||||
different fields (conn->le_{tx,rx}_def_phys) to track what PHYs are
|
||||
supported by the connection.
|
||||
|
||||
Fixes: eab2404ba798 ("Bluetooth: Add BT_PHY socket option")
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
|
||||
index 4263e71a23ef..8aadf4cdead2 100644
|
||||
--- a/include/net/bluetooth/hci_core.h
|
||||
+++ b/include/net/bluetooth/hci_core.h
|
||||
@@ -730,6 +730,8 @@ struct hci_conn {
|
||||
__u16 le_per_adv_data_offset;
|
||||
__u8 le_adv_phy;
|
||||
__u8 le_adv_sec_phy;
|
||||
+ __u8 le_tx_def_phys;
|
||||
+ __u8 le_rx_def_phys;
|
||||
__u8 le_tx_phy;
|
||||
__u8 le_rx_phy;
|
||||
__s8 rssi;
|
||||
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
|
||||
index c3f7828bf9d5..5a4374ccf8e8 100644
|
||||
--- a/net/bluetooth/hci_conn.c
|
||||
+++ b/net/bluetooth/hci_conn.c
|
||||
@@ -1008,6 +1008,11 @@ static struct hci_conn *__hci_conn_add(struct hci_dev *hdev, int type,
|
||||
/* conn->src should reflect the local identity address */
|
||||
hci_copy_identity_address(hdev, &conn->src, &conn->src_type);
|
||||
conn->mtu = hdev->le_mtu ? hdev->le_mtu : hdev->acl_mtu;
|
||||
+ /* Use the controller supported PHYS as default until the
|
||||
+ * remote features are resolved.
|
||||
+ */
|
||||
+ conn->le_tx_def_phys = hdev->le_tx_def_phys;
|
||||
+ conn->le_rx_def_phys = hdev->le_tx_def_phys;
|
||||
break;
|
||||
case CIS_LINK:
|
||||
/* conn->src should reflect the local identity address */
|
||||
@@ -2928,22 +2933,22 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
|
||||
break;
|
||||
|
||||
case LE_LINK:
|
||||
- if (conn->le_tx_phy & HCI_LE_SET_PHY_1M)
|
||||
+ if (conn->le_tx_def_phys & HCI_LE_SET_PHY_1M)
|
||||
phys |= BT_PHY_LE_1M_TX;
|
||||
|
||||
- if (conn->le_rx_phy & HCI_LE_SET_PHY_1M)
|
||||
+ if (conn->le_rx_def_phys & HCI_LE_SET_PHY_1M)
|
||||
phys |= BT_PHY_LE_1M_RX;
|
||||
|
||||
- if (conn->le_tx_phy & HCI_LE_SET_PHY_2M)
|
||||
+ if (conn->le_tx_def_phys & HCI_LE_SET_PHY_2M)
|
||||
phys |= BT_PHY_LE_2M_TX;
|
||||
|
||||
- if (conn->le_rx_phy & HCI_LE_SET_PHY_2M)
|
||||
+ if (conn->le_rx_def_phys & HCI_LE_SET_PHY_2M)
|
||||
phys |= BT_PHY_LE_2M_RX;
|
||||
|
||||
- if (conn->le_tx_phy & HCI_LE_SET_PHY_CODED)
|
||||
+ if (conn->le_tx_def_phys & HCI_LE_SET_PHY_CODED)
|
||||
phys |= BT_PHY_LE_CODED_TX;
|
||||
|
||||
- if (conn->le_rx_phy & HCI_LE_SET_PHY_CODED)
|
||||
+ if (conn->le_rx_def_phys & HCI_LE_SET_PHY_CODED)
|
||||
phys |= BT_PHY_LE_CODED_RX;
|
||||
|
||||
break;
|
||||
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
|
||||
index a9868f17ef40..58075bf72055 100644
|
||||
--- a/net/bluetooth/hci_event.c
|
||||
+++ b/net/bluetooth/hci_event.c
|
||||
@@ -6607,8 +6607,20 @@ static void hci_le_remote_feat_complete_evt(struct hci_dev *hdev, void *data,
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle));
|
||||
if (conn) {
|
||||
- if (!ev->status)
|
||||
- memcpy(conn->features[0], ev->features, 8);
|
||||
+ if (!ev->status) {
|
||||
+ memcpy(conn->le_features, ev->features, 8);
|
||||
+
|
||||
+ /* Update supported PHYs */
|
||||
+ if (!(conn->le_features[1] & HCI_LE_PHY_2M)) {
|
||||
+ conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M;
|
||||
+ conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M;
|
||||
+ }
|
||||
+
|
||||
+ if (!(conn->le_features[1] & HCI_LE_PHY_CODED)) {
|
||||
+ conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED;
|
||||
+ conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
__u8 status;
|
||||
@@ -7221,9 +7233,21 @@ static void hci_le_read_all_remote_features_evt(struct hci_dev *hdev,
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
- if (!ev->status)
|
||||
+ if (!ev->status) {
|
||||
memcpy(conn->le_features, ev->features, 248);
|
||||
|
||||
+ /* Update supported PHYs */
|
||||
+ if (!(conn->le_features[1] & HCI_LE_PHY_2M)) {
|
||||
+ conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_2M;
|
||||
+ conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_2M;
|
||||
+ }
|
||||
+
|
||||
+ if (!(conn->le_features[1] & HCI_LE_PHY_CODED)) {
|
||||
+ conn->le_tx_def_phys &= ~HCI_LE_SET_PHY_CODED;
|
||||
+ conn->le_rx_def_phys &= ~HCI_LE_SET_PHY_CODED;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if (conn->state == BT_CONFIG) {
|
||||
__u8 status;
|
||||
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
442
1279-bluetooth-l2cap-add-support-for-setting-bt-phy.patch
Normal file
442
1279-bluetooth-l2cap-add-support-for-setting-bt-phy.patch
Normal file
@ -0,0 +1,442 @@
|
||||
From 81102db981020f3c766c0e7c88bdd2bf7d12ea30 Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Wed, 15 Apr 2026 22:50:00 -0500
|
||||
Subject: [PATCH] Bluetooth: L2CAP: Add support for setting BT_PHY
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit 132c0779d4a2d08541519cf04783bca52c6ec85c
|
||||
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
Date: Wed Dec 17 10:50:51 2025 -0500
|
||||
|
||||
Bluetooth: L2CAP: Add support for setting BT_PHY
|
||||
|
||||
This enables client to use setsockopt(BT_PHY) to set the connection
|
||||
packet type/PHY:
|
||||
|
||||
Example setting BT_PHY_BR_1M_1SLOT:
|
||||
|
||||
< HCI Command: Change Conne.. (0x01|0x000f) plen 4
|
||||
Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation)
|
||||
Packet type: 0x331e
|
||||
2-DH1 may not be used
|
||||
3-DH1 may not be used
|
||||
DM1 may be used
|
||||
DH1 may be used
|
||||
2-DH3 may not be used
|
||||
3-DH3 may not be used
|
||||
2-DH5 may not be used
|
||||
3-DH5 may not be used
|
||||
> HCI Event: Command Status (0x0f) plen 4
|
||||
Change Connection Packet Type (0x01|0x000f) ncmd 1
|
||||
Status: Success (0x00)
|
||||
> HCI Event: Connection Packet Typ.. (0x1d) plen 5
|
||||
Status: Success (0x00)
|
||||
Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation)
|
||||
Packet type: 0x331e
|
||||
2-DH1 may not be used
|
||||
3-DH1 may not be used
|
||||
DM1 may be used
|
||||
DH1 may be used
|
||||
2-DH3 may not be used
|
||||
3-DH3 may not be used
|
||||
2-DH5 may not be used
|
||||
|
||||
Example setting BT_PHY_LE_1M_TX and BT_PHY_LE_1M_RX:
|
||||
|
||||
< HCI Command: LE Set PHY (0x08|0x0032) plen 7
|
||||
Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation)
|
||||
All PHYs preference: 0x00
|
||||
TX PHYs preference: 0x01
|
||||
LE 1M
|
||||
RX PHYs preference: 0x01
|
||||
LE 1M
|
||||
PHY options preference: Reserved (0x0000)
|
||||
> HCI Event: Command Status (0x0f) plen 4
|
||||
LE Set PHY (0x08|0x0032) ncmd 1
|
||||
Status: Success (0x00)
|
||||
> HCI Event: LE Meta Event (0x3e) plen 6
|
||||
LE PHY Update Complete (0x0c)
|
||||
Status: Success (0x00)
|
||||
Handle: 1 Address: 00:AA:01:01:00:00 (Intel Corporation)
|
||||
TX PHY: LE 1M (0x01)
|
||||
RX PHY: LE 1M (0x01)
|
||||
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
|
||||
index d46ed9011ee5..89a60919050b 100644
|
||||
--- a/include/net/bluetooth/bluetooth.h
|
||||
+++ b/include/net/bluetooth/bluetooth.h
|
||||
@@ -130,21 +130,30 @@ struct bt_voice {
|
||||
#define BT_RCVMTU 13
|
||||
#define BT_PHY 14
|
||||
|
||||
-#define BT_PHY_BR_1M_1SLOT 0x00000001
|
||||
-#define BT_PHY_BR_1M_3SLOT 0x00000002
|
||||
-#define BT_PHY_BR_1M_5SLOT 0x00000004
|
||||
-#define BT_PHY_EDR_2M_1SLOT 0x00000008
|
||||
-#define BT_PHY_EDR_2M_3SLOT 0x00000010
|
||||
-#define BT_PHY_EDR_2M_5SLOT 0x00000020
|
||||
-#define BT_PHY_EDR_3M_1SLOT 0x00000040
|
||||
-#define BT_PHY_EDR_3M_3SLOT 0x00000080
|
||||
-#define BT_PHY_EDR_3M_5SLOT 0x00000100
|
||||
-#define BT_PHY_LE_1M_TX 0x00000200
|
||||
-#define BT_PHY_LE_1M_RX 0x00000400
|
||||
-#define BT_PHY_LE_2M_TX 0x00000800
|
||||
-#define BT_PHY_LE_2M_RX 0x00001000
|
||||
-#define BT_PHY_LE_CODED_TX 0x00002000
|
||||
-#define BT_PHY_LE_CODED_RX 0x00004000
|
||||
+#define BT_PHY_BR_1M_1SLOT BIT(0)
|
||||
+#define BT_PHY_BR_1M_3SLOT BIT(1)
|
||||
+#define BT_PHY_BR_1M_5SLOT BIT(2)
|
||||
+#define BT_PHY_EDR_2M_1SLOT BIT(3)
|
||||
+#define BT_PHY_EDR_2M_3SLOT BIT(4)
|
||||
+#define BT_PHY_EDR_2M_5SLOT BIT(5)
|
||||
+#define BT_PHY_EDR_3M_1SLOT BIT(6)
|
||||
+#define BT_PHY_EDR_3M_3SLOT BIT(7)
|
||||
+#define BT_PHY_EDR_3M_5SLOT BIT(8)
|
||||
+#define BT_PHY_LE_1M_TX BIT(9)
|
||||
+#define BT_PHY_LE_1M_RX BIT(10)
|
||||
+#define BT_PHY_LE_2M_TX BIT(11)
|
||||
+#define BT_PHY_LE_2M_RX BIT(12)
|
||||
+#define BT_PHY_LE_CODED_TX BIT(13)
|
||||
+#define BT_PHY_LE_CODED_RX BIT(14)
|
||||
+
|
||||
+#define BT_PHY_BREDR_MASK (BT_PHY_BR_1M_1SLOT | BT_PHY_BR_1M_3SLOT | \
|
||||
+ BT_PHY_BR_1M_5SLOT | BT_PHY_EDR_2M_1SLOT | \
|
||||
+ BT_PHY_EDR_2M_3SLOT | BT_PHY_EDR_2M_5SLOT | \
|
||||
+ BT_PHY_EDR_3M_1SLOT | BT_PHY_EDR_3M_3SLOT | \
|
||||
+ BT_PHY_EDR_3M_5SLOT)
|
||||
+#define BT_PHY_LE_MASK (BT_PHY_LE_1M_TX | BT_PHY_LE_1M_RX | \
|
||||
+ BT_PHY_LE_2M_TX | BT_PHY_LE_2M_RX | \
|
||||
+ BT_PHY_LE_CODED_TX | BT_PHY_LE_CODED_RX)
|
||||
|
||||
#define BT_MODE 15
|
||||
|
||||
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
|
||||
index 4a731e1bec53..db76c2d1eeaa 100644
|
||||
--- a/include/net/bluetooth/hci.h
|
||||
+++ b/include/net/bluetooth/hci.h
|
||||
@@ -1885,6 +1885,15 @@ struct hci_cp_le_set_default_phy {
|
||||
#define HCI_LE_SET_PHY_2M 0x02
|
||||
#define HCI_LE_SET_PHY_CODED 0x04
|
||||
|
||||
+#define HCI_OP_LE_SET_PHY 0x2032
|
||||
+struct hci_cp_le_set_phy {
|
||||
+ __le16 handle;
|
||||
+ __u8 all_phys;
|
||||
+ __u8 tx_phys;
|
||||
+ __u8 rx_phys;
|
||||
+ __le16 phy_opts;
|
||||
+} __packed;
|
||||
+
|
||||
#define HCI_OP_LE_SET_EXT_SCAN_PARAMS 0x2041
|
||||
struct hci_cp_le_set_ext_scan_params {
|
||||
__u8 own_addr_type;
|
||||
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
|
||||
index a7d5beb01b69..a7bffb908c1e 100644
|
||||
--- a/include/net/bluetooth/hci_core.h
|
||||
+++ b/include/net/bluetooth/hci_core.h
|
||||
@@ -2342,6 +2342,7 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
|
||||
void *hci_recv_event_data(struct hci_dev *hdev, __u8 event);
|
||||
|
||||
u32 hci_conn_get_phy(struct hci_conn *conn);
|
||||
+int hci_conn_set_phy(struct hci_conn *conn, u32 phys);
|
||||
|
||||
/* ----- HCI Sockets ----- */
|
||||
void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
diff --git a/include/net/bluetooth/hci_sync.h b/include/net/bluetooth/hci_sync.h
|
||||
index 56076bbc981d..73e494b2591d 100644
|
||||
--- a/include/net/bluetooth/hci_sync.h
|
||||
+++ b/include/net/bluetooth/hci_sync.h
|
||||
@@ -191,3 +191,6 @@ int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||
int hci_past_sync(struct hci_conn *conn, struct hci_conn *le);
|
||||
|
||||
int hci_le_read_remote_features(struct hci_conn *conn);
|
||||
+
|
||||
+int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type);
|
||||
+int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys);
|
||||
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
|
||||
index 0f512c2c2fd3..48aaccd35954 100644
|
||||
--- a/net/bluetooth/hci_conn.c
|
||||
+++ b/net/bluetooth/hci_conn.c
|
||||
@@ -2958,6 +2958,111 @@ u32 hci_conn_get_phy(struct hci_conn *conn)
|
||||
return phys;
|
||||
}
|
||||
|
||||
+static u16 bt_phy_pkt_type(struct hci_conn *conn, u32 phys)
|
||||
+{
|
||||
+ u16 pkt_type = conn->pkt_type;
|
||||
+
|
||||
+ if (phys & BT_PHY_BR_1M_3SLOT)
|
||||
+ pkt_type |= HCI_DM3 | HCI_DH3;
|
||||
+ else
|
||||
+ pkt_type &= ~(HCI_DM3 | HCI_DH3);
|
||||
+
|
||||
+ if (phys & BT_PHY_BR_1M_5SLOT)
|
||||
+ pkt_type |= HCI_DM5 | HCI_DH5;
|
||||
+ else
|
||||
+ pkt_type &= ~(HCI_DM5 | HCI_DH5);
|
||||
+
|
||||
+ if (phys & BT_PHY_EDR_2M_1SLOT)
|
||||
+ pkt_type &= ~HCI_2DH1;
|
||||
+ else
|
||||
+ pkt_type |= HCI_2DH1;
|
||||
+
|
||||
+ if (phys & BT_PHY_EDR_2M_3SLOT)
|
||||
+ pkt_type &= ~HCI_2DH3;
|
||||
+ else
|
||||
+ pkt_type |= HCI_2DH3;
|
||||
+
|
||||
+ if (phys & BT_PHY_EDR_2M_5SLOT)
|
||||
+ pkt_type &= ~HCI_2DH5;
|
||||
+ else
|
||||
+ pkt_type |= HCI_2DH5;
|
||||
+
|
||||
+ if (phys & BT_PHY_EDR_3M_1SLOT)
|
||||
+ pkt_type &= ~HCI_3DH1;
|
||||
+ else
|
||||
+ pkt_type |= HCI_3DH1;
|
||||
+
|
||||
+ if (phys & BT_PHY_EDR_3M_3SLOT)
|
||||
+ pkt_type &= ~HCI_3DH3;
|
||||
+ else
|
||||
+ pkt_type |= HCI_3DH3;
|
||||
+
|
||||
+ if (phys & BT_PHY_EDR_3M_5SLOT)
|
||||
+ pkt_type &= ~HCI_3DH5;
|
||||
+ else
|
||||
+ pkt_type |= HCI_3DH5;
|
||||
+
|
||||
+ return pkt_type;
|
||||
+}
|
||||
+
|
||||
+static int bt_phy_le_phy(u32 phys, u8 *tx_phys, u8 *rx_phys)
|
||||
+{
|
||||
+ if (!tx_phys || !rx_phys)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *tx_phys = 0;
|
||||
+ *rx_phys = 0;
|
||||
+
|
||||
+ if (phys & BT_PHY_LE_1M_TX)
|
||||
+ *tx_phys |= HCI_LE_SET_PHY_1M;
|
||||
+
|
||||
+ if (phys & BT_PHY_LE_1M_RX)
|
||||
+ *rx_phys |= HCI_LE_SET_PHY_1M;
|
||||
+
|
||||
+ if (phys & BT_PHY_LE_2M_TX)
|
||||
+ *tx_phys |= HCI_LE_SET_PHY_2M;
|
||||
+
|
||||
+ if (phys & BT_PHY_LE_2M_RX)
|
||||
+ *rx_phys |= HCI_LE_SET_PHY_2M;
|
||||
+
|
||||
+ if (phys & BT_PHY_LE_CODED_TX)
|
||||
+ *tx_phys |= HCI_LE_SET_PHY_CODED;
|
||||
+
|
||||
+ if (phys & BT_PHY_LE_CODED_RX)
|
||||
+ *rx_phys |= HCI_LE_SET_PHY_CODED;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+int hci_conn_set_phy(struct hci_conn *conn, u32 phys)
|
||||
+{
|
||||
+ u8 tx_phys, rx_phys;
|
||||
+
|
||||
+ switch (conn->type) {
|
||||
+ case SCO_LINK:
|
||||
+ case ESCO_LINK:
|
||||
+ return -EINVAL;
|
||||
+ case ACL_LINK:
|
||||
+ /* Only allow setting BR/EDR PHYs if link type is ACL */
|
||||
+ if (phys & ~BT_PHY_BREDR_MASK)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return hci_acl_change_pkt_type(conn,
|
||||
+ bt_phy_pkt_type(conn, phys));
|
||||
+ case LE_LINK:
|
||||
+ /* Only allow setting LE PHYs if link type is LE */
|
||||
+ if (phys & ~BT_PHY_LE_MASK)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ if (bt_phy_le_phy(phys, &tx_phys, &rx_phys))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return hci_le_set_phy(conn, tx_phys, rx_phys);
|
||||
+ default:
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static int abort_conn_sync(struct hci_dev *hdev, void *data)
|
||||
{
|
||||
struct hci_conn *conn = data;
|
||||
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
|
||||
index 58075bf72055..467710a42d45 100644
|
||||
--- a/net/bluetooth/hci_event.c
|
||||
+++ b/net/bluetooth/hci_event.c
|
||||
@@ -2869,6 +2869,31 @@ static void hci_cs_le_ext_create_conn(struct hci_dev *hdev, u8 status)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
+static void hci_cs_le_set_phy(struct hci_dev *hdev, u8 status)
|
||||
+{
|
||||
+ struct hci_cp_le_set_phy *cp;
|
||||
+ struct hci_conn *conn;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "status 0x%2.2x", status);
|
||||
+
|
||||
+ if (status)
|
||||
+ return;
|
||||
+
|
||||
+ cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_PHY);
|
||||
+ if (!cp)
|
||||
+ return;
|
||||
+
|
||||
+ hci_dev_lock(hdev);
|
||||
+
|
||||
+ conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(cp->handle));
|
||||
+ if (conn) {
|
||||
+ conn->le_tx_def_phys = cp->tx_phys;
|
||||
+ conn->le_rx_def_phys = cp->rx_phys;
|
||||
+ }
|
||||
+
|
||||
+ hci_dev_unlock(hdev);
|
||||
+}
|
||||
+
|
||||
static void hci_cs_le_read_remote_features(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct hci_cp_le_read_remote_features *cp;
|
||||
@@ -4359,6 +4384,7 @@ static const struct hci_cs {
|
||||
HCI_CS(HCI_OP_LE_CREATE_CONN, hci_cs_le_create_conn),
|
||||
HCI_CS(HCI_OP_LE_READ_REMOTE_FEATURES, hci_cs_le_read_remote_features),
|
||||
HCI_CS(HCI_OP_LE_START_ENC, hci_cs_le_start_enc),
|
||||
+ HCI_CS(HCI_OP_LE_SET_PHY, hci_cs_le_set_phy),
|
||||
HCI_CS(HCI_OP_LE_EXT_CREATE_CONN, hci_cs_le_ext_create_conn),
|
||||
HCI_CS(HCI_OP_LE_CREATE_CIS, hci_cs_le_create_cis),
|
||||
HCI_CS(HCI_OP_LE_CREATE_BIG, hci_cs_le_create_big),
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index c5356a6a3588..4f7f15544393 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -7448,3 +7448,75 @@ int hci_le_read_remote_features(struct hci_conn *conn)
|
||||
|
||||
return err;
|
||||
}
|
||||
+
|
||||
+static void pkt_type_changed(struct hci_dev *hdev, void *data, int err)
|
||||
+{
|
||||
+ struct hci_cp_change_conn_ptype *cp = data;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "err %d", err);
|
||||
+
|
||||
+ kfree(cp);
|
||||
+}
|
||||
+
|
||||
+static int hci_change_conn_ptype_sync(struct hci_dev *hdev, void *data)
|
||||
+{
|
||||
+ struct hci_cp_change_conn_ptype *cp = data;
|
||||
+
|
||||
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_CHANGE_CONN_PTYPE,
|
||||
+ sizeof(*cp), cp,
|
||||
+ HCI_EV_PKT_TYPE_CHANGE,
|
||||
+ HCI_CMD_TIMEOUT, NULL);
|
||||
+}
|
||||
+
|
||||
+int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
|
||||
+{
|
||||
+ struct hci_dev *hdev = conn->hdev;
|
||||
+ struct hci_cp_change_conn_ptype *cp;
|
||||
+
|
||||
+ cp = kmalloc(sizeof(*cp), GFP_KERNEL);
|
||||
+ if (!cp)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ cp->handle = cpu_to_le16(conn->handle);
|
||||
+ cp->pkt_type = cpu_to_le16(pkt_type);
|
||||
+
|
||||
+ return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
|
||||
+ pkt_type_changed);
|
||||
+}
|
||||
+
|
||||
+static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err)
|
||||
+{
|
||||
+ struct hci_cp_le_set_phy *cp = data;
|
||||
+
|
||||
+ bt_dev_dbg(hdev, "err %d", err);
|
||||
+
|
||||
+ kfree(cp);
|
||||
+}
|
||||
+
|
||||
+static int hci_le_set_phy_sync(struct hci_dev *hdev, void *data)
|
||||
+{
|
||||
+ struct hci_cp_le_set_phy *cp = data;
|
||||
+
|
||||
+ return __hci_cmd_sync_status_sk(hdev, HCI_OP_LE_SET_PHY,
|
||||
+ sizeof(*cp), cp,
|
||||
+ HCI_EV_LE_PHY_UPDATE_COMPLETE,
|
||||
+ HCI_CMD_TIMEOUT, NULL);
|
||||
+}
|
||||
+
|
||||
+int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
|
||||
+{
|
||||
+ struct hci_dev *hdev = conn->hdev;
|
||||
+ struct hci_cp_le_set_phy *cp;
|
||||
+
|
||||
+ cp = kmalloc(sizeof(*cp), GFP_KERNEL);
|
||||
+ if (!cp)
|
||||
+ return -ENOMEM;
|
||||
+
|
||||
+ memset(cp, 0, sizeof(*cp));
|
||||
+ cp->handle = cpu_to_le16(conn->handle);
|
||||
+ cp->tx_phys = tx_phys;
|
||||
+ cp->rx_phys = rx_phys;
|
||||
+
|
||||
+ return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
|
||||
+ le_phy_update_complete);
|
||||
+}
|
||||
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
|
||||
index bca8f9b62fac..82dab62616d0 100644
|
||||
--- a/net/bluetooth/l2cap_sock.c
|
||||
+++ b/net/bluetooth/l2cap_sock.c
|
||||
@@ -885,7 +885,7 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
struct bt_power pwr;
|
||||
struct l2cap_conn *conn;
|
||||
int err = 0;
|
||||
- u32 opt;
|
||||
+ u32 opt, phys;
|
||||
u16 mtu;
|
||||
u8 mode;
|
||||
|
||||
@@ -1066,6 +1066,24 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname,
|
||||
|
||||
break;
|
||||
|
||||
+ case BT_PHY:
|
||||
+ if (sk->sk_state != BT_CONNECTED) {
|
||||
+ err = -ENOTCONN;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ err = copy_safe_from_sockptr(&phys, sizeof(phys), optval,
|
||||
+ optlen);
|
||||
+ if (err)
|
||||
+ break;
|
||||
+
|
||||
+ if (!chan->conn)
|
||||
+ break;
|
||||
+
|
||||
+ conn = chan->conn;
|
||||
+ err = hci_conn_set_phy(conn->hcon, phys);
|
||||
+ break;
|
||||
+
|
||||
case BT_MODE:
|
||||
if (!enable_ecred) {
|
||||
err = -ENOPROTOOPT;
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,180 @@
|
||||
From 73744f2131d220b7c5f7571863c45381764f4559 Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Wed, 15 Apr 2026 23:57:05 -0500
|
||||
Subject: [PATCH] Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST
|
||||
if exists
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit 2969554bcfccb5c609f6b6cd4a014933f3a66dd0
|
||||
Author: Pauli Virtanen <pav@iki.fi>
|
||||
Date: Wed Mar 25 21:07:43 2026 +0200
|
||||
|
||||
Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists
|
||||
|
||||
hci_cmd_sync_queue_once() needs to indicate whether a queue item was
|
||||
added, so caller can know if callbacks are called, so it can avoid
|
||||
leaking resources.
|
||||
|
||||
Change the function to return -EEXIST if queue item already exists.
|
||||
|
||||
Modify all callsites to handle that.
|
||||
|
||||
Signed-off-by: Pauli Virtanen <pav@iki.fi>
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
|
||||
Conflicts: Context difference due to missing tree-wide changes:
|
||||
bf4afc53b77ae Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
|
||||
69050f8d6d075 treewide: Replace kmalloc with kmalloc_obj for non-scalar types
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index 4f7f15544393..6291e1ce61f4 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -780,7 +780,7 @@ int hci_cmd_sync_queue_once(struct hci_dev *hdev, hci_cmd_sync_work_func_t func,
|
||||
void *data, hci_cmd_sync_work_destroy_t destroy)
|
||||
{
|
||||
if (hci_cmd_sync_lookup_entry(hdev, func, data, destroy))
|
||||
- return 0;
|
||||
+ return -EEXIST;
|
||||
|
||||
return hci_cmd_sync_queue(hdev, func, data, destroy);
|
||||
}
|
||||
@@ -3255,6 +3255,8 @@ static int update_passive_scan_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
int hci_update_passive_scan(struct hci_dev *hdev)
|
||||
{
|
||||
+ int err;
|
||||
+
|
||||
/* Only queue if it would have any effect */
|
||||
if (!test_bit(HCI_UP, &hdev->flags) ||
|
||||
test_bit(HCI_INIT, &hdev->flags) ||
|
||||
@@ -3264,8 +3266,9 @@ int hci_update_passive_scan(struct hci_dev *hdev)
|
||||
hci_dev_test_flag(hdev, HCI_UNREGISTER))
|
||||
return 0;
|
||||
|
||||
- return hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
|
||||
- NULL);
|
||||
+ err = hci_cmd_sync_queue_once(hdev, update_passive_scan_sync, NULL,
|
||||
+ NULL);
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
int hci_write_sc_support_sync(struct hci_dev *hdev, u8 val)
|
||||
@@ -6958,8 +6961,11 @@ static int hci_acl_create_conn_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
int hci_connect_acl_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
- return hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
|
||||
- NULL);
|
||||
+ int err;
|
||||
+
|
||||
+ err = hci_cmd_sync_queue_once(hdev, hci_acl_create_conn_sync, conn,
|
||||
+ NULL);
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -6995,8 +7001,11 @@ static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
int hci_connect_le_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
- return hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
|
||||
- create_le_conn_complete);
|
||||
+ int err;
|
||||
+
|
||||
+ err = hci_cmd_sync_queue_once(hdev, hci_le_create_conn_sync, conn,
|
||||
+ create_le_conn_complete);
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
int hci_cancel_connect_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
@@ -7203,8 +7212,11 @@ static int hci_le_pa_create_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
int hci_connect_pa_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
- return hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
|
||||
- create_pa_complete);
|
||||
+ int err;
|
||||
+
|
||||
+ err = hci_cmd_sync_queue_once(hdev, hci_le_pa_create_sync, conn,
|
||||
+ create_pa_complete);
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void create_big_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7266,8 +7278,11 @@ static int hci_le_big_create_sync(struct hci_dev *hdev, void *data)
|
||||
|
||||
int hci_connect_big_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||
{
|
||||
- return hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
|
||||
- create_big_complete);
|
||||
+ int err;
|
||||
+
|
||||
+ err = hci_cmd_sync_queue_once(hdev, hci_le_big_create_sync, conn,
|
||||
+ create_big_complete);
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
struct past_data {
|
||||
@@ -7359,7 +7374,7 @@ int hci_past_sync(struct hci_conn *conn, struct hci_conn *le)
|
||||
if (err)
|
||||
kfree(data);
|
||||
|
||||
- return err;
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7446,7 +7461,7 @@ int hci_le_read_remote_features(struct hci_conn *conn)
|
||||
else
|
||||
err = -EOPNOTSUPP;
|
||||
|
||||
- return err;
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void pkt_type_changed(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7472,6 +7487,7 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_change_conn_ptype *cp;
|
||||
+ int err;
|
||||
|
||||
cp = kmalloc(sizeof(*cp), GFP_KERNEL);
|
||||
if (!cp)
|
||||
@@ -7480,8 +7496,9 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
|
||||
cp->handle = cpu_to_le16(conn->handle);
|
||||
cp->pkt_type = cpu_to_le16(pkt_type);
|
||||
|
||||
- return hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
|
||||
- pkt_type_changed);
|
||||
+ err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
|
||||
+ pkt_type_changed);
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
static void le_phy_update_complete(struct hci_dev *hdev, void *data, int err)
|
||||
@@ -7507,6 +7524,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_set_phy *cp;
|
||||
+ int err;
|
||||
|
||||
cp = kmalloc(sizeof(*cp), GFP_KERNEL);
|
||||
if (!cp)
|
||||
@@ -7517,6 +7535,7 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
|
||||
cp->tx_phys = tx_phys;
|
||||
cp->rx_phys = rx_phys;
|
||||
|
||||
- return hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
|
||||
- le_phy_update_complete);
|
||||
+ err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
|
||||
+ le_phy_update_complete);
|
||||
+ return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
From 3eed784fc3a9e5477b2948a4fa33f6ce32c33319 Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Thu, 16 Apr 2026 00:04:46 -0500
|
||||
Subject: [PATCH] Bluetooth: hci_sync: fix leaks when hci_cmd_sync_queue_once
|
||||
fails
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit aca377208e7f7322bf4e107cdec6e7d7e8aa7a88
|
||||
Author: Pauli Virtanen <pav@iki.fi>
|
||||
Date: Wed Mar 25 21:07:44 2026 +0200
|
||||
|
||||
Bluetooth: hci_sync: fix leaks when hci_cmd_sync_queue_once fails
|
||||
|
||||
When hci_cmd_sync_queue_once() returns with error, the destroy callback
|
||||
will not be called.
|
||||
|
||||
Fix leaking references / memory on these failures.
|
||||
|
||||
Signed-off-by: Pauli Virtanen <pav@iki.fi>
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index 6291e1ce61f4..cac7a8078795 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -7453,13 +7453,16 @@ int hci_le_read_remote_features(struct hci_conn *conn)
|
||||
* role is possible. Otherwise just transition into the
|
||||
* connected state without requesting the remote features.
|
||||
*/
|
||||
- if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES))
|
||||
+ if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
|
||||
err = hci_cmd_sync_queue_once(hdev,
|
||||
hci_le_read_remote_features_sync,
|
||||
hci_conn_hold(conn),
|
||||
le_read_features_complete);
|
||||
- else
|
||||
+ if (err)
|
||||
+ hci_conn_drop(conn);
|
||||
+ } else {
|
||||
err = -EOPNOTSUPP;
|
||||
+ }
|
||||
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
@@ -7498,6 +7501,9 @@ int hci_acl_change_pkt_type(struct hci_conn *conn, u16 pkt_type)
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_change_conn_ptype_sync, cp,
|
||||
pkt_type_changed);
|
||||
+ if (err)
|
||||
+ kfree(cp);
|
||||
+
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
|
||||
@@ -7537,5 +7543,8 @@ int hci_le_set_phy(struct hci_conn *conn, u8 tx_phys, u8 rx_phys)
|
||||
|
||||
err = hci_cmd_sync_queue_once(hdev, hci_le_set_phy_sync, cp,
|
||||
le_phy_update_complete);
|
||||
+ if (err)
|
||||
+ kfree(cp);
|
||||
+
|
||||
return (err == -EEXIST) ? 0 : err;
|
||||
}
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,243 @@
|
||||
From b4a37eb54b3d79e24a8ad9cd4a58baecef81189a Mon Sep 17 00:00:00 2001
|
||||
From: David Marlin <dmarlin@redhat.com>
|
||||
Date: Thu, 16 Apr 2026 00:04:53 -0500
|
||||
Subject: [PATCH] Bluetooth: hci_sync: Fix UAF in le_read_features_complete
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157827
|
||||
|
||||
commit 035c25007c9e698bef3826070ee34bb6d778020c
|
||||
Author: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
Date: Wed Mar 25 11:11:46 2026 -0400
|
||||
|
||||
Bluetooth: hci_sync: Fix UAF in le_read_features_complete
|
||||
|
||||
This fixes the following backtrace caused by hci_conn being freed
|
||||
before le_read_features_complete but after
|
||||
hci_le_read_remote_features_sync so hci_conn_del -> hci_cmd_sync_dequeue
|
||||
is not able to prevent it:
|
||||
|
||||
==================================================================
|
||||
BUG: KASAN: slab-use-after-free in instrument_atomic_read_write include/linux/instrumented.h:96 [inline]
|
||||
BUG: KASAN: slab-use-after-free in atomic_dec_and_test include/linux/atomic/atomic-instrumented.h:1383 [inline]
|
||||
BUG: KASAN: slab-use-after-free in hci_conn_drop include/net/bluetooth/hci_core.h:1688 [inline]
|
||||
BUG: KASAN: slab-use-after-free in le_read_features_complete+0x5b/0x340 net/bluetooth/hci_sync.c:7344
|
||||
Write of size 4 at addr ffff8880796b0010 by task kworker/u9:0/52
|
||||
|
||||
CPU: 0 UID: 0 PID: 52 Comm: kworker/u9:0 Not tainted syzkaller #0 PREEMPT(full)
|
||||
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025
|
||||
Workqueue: hci0 hci_cmd_sync_work
|
||||
Call Trace:
|
||||
<TASK>
|
||||
__dump_stack lib/dump_stack.c:94 [inline]
|
||||
dump_stack_lvl+0x116/0x1f0 lib/dump_stack.c:120
|
||||
print_address_description mm/kasan/report.c:378 [inline]
|
||||
print_report+0xcd/0x630 mm/kasan/report.c:482
|
||||
kasan_report+0xe0/0x110 mm/kasan/report.c:595
|
||||
check_region_inline mm/kasan/generic.c:194 [inline]
|
||||
kasan_check_range+0x100/0x1b0 mm/kasan/generic.c:200
|
||||
instrument_atomic_read_write include/linux/instrumented.h:96 [inline]
|
||||
atomic_dec_and_test include/linux/atomic/atomic-instrumented.h:1383 [inline]
|
||||
hci_conn_drop include/net/bluetooth/hci_core.h:1688 [inline]
|
||||
le_read_features_complete+0x5b/0x340 net/bluetooth/hci_sync.c:7344
|
||||
hci_cmd_sync_work+0x1ff/0x430 net/bluetooth/hci_sync.c:334
|
||||
process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257
|
||||
process_scheduled_works kernel/workqueue.c:3340 [inline]
|
||||
worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421
|
||||
kthread+0x3c5/0x780 kernel/kthread.c:463
|
||||
ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158
|
||||
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246
|
||||
</TASK>
|
||||
|
||||
Allocated by task 5932:
|
||||
kasan_save_stack+0x33/0x60 mm/kasan/common.c:56
|
||||
kasan_save_track+0x14/0x30 mm/kasan/common.c:77
|
||||
poison_kmalloc_redzone mm/kasan/common.c:400 [inline]
|
||||
__kasan_kmalloc+0xaa/0xb0 mm/kasan/common.c:417
|
||||
kmalloc_noprof include/linux/slab.h:957 [inline]
|
||||
kzalloc_noprof include/linux/slab.h:1094 [inline]
|
||||
__hci_conn_add+0xf8/0x1c70 net/bluetooth/hci_conn.c:963
|
||||
hci_conn_add_unset+0x76/0x100 net/bluetooth/hci_conn.c:1084
|
||||
le_conn_complete_evt+0x639/0x1f20 net/bluetooth/hci_event.c:5714
|
||||
hci_le_enh_conn_complete_evt+0x23d/0x380 net/bluetooth/hci_event.c:5861
|
||||
hci_le_meta_evt+0x357/0x5e0 net/bluetooth/hci_event.c:7408
|
||||
hci_event_func net/bluetooth/hci_event.c:7716 [inline]
|
||||
hci_event_packet+0x685/0x11c0 net/bluetooth/hci_event.c:7773
|
||||
hci_rx_work+0x2c9/0xeb0 net/bluetooth/hci_core.c:4076
|
||||
process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257
|
||||
process_scheduled_works kernel/workqueue.c:3340 [inline]
|
||||
worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421
|
||||
kthread+0x3c5/0x780 kernel/kthread.c:463
|
||||
ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158
|
||||
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246
|
||||
|
||||
Freed by task 5932:
|
||||
kasan_save_stack+0x33/0x60 mm/kasan/common.c:56
|
||||
kasan_save_track+0x14/0x30 mm/kasan/common.c:77
|
||||
__kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:587
|
||||
kasan_save_free_info mm/kasan/kasan.h:406 [inline]
|
||||
poison_slab_object mm/kasan/common.c:252 [inline]
|
||||
__kasan_slab_free+0x5f/0x80 mm/kasan/common.c:284
|
||||
kasan_slab_free include/linux/kasan.h:234 [inline]
|
||||
slab_free_hook mm/slub.c:2540 [inline]
|
||||
slab_free mm/slub.c:6663 [inline]
|
||||
kfree+0x2f8/0x6e0 mm/slub.c:6871
|
||||
device_release+0xa4/0x240 drivers/base/core.c:2565
|
||||
kobject_cleanup lib/kobject.c:689 [inline]
|
||||
kobject_release lib/kobject.c:720 [inline]
|
||||
kref_put include/linux/kref.h:65 [inline]
|
||||
kobject_put+0x1e7/0x590 lib/kobject.c:737
|
||||
put_device drivers/base/core.c:3797 [inline]
|
||||
device_unregister+0x2f/0xc0 drivers/base/core.c:3920
|
||||
hci_conn_del_sysfs+0xb4/0x180 net/bluetooth/hci_sysfs.c:79
|
||||
hci_conn_cleanup net/bluetooth/hci_conn.c:173 [inline]
|
||||
hci_conn_del+0x657/0x1180 net/bluetooth/hci_conn.c:1234
|
||||
hci_disconn_complete_evt+0x410/0xa00 net/bluetooth/hci_event.c:3451
|
||||
hci_event_func net/bluetooth/hci_event.c:7719 [inline]
|
||||
hci_event_packet+0xa10/0x11c0 net/bluetooth/hci_event.c:7773
|
||||
hci_rx_work+0x2c9/0xeb0 net/bluetooth/hci_core.c:4076
|
||||
process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257
|
||||
process_scheduled_works kernel/workqueue.c:3340 [inline]
|
||||
worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421
|
||||
kthread+0x3c5/0x780 kernel/kthread.c:463
|
||||
ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158
|
||||
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246
|
||||
|
||||
The buggy address belongs to the object at ffff8880796b0000
|
||||
which belongs to the cache kmalloc-8k of size 8192
|
||||
The buggy address is located 16 bytes inside of
|
||||
freed 8192-byte region [ffff8880796b0000, ffff8880796b2000)
|
||||
|
||||
The buggy address belongs to the physical page:
|
||||
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x796b0
|
||||
head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
|
||||
anon flags: 0xfff00000000040(head|node=0|zone=1|lastcpupid=0x7ff)
|
||||
page_type: f5(slab)
|
||||
raw: 00fff00000000040 ffff88813ff27280 0000000000000000 0000000000000001
|
||||
raw: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000
|
||||
head: 00fff00000000040 ffff88813ff27280 0000000000000000 0000000000000001
|
||||
head: 0000000000000000 0000000000020002 00000000f5000000 0000000000000000
|
||||
head: 00fff00000000003 ffffea0001e5ac01 00000000ffffffff 00000000ffffffff
|
||||
head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000008
|
||||
page dumped because: kasan: bad access detected
|
||||
page_owner tracks the page as allocated
|
||||
page last allocated via order 3, migratetype Unmovable, gfp_mask 0xd2040(__GFP_IO|__GFP_NOWARN|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 5657, tgid 5657 (dhcpcd-run-hook), ts 79819636908, free_ts 79814310558
|
||||
set_page_owner include/linux/page_owner.h:32 [inline]
|
||||
post_alloc_hook+0x1af/0x220 mm/page_alloc.c:1845
|
||||
prep_new_page mm/page_alloc.c:1853 [inline]
|
||||
get_page_from_freelist+0xd0b/0x31a0 mm/page_alloc.c:3879
|
||||
__alloc_frozen_pages_noprof+0x25f/0x2440 mm/page_alloc.c:5183
|
||||
alloc_pages_mpol+0x1fb/0x550 mm/mempolicy.c:2416
|
||||
alloc_slab_page mm/slub.c:3075 [inline]
|
||||
allocate_slab mm/slub.c:3248 [inline]
|
||||
new_slab+0x2c3/0x430 mm/slub.c:3302
|
||||
___slab_alloc+0xe18/0x1c90 mm/slub.c:4651
|
||||
__slab_alloc.constprop.0+0x63/0x110 mm/slub.c:4774
|
||||
__slab_alloc_node mm/slub.c:4850 [inline]
|
||||
slab_alloc_node mm/slub.c:5246 [inline]
|
||||
__kmalloc_cache_noprof+0x477/0x800 mm/slub.c:5766
|
||||
kmalloc_noprof include/linux/slab.h:957 [inline]
|
||||
kzalloc_noprof include/linux/slab.h:1094 [inline]
|
||||
tomoyo_print_bprm security/tomoyo/audit.c:26 [inline]
|
||||
tomoyo_init_log+0xc8a/0x2140 security/tomoyo/audit.c:264
|
||||
tomoyo_supervisor+0x302/0x13b0 security/tomoyo/common.c:2198
|
||||
tomoyo_audit_env_log security/tomoyo/environ.c:36 [inline]
|
||||
tomoyo_env_perm+0x191/0x200 security/tomoyo/environ.c:63
|
||||
tomoyo_environ security/tomoyo/domain.c:672 [inline]
|
||||
tomoyo_find_next_domain+0xec1/0x20b0 security/tomoyo/domain.c:888
|
||||
tomoyo_bprm_check_security security/tomoyo/tomoyo.c:102 [inline]
|
||||
tomoyo_bprm_check_security+0x12d/0x1d0 security/tomoyo/tomoyo.c:92
|
||||
security_bprm_check+0x1b9/0x1e0 security/security.c:794
|
||||
search_binary_handler fs/exec.c:1659 [inline]
|
||||
exec_binprm fs/exec.c:1701 [inline]
|
||||
bprm_execve fs/exec.c:1753 [inline]
|
||||
bprm_execve+0x81e/0x1620 fs/exec.c:1729
|
||||
do_execveat_common.isra.0+0x4a5/0x610 fs/exec.c:1859
|
||||
page last free pid 5657 tgid 5657 stack trace:
|
||||
reset_page_owner include/linux/page_owner.h:25 [inline]
|
||||
free_pages_prepare mm/page_alloc.c:1394 [inline]
|
||||
__free_frozen_pages+0x7df/0x1160 mm/page_alloc.c:2901
|
||||
discard_slab mm/slub.c:3346 [inline]
|
||||
__put_partials+0x130/0x170 mm/slub.c:3886
|
||||
qlink_free mm/kasan/quarantine.c:163 [inline]
|
||||
qlist_free_all+0x4c/0xf0 mm/kasan/quarantine.c:179
|
||||
kasan_quarantine_reduce+0x195/0x1e0 mm/kasan/quarantine.c:286
|
||||
__kasan_slab_alloc+0x69/0x90 mm/kasan/common.c:352
|
||||
kasan_slab_alloc include/linux/kasan.h:252 [inline]
|
||||
slab_post_alloc_hook mm/slub.c:4948 [inline]
|
||||
slab_alloc_node mm/slub.c:5258 [inline]
|
||||
__kmalloc_cache_noprof+0x274/0x800 mm/slub.c:5766
|
||||
kmalloc_noprof include/linux/slab.h:957 [inline]
|
||||
tomoyo_print_header security/tomoyo/audit.c:156 [inline]
|
||||
tomoyo_init_log+0x197/0x2140 security/tomoyo/audit.c:255
|
||||
tomoyo_supervisor+0x302/0x13b0 security/tomoyo/common.c:2198
|
||||
tomoyo_audit_env_log security/tomoyo/environ.c:36 [inline]
|
||||
tomoyo_env_perm+0x191/0x200 security/tomoyo/environ.c:63
|
||||
tomoyo_environ security/tomoyo/domain.c:672 [inline]
|
||||
tomoyo_find_next_domain+0xec1/0x20b0 security/tomoyo/domain.c:888
|
||||
tomoyo_bprm_check_security security/tomoyo/tomoyo.c:102 [inline]
|
||||
tomoyo_bprm_check_security+0x12d/0x1d0 security/tomoyo/tomoyo.c:92
|
||||
security_bprm_check+0x1b9/0x1e0 security/security.c:794
|
||||
search_binary_handler fs/exec.c:1659 [inline]
|
||||
exec_binprm fs/exec.c:1701 [inline]
|
||||
bprm_execve fs/exec.c:1753 [inline]
|
||||
bprm_execve+0x81e/0x1620 fs/exec.c:1729
|
||||
do_execveat_common.isra.0+0x4a5/0x610 fs/exec.c:1859
|
||||
do_execve fs/exec.c:1933 [inline]
|
||||
__do_sys_execve fs/exec.c:2009 [inline]
|
||||
__se_sys_execve fs/exec.c:2004 [inline]
|
||||
__x64_sys_execve+0x8e/0xb0 fs/exec.c:2004
|
||||
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
|
||||
do_syscall_64+0xcd/0xf80 arch/x86/entry/syscall_64.c:94
|
||||
|
||||
Memory state around the buggy address:
|
||||
ffff8880796aff00: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
|
||||
ffff8880796aff80: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
|
||||
>ffff8880796b0000: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
^
|
||||
ffff8880796b0080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
ffff8880796b0100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
|
||||
==================================================================
|
||||
|
||||
Fixes: a106e50be74b ("Bluetooth: HCI: Add support for LL Extended Feature Set")
|
||||
Reported-by: syzbot+87badbb9094e008e0685@syzkaller.appspotmail.com
|
||||
Tested-by: syzbot+87badbb9094e008e0685@syzkaller.appspotmail.com
|
||||
Closes: https://syzbot.org/bug?extid=87badbb9094e008e0685
|
||||
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
|
||||
Signed-off-by: Pauli Virtanen <pav@iki.fi>
|
||||
|
||||
Signed-off-by: David Marlin <dmarlin@redhat.com>
|
||||
|
||||
diff --git a/net/bluetooth/hci_sync.c b/net/bluetooth/hci_sync.c
|
||||
index cac7a8078795..5101efc6a0e4 100644
|
||||
--- a/net/bluetooth/hci_sync.c
|
||||
+++ b/net/bluetooth/hci_sync.c
|
||||
@@ -7383,10 +7383,8 @@ static void le_read_features_complete(struct hci_dev *hdev, void *data, int err)
|
||||
|
||||
bt_dev_dbg(hdev, "err %d", err);
|
||||
|
||||
- if (err == -ECANCELED)
|
||||
- return;
|
||||
-
|
||||
hci_conn_drop(conn);
|
||||
+ hci_conn_put(conn);
|
||||
}
|
||||
|
||||
static int hci_le_read_all_remote_features_sync(struct hci_dev *hdev,
|
||||
@@ -7456,10 +7454,12 @@ int hci_le_read_remote_features(struct hci_conn *conn)
|
||||
if (conn->out || (hdev->le_features[0] & HCI_LE_PERIPHERAL_FEATURES)) {
|
||||
err = hci_cmd_sync_queue_once(hdev,
|
||||
hci_le_read_remote_features_sync,
|
||||
- hci_conn_hold(conn),
|
||||
+ hci_conn_hold(hci_conn_get(conn)),
|
||||
le_read_features_complete);
|
||||
- if (err)
|
||||
+ if (err) {
|
||||
hci_conn_drop(conn);
|
||||
+ hci_conn_put(conn);
|
||||
+ }
|
||||
} else {
|
||||
err = -EOPNOTSUPP;
|
||||
}
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,63 @@
|
||||
From 1f16a5989467930fa2a1bf222fdc20207b917c50 Mon Sep 17 00:00:00 2001
|
||||
From: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Date: Thu, 19 Mar 2026 10:00:42 -0400
|
||||
Subject: [PATCH] pNFS: fix a missing wake up while waiting on NFS_LAYOUT_DRAIN
|
||||
|
||||
JIRA: https://issues.redhat.com/browse/RHEL-157444
|
||||
|
||||
commit 5248d8474e594d156bee1ed10339cc16e207a28b
|
||||
Author: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Date: Mon Jan 26 14:15:39 2026 -0500
|
||||
|
||||
pNFS: fix a missing wake up while waiting on NFS_LAYOUT_DRAIN
|
||||
|
||||
It is possible to have a task get stuck on waiting on the
|
||||
NFS_LAYOUT_DRAIN in the following scenario
|
||||
|
||||
1. cpu a: waiter test NFS_LAYOUT_DRAIN (1) and plh_outstanding (1)
|
||||
2. cpu b: atomic_dec_and_test() -> clear bit -> wake up
|
||||
3. cpu c: sets NFS_LAYOUT_DRAIN again
|
||||
4. cpu a: calls wait_on_bit() sleeps forever.
|
||||
|
||||
To expand on this we have say 2 outstanding pnfs write IO that get
|
||||
ESTALE which causes both to call pnfs_destroy_layout() and set the
|
||||
NFS_LAYOUT_DRAIN bit but the 1st one doesn't call the
|
||||
pnfs_put_layout_hdr() yet (as that would prevent the 2nd ESTALE write
|
||||
from trying to call pnfs_destroy_layout()). If the 1st ESTALE write
|
||||
is the one that initially sets the NFS_LAYOUT_DRAIN so that new IO
|
||||
on this file initiates new LAYOUTGET. Another new write would find
|
||||
NFS_LAYOUT_DRAIN set and phl_outstanding>0 (step 1) and would
|
||||
wait_on_bit(). LAYOUTGET completes doing step 2. Now, the 2nd of
|
||||
ESTALE writes is calling pnfs_destory_layout() and set the
|
||||
NFS_LAYOUT_DRAIN bit (step 3). Finally, the waiting write wakes up
|
||||
to check the bit and goes back to sleep.
|
||||
|
||||
The problem revolves around the fact that if NFS_LAYOUT_INVALID_STID
|
||||
was already set, it should not do the work of
|
||||
pnfs_mark_layout_stateid_invalid(), thus NFS_LAYOUT_DRAIN will not
|
||||
be set more than once for an invalid layout.
|
||||
|
||||
Suggested-by: Trond Myklebust <trond.myklebust@hammerspace.com>
|
||||
Fixes: 880265c77ac4 ("pNFS: Avoid a live lock condition in pnfs_update_layout()")
|
||||
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
|
||||
Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
|
||||
|
||||
Signed-off-by: Olga Kornievskaia <okorniev@redhat.com>
|
||||
|
||||
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
|
||||
index 33bc6db0dc92..b3cb5ee9d821 100644
|
||||
--- a/fs/nfs/pnfs.c
|
||||
+++ b/fs/nfs/pnfs.c
|
||||
@@ -463,7 +463,8 @@ pnfs_mark_layout_stateid_invalid(struct pnfs_layout_hdr *lo,
|
||||
};
|
||||
struct pnfs_layout_segment *lseg, *next;
|
||||
|
||||
- set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags);
|
||||
+ if (test_and_set_bit(NFS_LAYOUT_INVALID_STID, &lo->plh_flags))
|
||||
+ return !list_empty(&lo->plh_segs);
|
||||
clear_bit(NFS_INO_LAYOUTCOMMIT, &NFS_I(lo->plh_inode)->flags);
|
||||
list_for_each_entry_safe(lseg, next, &lo->plh_segs, pls_list)
|
||||
pnfs_clear_lseg_state(lseg, lseg_list);
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
From 33e2b5cdb5bd48ccb14dd2e4db98bc07d209dc70 Mon Sep 17 00:00:00 2001
|
||||
From: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
|
||||
Date: Tue, 28 Apr 2026 12:03:58 +0000
|
||||
Subject: [PATCH] smb: client: fix OOB reads parsing symlink error response
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-171476
|
||||
CVE: CVE-2026-31613
|
||||
|
||||
commit 3df690bba28edec865cf7190be10708ad0ddd67e
|
||||
Author: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Date: Mon Apr 6 15:49:38 2026 +0200
|
||||
|
||||
smb: client: fix OOB reads parsing symlink error response
|
||||
|
||||
When a CREATE returns STATUS_STOPPED_ON_SYMLINK, smb2_check_message()
|
||||
returns success without any length validation, leaving the symlink
|
||||
parsers as the only defense against an untrusted server.
|
||||
|
||||
symlink_data() walks SMB 3.1.1 error contexts with the loop test "p <
|
||||
end", but reads p->ErrorId at offset 4 and p->ErrorDataLength at offset
|
||||
0. When the server-controlled ErrorDataLength advances p to within 1-7
|
||||
bytes of end, the next iteration will read past it. When the matching
|
||||
context is found, sym->SymLinkErrorTag is read at offset 4 from
|
||||
p->ErrorContextData with no check that the symlink header itself fits.
|
||||
|
||||
smb2_parse_symlink_response() then bounds-checks the substitute name
|
||||
using SMB2_SYMLINK_STRUCT_SIZE as the offset of PathBuffer from
|
||||
iov_base. That value is computed as sizeof(smb2_err_rsp) +
|
||||
sizeof(smb2_symlink_err_rsp), which is correct only when
|
||||
ErrorContextCount == 0.
|
||||
|
||||
With at least one error context the symlink data sits 8 bytes deeper,
|
||||
and each skipped non-matching context shifts it further by 8 +
|
||||
ALIGN(ErrorDataLength, 8). The check is too short, allowing the
|
||||
substitute name read to run past iov_len. The out-of-bound heap bytes
|
||||
are UTF-16-decoded into the symlink target and returned to userspace via
|
||||
readlink(2).
|
||||
|
||||
Fix this all up by making the loops test require the full context header
|
||||
to fit, rejecting sym if its header runs past end, and bound the
|
||||
substitute name against the actual position of sym->PathBuffer rather
|
||||
than a fixed offset.
|
||||
|
||||
Because sub_offs and sub_len are 16bits, the pointer math will not
|
||||
overflow here with the new greater-than.
|
||||
|
||||
Cc: Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
Cc: Shyam Prasad N <sprasad@microsoft.com>
|
||||
Cc: Tom Talpey <tom@talpey.com>
|
||||
Cc: Bharath SM <bharathsm@microsoft.com>
|
||||
Cc: linux-cifs@vger.kernel.org
|
||||
Cc: samba-technical@lists.samba.org
|
||||
Cc: stable <stable@kernel.org>
|
||||
Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org>
|
||||
Assisted-by: gregkh_clanker_t1000
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
Signed-off-by: Steve French <stfrench@microsoft.com>
|
||||
|
||||
Signed-off-by: CKI Backport Bot <cki-ci-bot+cki-gitlab-backport-bot@redhat.com>
|
||||
|
||||
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
|
||||
index 1f7f284a7844..b2ddcecd00b9 100644
|
||||
--- a/fs/smb/client/smb2file.c
|
||||
+++ b/fs/smb/client/smb2file.c
|
||||
@@ -27,10 +27,11 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
{
|
||||
struct smb2_err_rsp *err = iov->iov_base;
|
||||
struct smb2_symlink_err_rsp *sym = ERR_PTR(-EINVAL);
|
||||
+ u8 *end = (u8 *)err + iov->iov_len;
|
||||
u32 len;
|
||||
|
||||
if (err->ErrorContextCount) {
|
||||
- struct smb2_error_context_rsp *p, *end;
|
||||
+ struct smb2_error_context_rsp *p;
|
||||
|
||||
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
|
||||
ErrorContextData) +
|
||||
@@ -39,8 +40,7 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
p = (struct smb2_error_context_rsp *)err->ErrorData;
|
||||
- end = (struct smb2_error_context_rsp *)((u8 *)err + iov->iov_len);
|
||||
- do {
|
||||
+ while ((u8 *)p + sizeof(*p) <= end) {
|
||||
if (le32_to_cpu(p->ErrorId) == SMB2_ERROR_ID_DEFAULT) {
|
||||
sym = (struct smb2_symlink_err_rsp *)p->ErrorContextData;
|
||||
break;
|
||||
@@ -50,14 +50,16 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
||||
|
||||
len = ALIGN(le32_to_cpu(p->ErrorDataLength), 8);
|
||||
p = (struct smb2_error_context_rsp *)(p->ErrorContextData + len);
|
||||
- } while (p < end);
|
||||
+ }
|
||||
} else if (le32_to_cpu(err->ByteCount) >= sizeof(*sym) &&
|
||||
iov->iov_len >= SMB2_SYMLINK_STRUCT_SIZE) {
|
||||
sym = (struct smb2_symlink_err_rsp *)err->ErrorData;
|
||||
}
|
||||
|
||||
- if (!IS_ERR(sym) && (le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
|
||||
- le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
|
||||
+ if (!IS_ERR(sym) &&
|
||||
+ ((u8 *)sym + sizeof(*sym) > end ||
|
||||
+ le32_to_cpu(sym->SymLinkErrorTag) != SYMLINK_ERROR_TAG ||
|
||||
+ le32_to_cpu(sym->ReparseTag) != IO_REPARSE_TAG_SYMLINK))
|
||||
sym = ERR_PTR(-EINVAL);
|
||||
|
||||
return sym;
|
||||
@@ -128,8 +130,10 @@ int smb2_parse_symlink_response(struct cifs_sb_info *cifs_sb, const struct kvec
|
||||
print_len = le16_to_cpu(sym->PrintNameLength);
|
||||
print_offs = le16_to_cpu(sym->PrintNameOffset);
|
||||
|
||||
- if (iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + sub_offs + sub_len ||
|
||||
- iov->iov_len < SMB2_SYMLINK_STRUCT_SIZE + print_offs + print_len)
|
||||
+ if ((char *)sym->PathBuffer + sub_offs + sub_len >
|
||||
+ (char *)iov->iov_base + iov->iov_len ||
|
||||
+ (char *)sym->PathBuffer + print_offs + print_len >
|
||||
+ (char *)iov->iov_base + iov->iov_len)
|
||||
return -EINVAL;
|
||||
|
||||
return smb2_parse_native_symlink(path,
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
164
1285-sched-deadline-fix-dl-server-time-accounting.patch
Normal file
164
1285-sched-deadline-fix-dl-server-time-accounting.patch
Normal file
@ -0,0 +1,164 @@
|
||||
|
||||
diff --git a/kernel/sched/deadline.c b/kernel/sched/deadline.c
|
||||
index 350c202..3645a12 100644
|
||||
--- a/kernel/sched/deadline.c
|
||||
+++ b/kernel/sched/deadline.c
|
||||
@@ -1166,8 +1166,12 @@ static enum hrtimer_restart dl_server_timer(struct hrtimer *timer, struct sched_
|
||||
sched_clock_tick();
|
||||
update_rq_clock(rq);
|
||||
|
||||
- if (!dl_se->dl_runtime)
|
||||
- return HRTIMER_NORESTART;
|
||||
+ /*
|
||||
+ * Make sure current has propagated its pending runtime into
|
||||
+ * any relevant server through calling dl_server_update() and
|
||||
+ * friends.
|
||||
+ */
|
||||
+ rq->donor->sched_class->update_curr(rq);
|
||||
|
||||
if (dl_se->dl_defer_armed) {
|
||||
/*
|
||||
@@ -1543,35 +1547,16 @@ static void update_curr_dl_se(struct rq *rq, struct sched_dl_entity *dl_se, s64
|
||||
* as time available for the fair server, avoiding a penalty for the
|
||||
* rt scheduler that did not consumed that time.
|
||||
*/
|
||||
-void dl_server_update_idle_time(struct rq *rq, struct task_struct *p)
|
||||
+void dl_server_update_idle(struct sched_dl_entity *dl_se, s64 delta_exec)
|
||||
{
|
||||
- s64 delta_exec;
|
||||
-
|
||||
- if (!rq->fair_server.dl_defer)
|
||||
- return;
|
||||
-
|
||||
- /* no need to discount more */
|
||||
- if (rq->fair_server.runtime < 0)
|
||||
- return;
|
||||
-
|
||||
- delta_exec = rq_clock_task(rq) - p->se.exec_start;
|
||||
- if (delta_exec < 0)
|
||||
- return;
|
||||
-
|
||||
- rq->fair_server.runtime -= delta_exec;
|
||||
-
|
||||
- if (rq->fair_server.runtime < 0) {
|
||||
- rq->fair_server.dl_defer_running = 0;
|
||||
- rq->fair_server.runtime = 0;
|
||||
- }
|
||||
-
|
||||
- p->se.exec_start = rq_clock_task(rq);
|
||||
+ if (dl_se->dl_server_active && dl_se->dl_runtime && dl_se->dl_defer)
|
||||
+ update_curr_dl_se(dl_se->rq, dl_se, delta_exec);
|
||||
}
|
||||
|
||||
void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec)
|
||||
{
|
||||
/* 0 runtime = fair server disabled */
|
||||
- if (dl_se->dl_runtime)
|
||||
+ if (dl_se->dl_server_active && dl_se->dl_runtime)
|
||||
update_curr_dl_se(dl_se->rq, dl_se, delta_exec);
|
||||
}
|
||||
|
||||
@@ -1582,6 +1567,11 @@ void dl_server_start(struct sched_dl_entity *dl_se)
|
||||
if (!dl_server(dl_se) || dl_se->dl_server_active)
|
||||
return;
|
||||
|
||||
+ /*
|
||||
+ * Update the current task to 'now'.
|
||||
+ */
|
||||
+ rq->donor->sched_class->update_curr(rq);
|
||||
+
|
||||
if (WARN_ON_ONCE(!cpu_online(cpu_of(rq))))
|
||||
return;
|
||||
|
||||
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
|
||||
index a135883..6e4e7b5 100644
|
||||
--- a/kernel/sched/fair.c
|
||||
+++ b/kernel/sched/fair.c
|
||||
@@ -1233,8 +1233,7 @@ static void update_curr(struct cfs_rq *cfs_rq)
|
||||
* against fair_server such that it can account for this time
|
||||
* and possibly avoid running this period.
|
||||
*/
|
||||
- if (dl_server_active(&rq->fair_server))
|
||||
- dl_server_update(&rq->fair_server, delta_exec);
|
||||
+ dl_server_update(&rq->fair_server, delta_exec);
|
||||
}
|
||||
|
||||
account_cfs_rq_runtime(cfs_rq, delta_exec);
|
||||
@@ -7001,12 +7000,8 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
||||
h_nr_idle = 1;
|
||||
}
|
||||
|
||||
- if (!rq_h_nr_queued && rq->cfs.h_nr_queued) {
|
||||
- /* Account for idle runtime */
|
||||
- if (!rq->nr_running)
|
||||
- dl_server_update_idle_time(rq, rq->curr);
|
||||
+ if (!rq_h_nr_queued && rq->cfs.h_nr_queued)
|
||||
dl_server_start(&rq->fair_server);
|
||||
- }
|
||||
|
||||
/* At this point se is NULL and we are at root level*/
|
||||
add_nr_running(rq, 1);
|
||||
diff --git a/kernel/sched/idle.c b/kernel/sched/idle.c
|
||||
index c39b089..e8cbba8 100644
|
||||
--- a/kernel/sched/idle.c
|
||||
+++ b/kernel/sched/idle.c
|
||||
@@ -452,9 +452,11 @@ static void wakeup_preempt_idle(struct rq *rq, struct task_struct *p, int flags)
|
||||
resched_curr(rq);
|
||||
}
|
||||
|
||||
+static void update_curr_idle(struct rq *rq);
|
||||
+
|
||||
static void put_prev_task_idle(struct rq *rq, struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
- dl_server_update_idle_time(rq, prev);
|
||||
+ update_curr_idle(rq);
|
||||
scx_update_idle(rq, false, true);
|
||||
}
|
||||
|
||||
@@ -496,6 +498,7 @@ dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
|
||||
*/
|
||||
static void task_tick_idle(struct rq *rq, struct task_struct *curr, int queued)
|
||||
{
|
||||
+ update_curr_idle(rq);
|
||||
}
|
||||
|
||||
static void switched_to_idle(struct rq *rq, struct task_struct *p)
|
||||
@@ -511,6 +514,17 @@ prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio)
|
||||
|
||||
static void update_curr_idle(struct rq *rq)
|
||||
{
|
||||
+ struct sched_entity *se = &rq->idle->se;
|
||||
+ u64 now = rq_clock_task(rq);
|
||||
+ s64 delta_exec;
|
||||
+
|
||||
+ delta_exec = now - se->exec_start;
|
||||
+ if (unlikely(delta_exec <= 0))
|
||||
+ return;
|
||||
+
|
||||
+ se->exec_start = now;
|
||||
+
|
||||
+ dl_server_update_idle(&rq->fair_server, delta_exec);
|
||||
}
|
||||
|
||||
/*
|
||||
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
|
||||
index 5461e9a..34813e3 100644
|
||||
--- a/kernel/sched/sched.h
|
||||
+++ b/kernel/sched/sched.h
|
||||
@@ -406,6 +406,7 @@ extern s64 dl_scaled_delta_exec(struct rq *rq, struct sched_dl_entity *dl_se, s6
|
||||
* naturally thottled to once per period, avoiding high context switch
|
||||
* workloads from spamming the hrtimer program/cancel paths.
|
||||
*/
|
||||
+extern void dl_server_update_idle(struct sched_dl_entity *dl_se, s64 delta_exec);
|
||||
extern void dl_server_update(struct sched_dl_entity *dl_se, s64 delta_exec);
|
||||
extern void dl_server_start(struct sched_dl_entity *dl_se);
|
||||
extern void dl_server_stop(struct sched_dl_entity *dl_se);
|
||||
@@ -413,8 +414,6 @@ extern void dl_server_init(struct sched_dl_entity *dl_se, struct rq *rq,
|
||||
dl_server_pick_f pick_task);
|
||||
extern void sched_init_dl_servers(void);
|
||||
|
||||
-extern void dl_server_update_idle_time(struct rq *rq,
|
||||
- struct task_struct *p);
|
||||
extern void fair_server_init(struct rq *rq);
|
||||
extern void __dl_server_attach_root(struct sched_dl_entity *dl_se, struct rq *rq);
|
||||
extern int dl_server_apply_params(struct sched_dl_entity *dl_se,
|
||||
30
1286-redhat-configs-automotive-disable-config-io-uring.patch
Normal file
30
1286-redhat-configs-automotive-disable-config-io-uring.patch
Normal file
@ -0,0 +1,30 @@
|
||||
From 65d0f5264af85b67546a34dc94b9b7778ed50e89 Mon Sep 17 00:00:00 2001
|
||||
From: Brian Masney <bmasney@redhat.com>
|
||||
Date: Wed, 20 May 2026 13:50:28 -0400
|
||||
Subject: [PATCH] redhat/configs: automotive: disable CONFIG_IO_URING
|
||||
|
||||
JIRA: https://redhat.atlassian.net/browse/RHEL-178183
|
||||
Upstream-status: RHEL-only
|
||||
|
||||
Disable CONFIG_IO_URING, and the dependency CONFIG_BLK_DEV_UBLK, since
|
||||
IO uring is currently not in supported at the moment in automotive.
|
||||
|
||||
Signed-off-by: Brian Masney <bmasney@redhat.com>
|
||||
|
||||
diff --git a/redhat/configs/rhel/automotive/generic/CONFIG_BLK_DEV_UBLK b/redhat/configs/rhel/automotive/generic/CONFIG_BLK_DEV_UBLK
|
||||
new file mode 100644
|
||||
index 000000000000..f773891a920f
|
||||
--- /dev/null
|
||||
+++ b/redhat/configs/rhel/automotive/generic/CONFIG_BLK_DEV_UBLK
|
||||
@@ -0,0 +1 @@
|
||||
+# CONFIG_BLK_DEV_UBLK is not set
|
||||
diff --git a/redhat/configs/rhel/automotive/generic/CONFIG_IO_URING b/redhat/configs/rhel/automotive/generic/CONFIG_IO_URING
|
||||
new file mode 100644
|
||||
index 000000000000..dcae2b3a1327
|
||||
--- /dev/null
|
||||
+++ b/redhat/configs/rhel/automotive/generic/CONFIG_IO_URING
|
||||
@@ -0,0 +1 @@
|
||||
+# CONFIG_IO_URING is not set
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
121
1287-rhel-kabi-dpll-adaptation.patch
Normal file
121
1287-rhel-kabi-dpll-adaptation.patch
Normal file
@ -0,0 +1,121 @@
|
||||
Subject: [PATCH] dpll: RHEL 10.2 kABI adaptation (RH_KABI_USE/REPLACE + ffo backward-compat)
|
||||
# RHEL-z-stream-specific kABI wrapping of the dpll feature additions (0015/0026/0028).
|
||||
# No CS10/upstream commit produces this exact kABI form; reconstructed to match RHEL.
|
||||
|
||||
diff --git a/drivers/dpll/dpll_netlink.c b/drivers/dpll/dpll_netlink.c
|
||||
index 00e8696..edd5967 100644
|
||||
--- a/drivers/dpll/dpll_netlink.c
|
||||
+++ b/drivers/dpll/dpll_netlink.c
|
||||
@@ -424,7 +424,13 @@ static int dpll_msg_add_ffo(struct sk_buff *msg, struct dpll_pin *pin,
|
||||
struct dpll_ffo_param ffo = { .type = type };
|
||||
int ret;
|
||||
|
||||
- if (!ops->ffo_get || !(ops->supported_ffo & BIT(type)))
|
||||
+ /* RHEL: To maintain backward compatibility with older binary modules,
|
||||
+ * we must accept a value of zero for .supported_ffo when the type is
|
||||
+ * DPLL_FFO_PORT_RXTX_RATE.
|
||||
+ */
|
||||
+ if (!ops->ffo_get ||
|
||||
+ !((ops->supported_ffo & BIT(type)) ||
|
||||
+ (!ops->supported_ffo && type == DPLL_FFO_PORT_RXTX_RATE)))
|
||||
return 0;
|
||||
ret = ops->ffo_get(pin, dpll_pin_on_dpll_priv(ref->dpll, pin),
|
||||
ref->dpll, dpll_priv(ref->dpll), &ffo, extack);
|
||||
diff --git a/include/linux/dpll.h b/include/linux/dpll.h
|
||||
index cd21b1f..d988c09 100644
|
||||
--- a/include/linux/dpll.h
|
||||
+++ b/include/linux/dpll.h
|
||||
@@ -54,15 +54,13 @@ struct dpll_device_ops {
|
||||
int (*phase_offset_avg_factor_get)(const struct dpll_device *dpll,
|
||||
void *dpll_priv, u32 *factor,
|
||||
struct netlink_ext_ack *extack);
|
||||
- int (*freq_monitor_set)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
+
|
||||
+ RH_KABI_USE(1, int (*freq_monitor_set)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_feature_state state,
|
||||
- struct netlink_ext_ack *extack);
|
||||
- int (*freq_monitor_get)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
+ struct netlink_ext_ack *extack))
|
||||
+ RH_KABI_USE(2, int (*freq_monitor_get)(const struct dpll_device *dpll, void *dpll_priv,
|
||||
enum dpll_feature_state *state,
|
||||
- struct netlink_ext_ack *extack);
|
||||
-
|
||||
- RH_KABI_RESERVE(1)
|
||||
- RH_KABI_RESERVE(2)
|
||||
+ struct netlink_ext_ack *extack))
|
||||
RH_KABI_RESERVE(3)
|
||||
RH_KABI_RESERVE(4)
|
||||
RH_KABI_RESERVE(5)
|
||||
@@ -80,13 +78,16 @@ enum dpll_ffo_type {
|
||||
__DPLL_FFO_TYPE_MAX,
|
||||
};
|
||||
|
||||
+/* RHEL: we have to keep 'ffo' field to be first to preserve compatibility
|
||||
+ * with older .ffo_get() callbacks that accepts 's64 *' as parameter instead
|
||||
+ * of 'struct dpll_ffo_param *'
|
||||
+ */
|
||||
struct dpll_ffo_param {
|
||||
- enum dpll_ffo_type type;
|
||||
s64 ffo;
|
||||
+ enum dpll_ffo_type type;
|
||||
};
|
||||
|
||||
struct dpll_pin_ops {
|
||||
- unsigned long supported_ffo;
|
||||
int (*frequency_set)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
const u64 frequency,
|
||||
@@ -111,12 +112,6 @@ struct dpll_pin_ops {
|
||||
const struct dpll_device *dpll,
|
||||
void *dpll_priv, enum dpll_pin_state *state,
|
||||
struct netlink_ext_ack *extack);
|
||||
- int (*operstate_on_dpll_get)(const struct dpll_pin *pin,
|
||||
- void *pin_priv,
|
||||
- const struct dpll_device *dpll,
|
||||
- void *dpll_priv,
|
||||
- enum dpll_pin_operstate *operstate,
|
||||
- struct netlink_ext_ack *extack);
|
||||
int (*state_on_pin_set)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_pin *parent_pin,
|
||||
void *parent_pin_priv,
|
||||
@@ -145,14 +140,13 @@ struct dpll_pin_ops {
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
const s32 phase_adjust,
|
||||
struct netlink_ext_ack *extack);
|
||||
- int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
+ RH_KABI_REPLACE(int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
+ const struct dpll_device *dpll, void *dpll_priv,
|
||||
+ s64 *ffo, struct netlink_ext_ack *extack),
|
||||
+ int (*ffo_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
struct dpll_ffo_param *ffo,
|
||||
- struct netlink_ext_ack *extack);
|
||||
- int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
- const struct dpll_device *dpll,
|
||||
- void *dpll_priv, u64 *measured_freq,
|
||||
- struct netlink_ext_ack *extack);
|
||||
+ struct netlink_ext_ack *extack))
|
||||
int (*esync_set)(const struct dpll_pin *pin, void *pin_priv,
|
||||
const struct dpll_device *dpll, void *dpll_priv,
|
||||
u64 freq, struct netlink_ext_ack *extack);
|
||||
@@ -171,9 +165,17 @@ struct dpll_pin_ops {
|
||||
enum dpll_pin_state *state,
|
||||
struct netlink_ext_ack *extack);
|
||||
|
||||
- RH_KABI_RESERVE(1)
|
||||
- RH_KABI_RESERVE(2)
|
||||
- RH_KABI_RESERVE(3)
|
||||
+ RH_KABI_USE(1, int (*measured_freq_get)(const struct dpll_pin *pin, void *pin_priv,
|
||||
+ const struct dpll_device *dpll,
|
||||
+ void *dpll_priv, u64 *measured_freq,
|
||||
+ struct netlink_ext_ack *extack))
|
||||
+ RH_KABI_USE(2, int (*operstate_on_dpll_get)(const struct dpll_pin *pin,
|
||||
+ void *pin_priv,
|
||||
+ const struct dpll_device *dpll,
|
||||
+ void *dpll_priv,
|
||||
+ enum dpll_pin_operstate *operstate,
|
||||
+ struct netlink_ext_ack *extack))
|
||||
+ RH_KABI_USE(3, unsigned long supported_ffo)
|
||||
RH_KABI_RESERVE(4)
|
||||
RH_KABI_RESERVE(5)
|
||||
RH_KABI_RESERVE(6)
|
||||
140
kernel.spec
140
kernel.spec
@ -182,7 +182,7 @@ Summary: The Linux kernel
|
||||
# This is needed to do merge window version magic
|
||||
%define patchlevel 12
|
||||
# This allows pkg_release to have configurable %%{?dist} tag
|
||||
%define specrelease 211.18.1%{?buildid}%{?dist}
|
||||
%define specrelease 211.20.1%{?buildid}%{?dist}
|
||||
# This defines the kabi tarball version
|
||||
%define kabiversion 6.12.0-211.7.1.el10_2
|
||||
|
||||
@ -1279,6 +1279,49 @@ Patch1241: 1241-bluetooth-mgmt-validate-ltk-enc-size-on-load.patch
|
||||
Patch1242: 1242-bluetooth-sco-fix-race-conditions-in-sco-sock-connect.patch
|
||||
Patch1243: 1243-xfs-delete-attr-leaf-freemap-entries-when-empty.patch
|
||||
Patch1244: 1244-xfs-fix-freemap-adjustments-when-adding-xattrs-to-leaf-block.patch
|
||||
Patch1245: 1245-proc-use-the-same-treatment-to-check-proc-lseek-as-ones-for-.patch
|
||||
Patch1246: 1246-proc-fix-missing-pde-set-flags-for-net-proc-files.patch
|
||||
Patch1247: 1247-proc-fix-type-confusion-in-pde-set-flags.patch
|
||||
Patch1248: 1248-nbd-defer-config-unlock-in-nbd-genl-connect.patch
|
||||
Patch1249: 1249-crypto-authenc-correctly-pass-einprogress-back-up-to-the-cal.patch
|
||||
Patch1250: 1250-dpll-zl3073x-detect-dpll-channel-count-from-chip-id-at-runti.patch
|
||||
Patch1251: 1251-dpll-zl3073x-add-die-temperature-reporting-for-supported-chi.patch
|
||||
Patch1252: 1252-dpll-zl3073x-use-struct-group-to-partition-states.patch
|
||||
Patch1253: 1253-dpll-zl3073x-add-zl3073x-ref-state-update-helper.patch
|
||||
Patch1254: 1254-dpll-zl3073x-introduce-zl3073x-chan-for-dpll-channel-state.patch
|
||||
Patch1255: 1255-dpll-zl3073x-add-dpll-channel-status-fields-to-zl3073x-chan.patch
|
||||
Patch1256: 1256-dpll-zl3073x-add-reference-priority-to-zl3073x-chan.patch
|
||||
Patch1257: 1257-dpll-zl3073x-drop-selected-and-simplify-connected-ref-getter.patch
|
||||
Patch1258: 1258-dpll-add-frequency-monitoring-to-netlink-spec.patch
|
||||
Patch1259: 1259-dpll-add-frequency-monitoring-callback-ops.patch
|
||||
Patch1260: 1260-dpll-zl3073x-implement-frequency-monitoring.patch
|
||||
Patch1261: 1261-dpll-zl3073x-clean-up-esync-get-set-and-use-zl3073x-out-is-n.patch
|
||||
Patch1262: 1262-dpll-zl3073x-use-field-modify-for-clear-and-set-patterns.patch
|
||||
Patch1263: 1263-dpll-zl3073x-add-ref-sync-and-output-clock-type-helpers.patch
|
||||
Patch1264: 1264-dpll-zl3073x-add-ref-sync-pair-support.patch
|
||||
Patch1265: 1265-smb-client-validate-the-whole-dacl-before-rewriting-it-in-ci.patch
|
||||
Patch1266: 1266-smb-client-require-a-full-nfs-mode-sid-before-reading-mode-b.patch
|
||||
Patch1267: 1267-smb-client-scope-end-of-dacl-to-cifs-debug2-use-in-parse-dac.patch
|
||||
Patch1268: 1268-smb-client-use-kzalloc-to-zero-initialize-security-descripto.patch
|
||||
Patch1269: 1269-smb-client-validate-dacloffset-before-building-dacl-pointers.patch
|
||||
Patch1270: 1270-dpll-add-pin-operational-state.patch
|
||||
Patch1271: 1271-dpll-zl3073x-implement-pin-operational-state-reporting.patch
|
||||
Patch1272: 1272-dpll-add-fractional-frequency-offset-to-pin-parent-device.patch
|
||||
Patch1273: 1273-dpll-zl3073x-report-ffo-as-dpll-vs-input-reference-offset.patch
|
||||
Patch1274: 1274-netfilter-flowtable-strictly-check-for-maximum-number-of-act.patch
|
||||
Patch1275: 1275-bluetooth-hci-add-initial-support-for-past.patch
|
||||
Patch1276: 1276-bluetooth-iso-add-support-to-bind-to-trigger-past.patch
|
||||
Patch1277: 1277-bluetooth-hci-add-support-for-ll-extended-feature-set.patch
|
||||
Patch1278: 1278-bluetooth-hci-conn-fix-using-conn-le-tx-rx-phy-as-supported-.patch
|
||||
Patch1279: 1279-bluetooth-l2cap-add-support-for-setting-bt-phy.patch
|
||||
Patch1280: 1280-bluetooth-hci-sync-hci-cmd-sync-queue-once-return-eexist-if-.patch
|
||||
Patch1281: 1281-bluetooth-hci-sync-fix-leaks-when-hci-cmd-sync-queue-once-fa.patch
|
||||
Patch1282: 1282-bluetooth-hci-sync-fix-uaf-in-le-read-features-complete.patch
|
||||
Patch1283: 1283-pnfs-fix-a-missing-wake-up-while-waiting-on-nfs-layout-drain.patch
|
||||
Patch1284: 1284-smb-client-fix-oob-reads-parsing-symlink-error-response.patch
|
||||
Patch1285: 1285-sched-deadline-fix-dl-server-time-accounting.patch
|
||||
Patch1286: 1286-redhat-configs-automotive-disable-config-io-uring.patch
|
||||
Patch1287: 1287-rhel-kabi-dpll-adaptation.patch
|
||||
# END OF PATCH DEFINITIONS
|
||||
|
||||
%description
|
||||
@ -2280,6 +2323,49 @@ ApplyPatch 1241-bluetooth-mgmt-validate-ltk-enc-size-on-load.patch
|
||||
ApplyPatch 1242-bluetooth-sco-fix-race-conditions-in-sco-sock-connect.patch
|
||||
ApplyPatch 1243-xfs-delete-attr-leaf-freemap-entries-when-empty.patch
|
||||
ApplyPatch 1244-xfs-fix-freemap-adjustments-when-adding-xattrs-to-leaf-block.patch
|
||||
ApplyPatch 1245-proc-use-the-same-treatment-to-check-proc-lseek-as-ones-for-.patch
|
||||
ApplyPatch 1246-proc-fix-missing-pde-set-flags-for-net-proc-files.patch
|
||||
ApplyPatch 1247-proc-fix-type-confusion-in-pde-set-flags.patch
|
||||
ApplyPatch 1248-nbd-defer-config-unlock-in-nbd-genl-connect.patch
|
||||
ApplyPatch 1249-crypto-authenc-correctly-pass-einprogress-back-up-to-the-cal.patch
|
||||
ApplyPatch 1250-dpll-zl3073x-detect-dpll-channel-count-from-chip-id-at-runti.patch
|
||||
ApplyPatch 1251-dpll-zl3073x-add-die-temperature-reporting-for-supported-chi.patch
|
||||
ApplyPatch 1252-dpll-zl3073x-use-struct-group-to-partition-states.patch
|
||||
ApplyPatch 1253-dpll-zl3073x-add-zl3073x-ref-state-update-helper.patch
|
||||
ApplyPatch 1254-dpll-zl3073x-introduce-zl3073x-chan-for-dpll-channel-state.patch
|
||||
ApplyPatch 1255-dpll-zl3073x-add-dpll-channel-status-fields-to-zl3073x-chan.patch
|
||||
ApplyPatch 1256-dpll-zl3073x-add-reference-priority-to-zl3073x-chan.patch
|
||||
ApplyPatch 1257-dpll-zl3073x-drop-selected-and-simplify-connected-ref-getter.patch
|
||||
ApplyPatch 1258-dpll-add-frequency-monitoring-to-netlink-spec.patch
|
||||
ApplyPatch 1259-dpll-add-frequency-monitoring-callback-ops.patch
|
||||
ApplyPatch 1260-dpll-zl3073x-implement-frequency-monitoring.patch
|
||||
ApplyPatch 1261-dpll-zl3073x-clean-up-esync-get-set-and-use-zl3073x-out-is-n.patch
|
||||
ApplyPatch 1262-dpll-zl3073x-use-field-modify-for-clear-and-set-patterns.patch
|
||||
ApplyPatch 1263-dpll-zl3073x-add-ref-sync-and-output-clock-type-helpers.patch
|
||||
ApplyPatch 1264-dpll-zl3073x-add-ref-sync-pair-support.patch
|
||||
ApplyPatch 1265-smb-client-validate-the-whole-dacl-before-rewriting-it-in-ci.patch
|
||||
ApplyPatch 1266-smb-client-require-a-full-nfs-mode-sid-before-reading-mode-b.patch
|
||||
ApplyPatch 1267-smb-client-scope-end-of-dacl-to-cifs-debug2-use-in-parse-dac.patch
|
||||
ApplyPatch 1268-smb-client-use-kzalloc-to-zero-initialize-security-descripto.patch
|
||||
ApplyPatch 1269-smb-client-validate-dacloffset-before-building-dacl-pointers.patch
|
||||
ApplyPatch 1270-dpll-add-pin-operational-state.patch
|
||||
ApplyPatch 1271-dpll-zl3073x-implement-pin-operational-state-reporting.patch
|
||||
ApplyPatch 1272-dpll-add-fractional-frequency-offset-to-pin-parent-device.patch
|
||||
ApplyPatch 1273-dpll-zl3073x-report-ffo-as-dpll-vs-input-reference-offset.patch
|
||||
ApplyPatch 1274-netfilter-flowtable-strictly-check-for-maximum-number-of-act.patch
|
||||
ApplyPatch 1275-bluetooth-hci-add-initial-support-for-past.patch
|
||||
ApplyPatch 1276-bluetooth-iso-add-support-to-bind-to-trigger-past.patch
|
||||
ApplyPatch 1277-bluetooth-hci-add-support-for-ll-extended-feature-set.patch
|
||||
ApplyPatch 1278-bluetooth-hci-conn-fix-using-conn-le-tx-rx-phy-as-supported-.patch
|
||||
ApplyPatch 1279-bluetooth-l2cap-add-support-for-setting-bt-phy.patch
|
||||
ApplyPatch 1280-bluetooth-hci-sync-hci-cmd-sync-queue-once-return-eexist-if-.patch
|
||||
ApplyPatch 1281-bluetooth-hci-sync-fix-leaks-when-hci-cmd-sync-queue-once-fa.patch
|
||||
ApplyPatch 1282-bluetooth-hci-sync-fix-uaf-in-le-read-features-complete.patch
|
||||
ApplyPatch 1283-pnfs-fix-a-missing-wake-up-while-waiting-on-nfs-layout-drain.patch
|
||||
ApplyPatch 1284-smb-client-fix-oob-reads-parsing-symlink-error-response.patch
|
||||
ApplyPatch 1285-sched-deadline-fix-dl-server-time-accounting.patch
|
||||
ApplyPatch 1286-redhat-configs-automotive-disable-config-io-uring.patch
|
||||
ApplyPatch 1287-rhel-kabi-dpll-adaptation.patch
|
||||
# END OF PATCH APPLICATIONS
|
||||
|
||||
# Any further pre-build tree manipulations happen here.
|
||||
@ -4784,6 +4870,58 @@ fi\
|
||||
#
|
||||
#
|
||||
%changelog
|
||||
* Sun Jun 07 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 6.12.0-211.20.1
|
||||
- Recreate RHEL 6.12.0-211.20.1 from CentOS Stream 10 and upstream stable backports (1245-1287)
|
||||
- smb cifs.spnego now shipped by RHEL too; existing ahead-fix 1105 is identical (RHEL's redundant copy omitted)
|
||||
- RHEL changelog for 211.19.1..211.20.1 follows:
|
||||
|
||||
* Tue Jun 02 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.20.1.el10_2]
|
||||
- smb: client: reject userspace cifs.spnego descriptions (Paulo Alcantara) [RHEL-178932] {CVE-2026-46243}
|
||||
- redhat/configs: automotive: disable CONFIG_IO_URING (Brian Masney) [RHEL-179469]
|
||||
- sched/deadline: Fix dl_server time accounting (Phil Auld) [RHEL-173950]
|
||||
- smb: client: fix OOB reads parsing symlink error response (CKI Backport Bot) [RHEL-171475] {CVE-2026-31613}
|
||||
- pNFS: fix a missing wake up while waiting on NFS_LAYOUT_DRAIN (Olga Kornievskaia) [RHEL-157466]
|
||||
|
||||
* Thu May 28 2026 CKI KWF Bot <cki-ci-bot+kwf-gitlab-com@redhat.com> [6.12.0-211.19.1.el10_2]
|
||||
- Bluetooth: hci_sync: Fix UAF in le_read_features_complete (David Marlin) [RHEL-176903] {CVE-2026-43322}
|
||||
- Bluetooth: hci_sync: fix leaks when hci_cmd_sync_queue_once fails (David Marlin) [RHEL-176903]
|
||||
- Bluetooth: hci_sync: hci_cmd_sync_queue_once() return -EEXIST if exists (David Marlin) [RHEL-176903]
|
||||
- Bluetooth: L2CAP: Add support for setting BT_PHY (David Marlin) [RHEL-176903]
|
||||
- Bluetooth: hci_conn: Fix using conn->le_{tx,rx}_phy as supported PHYs (David Marlin) [RHEL-176903]
|
||||
- Bluetooth: HCI: Add support for LL Extended Feature Set (David Marlin) [RHEL-176903]
|
||||
- Bluetooth: ISO: Add support to bind to trigger PAST (David Marlin) [RHEL-176903]
|
||||
- Bluetooth: HCI: Add initial support for PAST (David Marlin) [RHEL-176903]
|
||||
- netfilter: flowtable: strictly check for maximum number of actions (CKI Backport Bot) [RHEL-176915] {CVE-2026-43329}
|
||||
- dpll: zl3073x: report FFO as DPLL vs input reference offset (Ivan Vecera) [RHEL-175824]
|
||||
- dpll: add fractional frequency offset to pin-parent-device (Ivan Vecera) [RHEL-175824]
|
||||
- dpll: zl3073x: implement pin operational state reporting (Ivan Vecera) [RHEL-175821]
|
||||
- dpll: add pin operational state (Ivan Vecera) [RHEL-175821]
|
||||
- smb: client: validate dacloffset before building DACL pointers (Paulo Alcantara) [RHEL-172827]
|
||||
- smb: client: use kzalloc to zero-initialize security descriptor buffer (Paulo Alcantara) [RHEL-172827]
|
||||
- smb: client: scope end_of_dacl to CIFS_DEBUG2 use in parse_dacl (Paulo Alcantara) [RHEL-172827]
|
||||
- smb: client: require a full NFS mode SID before reading mode bits (Paulo Alcantara) [RHEL-172827]
|
||||
- smb: client: validate the whole DACL before rewriting it in cifsacl (Paulo Alcantara) [RHEL-172827] {CVE-2026-31709}
|
||||
- dpll: zl3073x: add ref-sync pair support (Ivan Vecera) [RHEL-167277]
|
||||
- dpll: zl3073x: add ref sync and output clock type helpers (Ivan Vecera) [RHEL-167277]
|
||||
- dpll: zl3073x: use FIELD_MODIFY() for clear-and-set patterns (Ivan Vecera) [RHEL-167277]
|
||||
- dpll: zl3073x: clean up esync get/set and use zl3073x_out_is_ndiv() (Ivan Vecera) [RHEL-167277]
|
||||
- dpll: zl3073x: implement frequency monitoring (Ivan Vecera) [RHEL-167837]
|
||||
- dpll: add frequency monitoring callback ops (Ivan Vecera) [RHEL-167837]
|
||||
- dpll: add frequency monitoring to netlink spec (Ivan Vecera) [RHEL-167837]
|
||||
- dpll: zl3073x: drop selected and simplify connected ref getter (Ivan Vecera) [RHEL-172930]
|
||||
- dpll: zl3073x: add reference priority to zl3073x_chan (Ivan Vecera) [RHEL-172930]
|
||||
- dpll: zl3073x: add DPLL channel status fields to zl3073x_chan (Ivan Vecera) [RHEL-172930]
|
||||
- dpll: zl3073x: introduce zl3073x_chan for DPLL channel state (Ivan Vecera) [RHEL-172930]
|
||||
- dpll: zl3073x: add zl3073x_ref_state_update helper (Ivan Vecera) [RHEL-172930]
|
||||
- dpll: zl3073x: use struct_group to partition states (Ivan Vecera) [RHEL-172930]
|
||||
- dpll: zl3073x: add die temperature reporting for supported chips (Ivan Vecera) [RHEL-172930]
|
||||
- dpll: zl3073x: detect DPLL channel count from chip ID at runtime (Ivan Vecera) [RHEL-172930]
|
||||
- crypto: authenc - Correctly pass EINPROGRESS back up to the caller (CKI Backport Bot) [RHEL-171302]
|
||||
- nbd: defer config unlock in nbd_genl_connect (CKI Backport Bot) [RHEL-166953] {CVE-2025-68366}
|
||||
- proc: fix type confusion in pde_set_flags() (Abhi Das) [RHEL-163345] {CVE-2025-38653}
|
||||
- proc: fix missing pde_set_flags() for net proc files (Abhi Das) [RHEL-163345] {CVE-2025-38653}
|
||||
- proc: use the same treatment to check proc_lseek as ones for proc_read_iter et.al (CKI Backport Bot) [RHEL-163345] {CVE-2025-38653}
|
||||
|
||||
* Sun Jun 07 2026 Andrew Lukoshko <alukoshko@almalinux.org> - 6.12.0-211.18.1
|
||||
- Recreate RHEL 6.12.0-211.18.1 from CentOS Stream 10 and upstream stable backports (1162-1244)
|
||||
- RHEL changelog for 211.17.1..211.18.1 follows:
|
||||
|
||||
Loading…
Reference in New Issue
Block a user