* [PATCH v7 09/22] RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Smcdeleg extension allows the M-mode to delegate selected counters
to S-mode so that it can access those counters and correpsonding
hpmevent CSRs without M-mode.
Ssccfg (‘Ss’ for Privileged architecture and Supervisor-level
extension, ‘ccfg’ for Counter Configuration) provides access to
delegated counters and new supervisor-level state.
This patch just enables these definitions and enable parsing.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/hwcap.h | 2 ++
arch/riscv/kernel/cpufeature.c | 24 ++++++++++++++++++++++++
2 files changed, 26 insertions(+)
diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index 51ad55b9677a..089353b250b0 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -115,6 +115,8 @@
#define RISCV_ISA_EXT_SSCSRIND 106
#define RISCV_ISA_EXT_SMCSRIND 107
#define RISCV_ISA_EXT_SMCNTRPMF 108
+#define RISCV_ISA_EXT_SSCCFG 109
+#define RISCV_ISA_EXT_SMCDELEG 110
#define RISCV_ISA_EXT_XLINUXENVCFG 127
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 1452521d740a..1fe647e03515 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -330,6 +330,27 @@ static const unsigned int riscv_a_exts[] = {
RISCV_ISA_EXT_ZKNE, \
RISCV_ISA_EXT_ZKNH
+static int riscv_ext_smcdeleg_validate(const struct riscv_isa_ext_data *data,
+ const unsigned long *isa_bitmap)
+{
+ if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SSCSRIND) &&
+ __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZIHPM) &&
+ __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_ZICNTR))
+ return 0;
+
+ return -EPROBE_DEFER;
+}
+
+static int riscv_ext_ssccfg_validate(const struct riscv_isa_ext_data *data,
+ const unsigned long *isa_bitmap)
+{
+ if (!riscv_ext_smcdeleg_validate(data, isa_bitmap) &&
+ __riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SMCDELEG))
+ return 0;
+
+ return -EPROBE_DEFER;
+}
+
static const unsigned int riscv_zk_bundled_exts[] = {
RISCV_ISA_EXT_ZKN,
RISCV_ISA_EXT_ZKR,
@@ -576,12 +597,15 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+ __RISCV_ISA_EXT_DATA_VALIDATE(smcdeleg, RISCV_ISA_EXT_SMCDELEG,
+ riscv_ext_smcdeleg_validate),
__RISCV_ISA_EXT_DATA(smcntrpmf, RISCV_ISA_EXT_SMCNTRPMF),
__RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),
__RISCV_ISA_EXT_DATA(smstateen, RISCV_ISA_EXT_SMSTATEEN),
__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
+ __RISCV_ISA_EXT_DATA_VALIDATE(ssccfg, RISCV_ISA_EXT_SSCCFG, riscv_ext_ssccfg_validate),
__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
__RISCV_ISA_EXT_DATA(sscsrind, RISCV_ISA_EXT_SSCSRIND),
__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 08/22] RISC-V: Add Sscfg extension CSR definition
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Kaiwen Xue <kaiwenx@rivosinc.com>
This adds the scountinhibit CSR definition and S-mode accessible hpmevent
bits defined by smcdeleg/ssccfg. scountinhibit allows S-mode to start/stop
counters directly from S-mode without invoking SBI calls to M-mode. It is
also used to figure out the counters delegated to S-mode by the M-mode as
well.
Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com>
Reviewed-by: Clément Léger <cleger@rivosinc.com>
---
arch/riscv/include/asm/csr.h | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index b4551a6cf7cb..26cb78dee2fd 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -241,6 +241,31 @@
#define SMSTATEEN0_HSENVCFG (_ULL(1) << SMSTATEEN0_HSENVCFG_SHIFT)
#define SMSTATEEN0_SSTATEEN0_SHIFT 63
#define SMSTATEEN0_SSTATEEN0 (_ULL(1) << SMSTATEEN0_SSTATEEN0_SHIFT)
+/* HPMEVENT bits. These are accessible in S-mode via Smcdeleg/Ssccfg */
+#ifdef CONFIG_64BIT
+#define HPMEVENT_OF (BIT_ULL(63))
+#define HPMEVENT_MINH (BIT_ULL(62))
+#define HPMEVENT_SINH (BIT_ULL(61))
+#define HPMEVENT_UINH (BIT_ULL(60))
+#define HPMEVENT_VSINH (BIT_ULL(59))
+#define HPMEVENT_VUINH (BIT_ULL(58))
+#else
+#define HPMEVENTH_OF (BIT_ULL(31))
+#define HPMEVENTH_MINH (BIT_ULL(30))
+#define HPMEVENTH_SINH (BIT_ULL(29))
+#define HPMEVENTH_UINH (BIT_ULL(28))
+#define HPMEVENTH_VSINH (BIT_ULL(27))
+#define HPMEVENTH_VUINH (BIT_ULL(26))
+
+#define HPMEVENT_OF (HPMEVENTH_OF << 32)
+#define HPMEVENT_MINH (HPMEVENTH_MINH << 32)
+#define HPMEVENT_SINH (HPMEVENTH_SINH << 32)
+#define HPMEVENT_UINH (HPMEVENTH_UINH << 32)
+#define HPMEVENT_VSINH (HPMEVENTH_VSINH << 32)
+#define HPMEVENT_VUINH (HPMEVENTH_VUINH << 32)
+#endif
+
+#define SISELECT_SSCCFG_BASE 0x40
/* mseccfg bits */
#define MSECCFG_PMM ENVCFG_PMM
@@ -322,6 +347,7 @@
#define CSR_SCOUNTEREN 0x106
#define CSR_SENVCFG 0x10a
#define CSR_SSTATEEN0 0x10c
+#define CSR_SCOUNTINHIBIT 0x120
#define CSR_SSCRATCH 0x140
#define CSR_SEPC 0x141
#define CSR_SCAUSE 0x142
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 07/22] dt-bindings: riscv: add Smcntrpmf ISA extension description
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Add the description for Smcntrpmf ISA extension
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
Documentation/devicetree/bindings/riscv/extensions.yaml | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 15cf0e2ee3ed..2493766e956d 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -181,6 +181,12 @@ properties:
changes to interrupts as frozen at commit ccbddab ("Merge pull
request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
+ - const: smcntrpmf
+ description: |
+ The standard Smcntrpmf machine-level extension for the machine mode
+ to enable privilege mode filtering for cycle and instret counters as
+ ratified in the 20240326 version of the privileged ISA specification.
+
- const: smcsrind
description: |
The standard Smcsrind machine-level extension extends the
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 06/22] RISC-V: Add Smcntrpmf extension parsing
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Smcntrpmf extension allows M-mode to enable privilege mode filtering
for cycle/instret counters. However, the cyclecfg/instretcfg CSRs are
only available only in Ssccfg only Smcntrpmf is present.
That's why, kernel needs to detect presence of Smcntrpmf extension and
enable privilege mode filtering for cycle/instret counters.
Reviewed-by: Clément Léger <cleger@rivosinc.com>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/hwcap.h | 1 +
arch/riscv/kernel/cpufeature.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index d4a7b90e2d78..51ad55b9677a 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -114,6 +114,7 @@
#define RISCV_ISA_EXT_ZICFISS 105
#define RISCV_ISA_EXT_SSCSRIND 106
#define RISCV_ISA_EXT_SMCSRIND 107
+#define RISCV_ISA_EXT_SMCNTRPMF 108
#define RISCV_ISA_EXT_XLINUXENVCFG 127
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index 3fa0a563fb21..1452521d740a 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -576,6 +576,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+ __RISCV_ISA_EXT_DATA(smcntrpmf, RISCV_ISA_EXT_SMCNTRPMF),
__RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 05/22] RISC-V: Define indirect CSR access helpers
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Atish Patra <atishp@rivosinc.com>
The indriect CSR requires multiple instructions to read/write CSR.
Add a few helper functions for ease of usage.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/csr_ind.h | 44 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/arch/riscv/include/asm/csr_ind.h b/arch/riscv/include/asm/csr_ind.h
new file mode 100644
index 000000000000..6fd7d44dc640
--- /dev/null
+++ b/arch/riscv/include/asm/csr_ind.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2024 Rivos Inc.
+ */
+
+#ifndef _ASM_RISCV_CSR_IND_H
+#define _ASM_RISCV_CSR_IND_H
+
+#include <linux/irqflags.h>
+
+#include <asm/csr.h>
+
+#define csr_ind_read(iregcsr, iselbase, iseloff) ({ \
+ unsigned long __value = 0; \
+ unsigned long __flags; \
+ local_irq_save(__flags); \
+ csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
+ __value = csr_read(iregcsr); \
+ local_irq_restore(__flags); \
+ __value; \
+})
+
+#define csr_ind_write(iregcsr, iselbase, iseloff, value) ({ \
+ unsigned long __flags; \
+ local_irq_save(__flags); \
+ csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
+ csr_write(iregcsr, (value)); \
+ local_irq_restore(__flags); \
+})
+
+#define csr_ind_warl(iregcsr, iselbase, iseloff, warl_val) ({ \
+ unsigned long __old_val = 0, __value = 0; \
+ unsigned long __flags; \
+ local_irq_save(__flags); \
+ csr_write(CSR_ISELECT, (iselbase) + (iseloff)); \
+ __old_val = csr_read(iregcsr); \
+ csr_write(iregcsr, (warl_val)); \
+ __value = csr_read(iregcsr); \
+ csr_write(iregcsr, __old_val); \
+ local_irq_restore(__flags); \
+ __value; \
+})
+
+#endif
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 03/22] RISC-V: Add Sxcsrind ISA extension definition and parsing
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Atish Patra <atishp@rivosinc.com>
The S[m|s]csrind extension extends the indirect CSR access mechanism
defined in Smaia/Ssaia extensions.
This patch just enables the definition and parsing.
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/hwcap.h | 4 ++++
arch/riscv/kernel/cpufeature.c | 2 ++
2 files changed, 6 insertions(+)
diff --git a/arch/riscv/include/asm/hwcap.h b/arch/riscv/include/asm/hwcap.h
index 7ef8e5f55c8d..d4a7b90e2d78 100644
--- a/arch/riscv/include/asm/hwcap.h
+++ b/arch/riscv/include/asm/hwcap.h
@@ -112,6 +112,8 @@
#define RISCV_ISA_EXT_ZCLSD 103
#define RISCV_ISA_EXT_ZICFILP 104
#define RISCV_ISA_EXT_ZICFISS 105
+#define RISCV_ISA_EXT_SSCSRIND 106
+#define RISCV_ISA_EXT_SMCSRIND 107
#define RISCV_ISA_EXT_XLINUXENVCFG 127
@@ -121,9 +123,11 @@
#ifdef CONFIG_RISCV_M_MODE
#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SMAIA
#define RISCV_ISA_EXT_SUPM RISCV_ISA_EXT_SMNPM
+#define RISCV_ISA_EXT_SxCSRIND RISCV_ISA_EXT_SMCSRIND
#else
#define RISCV_ISA_EXT_SxAIA RISCV_ISA_EXT_SSAIA
#define RISCV_ISA_EXT_SUPM RISCV_ISA_EXT_SSNPM
+#define RISCV_ISA_EXT_SxCSRIND RISCV_ISA_EXT_SSCSRIND
#endif
#endif /* _ASM_RISCV_HWCAP_H */
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index f46aa5602d74..3fa0a563fb21 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -576,11 +576,13 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
__RISCV_ISA_EXT_BUNDLE_VALIDATE(zvksg, riscv_zvksg_bundled_exts, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA_VALIDATE(zvkt, RISCV_ISA_EXT_ZVKT, riscv_ext_vector_crypto_validate),
__RISCV_ISA_EXT_DATA(smaia, RISCV_ISA_EXT_SMAIA),
+ __RISCV_ISA_EXT_DATA(smcsrind, RISCV_ISA_EXT_SMCSRIND),
__RISCV_ISA_EXT_DATA(smmpm, RISCV_ISA_EXT_SMMPM),
__RISCV_ISA_EXT_SUPERSET(smnpm, RISCV_ISA_EXT_SMNPM, riscv_xlinuxenvcfg_exts),
__RISCV_ISA_EXT_DATA(smstateen, RISCV_ISA_EXT_SMSTATEEN),
__RISCV_ISA_EXT_DATA(ssaia, RISCV_ISA_EXT_SSAIA),
__RISCV_ISA_EXT_DATA(sscofpmf, RISCV_ISA_EXT_SSCOFPMF),
+ __RISCV_ISA_EXT_DATA(sscsrind, RISCV_ISA_EXT_SSCSRIND),
__RISCV_ISA_EXT_SUPERSET(ssnpm, RISCV_ISA_EXT_SSNPM, riscv_xlinuxenvcfg_exts),
__RISCV_ISA_EXT_DATA(sstc, RISCV_ISA_EXT_SSTC),
__RISCV_ISA_EXT_DATA(svade, RISCV_ISA_EXT_SVADE),
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 04/22] dt-bindings: riscv: add Sxcsrind ISA extension description
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Atish Patra <atishp@rivosinc.com>
Add the S[m|s]csrind ISA extension description.
Acked-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
Documentation/devicetree/bindings/riscv/extensions.yaml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 2b0a8a93bb21..15cf0e2ee3ed 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -181,6 +181,14 @@ properties:
changes to interrupts as frozen at commit ccbddab ("Merge pull
request #42 from riscv/jhauser-2023-RC4") of riscv-aia.
+ - const: smcsrind
+ description: |
+ The standard Smcsrind machine-level extension extends the
+ indirect CSR access mechanism defined by the Smaia extension. This
+ extension allows other ISA extensions to use indirect CSR access
+ mechanism in M-mode as ratified in the 20240326 version of the
+ privileged ISA specification.
+
- const: smmpm
description: |
The standard Smmpm extension for M-mode pointer masking as
@@ -226,6 +234,14 @@ properties:
Profiles Version 1.0, with commit b1d806605f87 ("Updated to
ratified state.")
+ - const: sscsrind
+ description: |
+ The standard Sscsrind supervisor-level extension extends the
+ indirect CSR access mechanism defined by the Ssaia extension. This
+ extension allows other ISA extensions to use indirect CSR access
+ mechanism in S-mode as ratified in the 20240326 version of the
+ privileged ISA specification.
+
- const: ssnpm
description: |
The standard Ssnpm extension for next-mode pointer masking as
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 00/22] Add Counter delegation ISA extension support
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
This series adds the counter delegation extension support. It is based on
very early PoC work done by Kevin Xue and mostly rewritten after that.
The counter delegation ISA extension(Smcdeleg/Ssccfg) actually depends
on multiple ISA extensions.
1. S[m|s]csrind : The indirect CSR extension[1] which defines additional
5 ([M|S|VS]IREG2-[M|S|VS]IREG6) register to address size limitation of
RISC-V CSR address space.
2. Smstateen: The stateen bit[60] controls the access to the registers
indirectly via the above indirect registers.
3. Smcdeleg/Ssccfg: The counter delegation extensions[2]
The counter delegation extension allows Supervisor mode to program the
hpmevent and hpmcounters directly without needing the assistance from the
M-mode via SBI calls. This results in a faster perf profiling and very
few traps. This extension also introduces a scountinhibit CSR which allows
to stop/start any counter directly from the S-mode. As the counter
delegation extension potentially can have more than 100 CSRs, the specification
leverages the indirect CSR extension to save the precious CSR address range.
Due to the dependency of these extensions, the following extensions must be
enabled in qemu to use the counter delegation feature in S-mode.
"smstateen=true,sscofpmf=true,ssccfg=true,smcdeleg=true,smcsrind=true,sscsrind=true"
or Virt machine users can just "max" cpu instead.
When we access the counters directly in S-mode, we also need to solve the
following problems.
1. Event to counter mapping
2. Event encoding discovery
The RISC-V ISA doesn't define any standard either for event encoding or the
event to counter mapping rules. Until now, the SBI PMU implementation relies
on device tree binding[3] to discover the event to counter mapping in RISC-V
platform in the firmware. The SBI PMU specification[4] defines event encoding
for standard perf events as well. Thus, the kernel can query the appropriate
counter for an given event from the firmware.
However, the kernel doesn't need any firmware interaction for hardware
counters if counter delegation is available in the hardware. Thus, the driver
needs to discover the above mappings/encodings by itself without any assistance
from firmware.
Solution to Problem #1:
This patch series solves the above problem #1 by extending the perf tool in a
way so that event json file can specify the counter constraints of each event
and that can be passed to the driver to choose the best counter for a given
event.
This series introduces a RISC-V specific event field in "CounterIDMask" in
event_fields that describes a bitmask of counters supported for a specific eventi.
This is the similar approach for few other existing properties in the event_fields
which were used by single architecture as well. The counter constraint bitmap is
passed to the perf driver via newly introduced "counterid_mask" property set in "config2".
The platform vendor have these three ways to encode/use the platform specific
events.
1. Directly in driver with appropriate constraints (discouraged due to bloating
of the driver)
2. Encode in Json with with CounterIDMask field (preferred as it is contained
within platform specific json file)
3. Directly pass counterid_mask at while invoking perf
cpu/event=<code>,counterid_mask=<mask>/
The last two patches show cases these use cases and not intended for merging.
Solution to problem #2:
The event encoding can come from the json or commandline as well.
The Qemu patches are available in upstream now.
The Linux kernel patches can be found here:
https://github.com/atishp04/linux/tree/b4/counter_delegation_v7
[1] https://github.com/riscv/riscv-indirect-csr-access
[2] https://github.com/riscv/riscv-smcdeleg-ssccfg
[3] https://www.kernel.org/doc/Documentation/devicetree/bindings/perf/riscv%2Cpmu.yaml
[4] https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/src/ext-pmu.adoc
To: Paul Walmsley <pjw@kernel.org>
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Will Deacon <will@kernel.org>
To: Mark Rutland <mark.rutland@arm.com>
To: Atish Patra <atish.patra@linux.dev>
To: Anup Patel <anup@brainfault.org>
To: Arnaldo Carvalho de Melo <acme@kernel.org>
To: Namhyung Kim <namhyung@kernel.org>
To: Jiri Olsa <jolsa@kernel.org>
To: Ian Rogers <irogers@google.com>
To: James Clark <james.clark@linaro.org>
Cc: linux-riscv@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
Cc: Conor Dooley <conor@kernel.org>
Cc: devicetree@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-perf-users@vger.kernel.org
Signed-off-by: Atish Patra <atishp@meta.com>
Changes in v7:
- Fixed various issues pointed by Sashiko.
- Rebased on top of v7.1
- Added a separate patch to fix various memory leak issues in probe error path
- Link to v6: https://lore.kernel.org/r/20260608-counter_delegation-v6-0-285b72ed65a9@meta.com
Changes in v6:
- Reverted the file name changes as suggested by Will. Now pmu-sbi.c will continue
to support both counter delegation and SBI PMU with different function prefixes.
- No longer depends up old upstream patch for reusing the Counter property to
encode event to counter mapping property. It directly uses additional field in
json event fields similar to other architectures.
- Added few test patches (not intended for upstreaming) to show case different
possibilities of providing mapping/event encodings.
- Fixed review comments and miscellenous minor typos/fixes on v5
- Rebased on top of v7.1-rc6
Changes in v5:
- Fixed dt_binding_check errors.
- Added the ISA extension dependancy for counter delegation extensions.
- Replaced the boolean variables with static key conditional check required at boot time.
- Miscellaneous minor code restructuring.
- Link to v4: https://lore.kernel.org/r/20250205-counter_delegation-v4-0-835cfa88e3b1@rivosinc.com
Changes in v4:
- Added ISA dependencies as per dt schema instead of description.
- Fixed few compilation issues due to patch reordering in v3.
- Link to v3: https://lore.kernel.org/r/20250127-counter_delegation-v3-0-64894d7e16d5@rivosinc.com
Changes in v3:
- Fixed the dtb binding check failures.
- Inlcuded the fix reported by Rajnesh Kanwal for guest counter overflow.
- Rearranged the overflow handling more efficiently for better modularity.
- Link to v2: https://lore.kernel.org/r/20250114-counter_delegation-v2-0-8ba74cdb851b@rivosinc.com
Changes in v2:
- Dropped architecture specific overrides for event encoding.
- Dropped hwprobe bits.
- Added a vendor specific event encoding table to support vendor specific event
encoding and counter mapping.
- Fixed few bugs and cleanup.
- Link to v1: https://lore.kernel.org/r/20240217005738.3744121-1-atishp@rivosinc.com
---
Atish Patra (19):
RISC-V: perf: fix resource cleanup on driver probe failure
RISC-V: Add Sxcsrind ISA extension definition and parsing
dt-bindings: riscv: add Sxcsrind ISA extension description
RISC-V: Define indirect CSR access helpers
RISC-V: Add Smcntrpmf extension parsing
dt-bindings: riscv: add Smcntrpmf ISA extension description
RISC-V: Add Ssccfg/Smcdeleg ISA extension definition and parsing
dt-bindings: riscv: add Counter delegation ISA extensions description
RISC-V: perf: Restructure the SBI PMU code
RISC-V: perf: Modify the counter discovery mechanism
RISC-V: perf: Add a mechanism to defined legacy event encoding
RISC-V: perf: Implement supervisor counter delegation support
RISC-V: perf: Use config2/vendor table for event to counter mapping
RISC-V: perf: Add legacy event encodings via sysfs
RISC-V: perf: Add Qemu virt machine events
tools/perf: Support event code for arch standard events
tools/perf: Add RISC-V CounterIDMask event field
TEST(do-not-upstream): fake qemu-virt PMU events for cdeleg counter-mask testing
TEST(do-not-upstream): fake qemu vendor JSON + mapfile entry for CounterIDMask path
Charlie Jenkins (1):
RISC-V: perf: Skip PMU SBI extension when not implemented
Kaiwen Xue (2):
RISC-V: Add Sxcsrind ISA extension CSR definitions
RISC-V: Add Sscfg extension CSR definition
.../devicetree/bindings/riscv/extensions.yaml | 63 ++
arch/riscv/include/asm/csr.h | 57 +
arch/riscv/include/asm/csr_ind.h | 44 +
arch/riscv/include/asm/hwcap.h | 7 +
arch/riscv/include/asm/vendorid_list.h | 4 +
arch/riscv/kernel/cpufeature.c | 27 +
drivers/perf/Kconfig | 14 +-
drivers/perf/riscv_pmu_sbi.c | 1095 ++++++++++++++++----
include/linux/perf/riscv_pmu.h | 5 +
.../perf/pmu-events/arch/riscv/arch-standard.json | 10 +
tools/perf/pmu-events/arch/riscv/mapfile.csv | 1 +
.../pmu-events/arch/riscv/qemu/virt/events.json | 26 +
tools/perf/pmu-events/jevents.py | 7 +-
13 files changed, 1155 insertions(+), 205 deletions(-)
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20240715-counter_delegation-628a32f8c9cc
Best regards,
--
Atish Patra <atishp@meta.com>
^ permalink raw reply
* [PATCH v7 02/22] RISC-V: Add Sxcsrind ISA extension CSR definitions
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Kaiwen Xue <kaiwenx@rivosinc.com>
This adds definitions of new CSRs and bits defined in Sxcsrind ISA
extension. These CSR enables indirect accesses mechanism to access
any CSRs in M-, S-, and VS-mode. The range of the select values
and ireg will be define by the ISA extension using Sxcsrind extension.
Signed-off-by: Kaiwen Xue <kaiwenx@rivosinc.com>
Reviewed-by: Clément Léger <cleger@rivosinc.com>
Signed-off-by: Atish Patra <atishp@rivosinc.com>
---
arch/riscv/include/asm/csr.h | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h
index 31b8988f4488..b4551a6cf7cb 100644
--- a/arch/riscv/include/asm/csr.h
+++ b/arch/riscv/include/asm/csr.h
@@ -347,6 +347,12 @@
/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */
#define CSR_SISELECT 0x150
#define CSR_SIREG 0x151
+/* Supervisor-Level Window to Indirectly Accessed Registers (Sxcsrind) */
+#define CSR_SIREG2 0x152
+#define CSR_SIREG3 0x153
+#define CSR_SIREG4 0x155
+#define CSR_SIREG5 0x156
+#define CSR_SIREG6 0x157
/* Supervisor-Level Interrupts (AIA) */
#define CSR_STOPEI 0x15c
@@ -394,6 +400,14 @@
/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */
#define CSR_VSISELECT 0x250
#define CSR_VSIREG 0x251
+/*
+ * VS-Level Window to Indirectly Accessed Registers (H-extension with Sxcsrind)
+ */
+#define CSR_VSIREG2 0x252
+#define CSR_VSIREG3 0x253
+#define CSR_VSIREG4 0x255
+#define CSR_VSIREG5 0x256
+#define CSR_VSIREG6 0x257
/* VS-Level Interrupts (H-extension with AIA) */
#define CSR_VSTOPEI 0x25c
@@ -436,6 +450,12 @@
/* Machine-Level Window to Indirectly Accessed Registers (AIA) */
#define CSR_MISELECT 0x350
#define CSR_MIREG 0x351
+/* Machine-Level Window to Indirectly Accessed Registers (Sxcsrind) */
+#define CSR_MIREG2 0x352
+#define CSR_MIREG3 0x353
+#define CSR_MIREG4 0x355
+#define CSR_MIREG5 0x356
+#define CSR_MIREG6 0x357
/* Machine-Level Interrupts (AIA) */
#define CSR_MTOPEI 0x35c
@@ -498,6 +518,11 @@
# define CSR_IEH CSR_MIEH
# define CSR_ISELECT CSR_MISELECT
# define CSR_IREG CSR_MIREG
+# define CSR_IREG2 CSR_MIREG2
+# define CSR_IREG3 CSR_MIREG3
+# define CSR_IREG4 CSR_MIREG4
+# define CSR_IREG5 CSR_MIREG5
+# define CSR_IREG6 CSR_MIREG6
# define CSR_IPH CSR_MIPH
# define CSR_TOPEI CSR_MTOPEI
# define CSR_TOPI CSR_MTOPI
@@ -523,6 +548,11 @@
# define CSR_IEH CSR_SIEH
# define CSR_ISELECT CSR_SISELECT
# define CSR_IREG CSR_SIREG
+# define CSR_IREG2 CSR_SIREG2
+# define CSR_IREG3 CSR_SIREG3
+# define CSR_IREG4 CSR_SIREG4
+# define CSR_IREG5 CSR_SIREG5
+# define CSR_IREG6 CSR_SIREG6
# define CSR_IPH CSR_SIPH
# define CSR_TOPEI CSR_STOPEI
# define CSR_TOPI CSR_STOPI
--
2.53.0-Meta
^ permalink raw reply related
* [PATCH v7 01/22] RISC-V: perf: fix resource cleanup on driver probe failure
From: Atish Patra @ 2026-06-22 8:04 UTC (permalink / raw)
To: Jiri Olsa, James Clark, Mark Rutland, Will Deacon,
Arnaldo Carvalho de Melo, Rob Herring, Ian Rogers,
Krzysztof Kozlowski, Anup Patel, Paul Walmsley, Atish Patra,
Namhyung Kim
Cc: devicetree, linux-perf-users, Conor Dooley, linux-arm-kernel,
linux-kernel, linux-riscv
In-Reply-To: <20260622-counter_delegation-v7-0-0ba2fd34614e@meta.com>
From: Atish Patra <atishp@meta.com>
Sashiko pointed out various UAF and memory leak issues around
pmu_sbi_device_probe() error paths.
If the probe fails, here are list of cleanups needed.
a. Already registered pmu must be freed
b. per cpu IRQ must be released
c. pmu_ctr_list data structure must be freed
d. cpu hotplug state must be cleaned up only if added.
Fix the resource cleanup by reorganizing the code around probe failure.
Reported-by: Sashiko AI <sashiko-bot@kernel.org>
Signed-off-by: Atish Patra <atishp@meta.com>
---
drivers/perf/riscv_pmu_sbi.c | 17 ++++++++++++++---
1 file changed, 14 insertions(+), 3 deletions(-)
diff --git a/drivers/perf/riscv_pmu_sbi.c b/drivers/perf/riscv_pmu_sbi.c
index 385af5e6e6d0..8753007cc57e 100644
--- a/drivers/perf/riscv_pmu_sbi.c
+++ b/drivers/perf/riscv_pmu_sbi.c
@@ -1302,7 +1302,8 @@ static void riscv_pmu_destroy(struct riscv_pmu *pmu)
}
}
riscv_pm_pmu_unregister(pmu);
- cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
+ if (!hlist_unhashed(&pmu->node))
+ cpuhp_state_remove_instance(CPUHP_AP_PERF_RISCV_STARTING, &pmu->node);
}
static void pmu_sbi_event_init(struct perf_event *event)
@@ -1424,6 +1425,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
struct riscv_pmu *pmu = NULL;
int ret = -ENODEV;
int num_counters;
+ bool irq_requested = false;
pr_info("SBI PMU extension is available\n");
pmu = riscv_pmu_alloc();
@@ -1452,6 +1454,7 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_INTERRUPT;
pmu->pmu.capabilities |= PERF_PMU_CAP_NO_EXCLUDE;
}
+ irq_requested = (ret == 0);
pmu->pmu.attr_groups = riscv_pmu_attr_groups;
pmu->pmu.parent = &pdev->dev;
@@ -1470,11 +1473,11 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
ret = riscv_pm_pmu_register(pmu);
if (ret)
- goto out_unregister;
+ goto out_destroy;
ret = perf_pmu_register(&pmu->pmu, "cpu", PERF_TYPE_RAW);
if (ret)
- goto out_unregister;
+ goto out_destroy;
/* SBI PMU Snapsphot is only available in SBI v2.0 */
if (sbi_v2_available) {
@@ -1515,9 +1518,17 @@ static int pmu_sbi_device_probe(struct platform_device *pdev)
return 0;
out_unregister:
+ perf_pmu_unregister(&pmu->pmu);
+
+out_destroy:
riscv_pmu_destroy(pmu);
+ if (irq_requested)
+ free_percpu_irq(riscv_pmu_irq, pmu->hw_events);
out_free:
+ free_percpu(pmu->hw_events);
+ kfree(pmu_ctr_list);
+ pmu_ctr_list = NULL;
kfree(pmu);
return ret;
}
--
2.53.0-Meta
^ permalink raw reply related
* Re: [PATCH v10 2/2] media: i2c: add Himax HM1246 image sensor driver
From: Matthias Fend @ 2026-06-22 7:58 UTC (permalink / raw)
To: Sakari Ailus
Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Hans Verkuil, Hans de Goede, Ricardo Ribalda,
André Apitzsch, Tarang Raval, Andy Shevchenko,
Benjamin Mugnier, Sylvain Petinot, Dongcheng Yan,
Bryan O'Donoghue, Alan Stern, Jingjing Xiong,
Heimir Thor Sverrisson, Mehdi Djait, Vladimir Zapolskiy,
Laurent Pinchart, Hardevsinh Palaniya, Svyatoslav Ryhel,
Philipp Zabel, Hans Verkuil, Hans de Goede, Xiaolei Wang,
Walter Werner Schneider, Kate Hsuan, Bartosz Golaszewski,
Miguel Vadillo, linux-media, devicetree, linux-kernel, Hao Yao,
Himanshu Bhavani
In-Reply-To: <ajZcTs5MoTmFbmmz@kekkonen.localdomain>
Hi Sakari,
thanks for your feedback and the suggested improvements.
Am 20.06.2026 um 11:24 schrieb Sakari Ailus:
> Hi Matthias,
>
> On Fri, Jun 19, 2026 at 02:28:55PM +0200, Matthias Fend wrote:
>> Add a V4L2 sub-device driver for Himax HM1246 image sensor.
>>
>> The Himax HM1246-AWD is a 1/3.7-Inch CMOS image sensor SoC with an active
>> array size of 1296 x 976. It is programmable through an I2C interface and
>> connected via parallel bus.
>>
>> The sensor has an internal ISP with a complete image processing pipeline
>> including control loops. However, this driver uses the sensor in raw mode
>> and the entire ISP is bypassed.
>
> Is there possibly a need to support the ISP later on? Some drivers such as
> the mt9m114 expose a separate sub-device for it and adding one would be a
> UAPI change. Is there more information on the ISP in the hm1246?
The ISP is quite complete and provides a full processing pipeline,
including black level correction, auto white balance, auto exposure,
lens shading correction, color correction, sharpening, noise reduction,
scaler, ...
An overview of the ISP can be found on page 19 of the datasheet [1].
Unfortunately, the ISP blocks themselves are not described in great
detail in the datasheet. Properly integrating a driver for the ISP will
likely require some additional information or, alternatively, some
experimentation.
If the goal is simply to add a few more controls, such as for automatic
gain control, the architecture can probably remain as is. However, if
the entire ISP with statistics and a parameter buffer is to be
implemented, the architecture will certainly need to be changed. This
will, of course, also require a corresponding implementation on the user
interface.
Fully implementing the ISP would be quite a load of work and I have no
plans to do so.
>
> A few more minor comments below.
I think I understand the comments and will send a new version with these
changes.
Thanks!
~Matthias
[1]
https://www.himax.com.tw/wp-content/uploads/2024/03/HM1246-AWD_DS_v01.pdf
>
>>
>> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Signed-off-by: Matthias Fend <matthias.fend@emfend.at>
>> ---
>> MAINTAINERS | 1 +
>> drivers/media/i2c/Kconfig | 10 +
>> drivers/media/i2c/Makefile | 1 +
>> drivers/media/i2c/hm1246.c | 1291 ++++++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 1303 insertions(+)
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index c320e7e9a2bd88319945bfe1202bc891d35f0f7c..ca13a5b21306e921e077e2548f6242041c6ee24c 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -11558,6 +11558,7 @@ M: Matthias Fend <matthias.fend@emfend.at>
>> L: linux-media@vger.kernel.org
>> S: Maintained
>> F: Documentation/devicetree/bindings/media/i2c/himax,hm1246.yaml
>> +F: drivers/media/i2c/hm1246.c
>>
>> HIMAX HX83112B TOUCHSCREEN SUPPORT
>> M: Job Noorman <job@noorman.info>
>> diff --git a/drivers/media/i2c/Kconfig b/drivers/media/i2c/Kconfig
>> index 5d173e0ecf424f2f204f8d426be818e44357f8e4..52398bad32a0989c40097524ee9dde04e76813b8 100644
>> --- a/drivers/media/i2c/Kconfig
>> +++ b/drivers/media/i2c/Kconfig
>> @@ -137,6 +137,16 @@ config VIDEO_HI847
>> To compile this driver as a module, choose M here: the
>> module will be called hi847.
>>
>> +config VIDEO_HM1246
>> + tristate "Himax HM1246 sensor support"
>> + select V4L2_CCI_I2C
>> + help
>> + This is a Video4Linux2 sensor driver for the Himax
>> + HM1246 camera.
>> +
>> + To compile this driver as a module, choose M here: the
>> + module will be called hm1246.
>> +
>> config VIDEO_IMX111
>> tristate "Sony IMX111 sensor support"
>> select V4L2_CCI_I2C
>> diff --git a/drivers/media/i2c/Makefile b/drivers/media/i2c/Makefile
>> index e45359efe0e41e13e3c0869e5ead7d6cf4aca3a7..df420ff4e1d6304ef62f9cd84c8ddb9e2db30a11 100644
>> --- a/drivers/media/i2c/Makefile
>> +++ b/drivers/media/i2c/Makefile
>> @@ -45,6 +45,7 @@ obj-$(CONFIG_VIDEO_GC2145) += gc2145.o
>> obj-$(CONFIG_VIDEO_HI556) += hi556.o
>> obj-$(CONFIG_VIDEO_HI846) += hi846.o
>> obj-$(CONFIG_VIDEO_HI847) += hi847.o
>> +obj-$(CONFIG_VIDEO_HM1246) += hm1246.o
>> obj-$(CONFIG_VIDEO_I2C) += video-i2c.o
>> obj-$(CONFIG_VIDEO_IMX111) += imx111.o
>> obj-$(CONFIG_VIDEO_IMX208) += imx208.o
>> diff --git a/drivers/media/i2c/hm1246.c b/drivers/media/i2c/hm1246.c
>> new file mode 100644
>> index 0000000000000000000000000000000000000000..ba120967814f3b2b4a5d2d6b1b6e4f5e5139944f
>> --- /dev/null
>> +++ b/drivers/media/i2c/hm1246.c
>> @@ -0,0 +1,1291 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Driver for Himax HM1246 image sensor
>> + *
>> + * Copyright 2026 Matthias Fend <matthias.fend@emfend.at>
>> + */
>> +
>> +#include <linux/array_size.h>
>> +#include <linux/bitops.h>
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/device.h>
>> +#include <linux/err.h>
>> +#include <linux/i2c.h>
>> +#include <linux/limits.h>
>> +#include <linux/math.h>
>> +#include <linux/math64.h>
>> +#include <linux/module.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/property.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/reset.h>
>> +#include <linux/types.h>
>> +#include <linux/units.h>
>> +#include <media/media-entity.h>
>> +#include <media/v4l2-async.h>
>> +#include <media/v4l2-cci.h>
>> +#include <media/v4l2-common.h>
>> +#include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-fwnode.h>
>> +#include <media/v4l2-subdev.h>
>> +
>> +/* Status registers */
>> +#define HM1246_MODEL_ID_REG CCI_REG16(0x0000)
>> +
>> +/* General setup registers */
>> +#define HM1246_MODE_SELECT_REG CCI_REG8(0x0100)
>> +#define HM1246_MODE_SELECT_STANDBY 0x00
>> +#define HM1246_MODE_SELECT_STREAM 0x01
>> +#define HM1246_MODE_SELECT_STOP 0x02
>> +#define HM1246_IMAGE_ORIENTATION_REG CCI_REG8(0x0101)
>> +#define HM1246_IMAGE_ORIENTATION_VFLIP BIT(1)
>> +#define HM1246_IMAGE_ORIENTATION_HFLIP BIT(0)
>> +#define HM1246_CMU_UPDATE_REG CCI_REG8(0x0104)
>> +
>> +/* Output setup registers */
>> +#define HM1246_COARSE_INTG_REG CCI_REG16(0x0202)
>> +#define HM1246_ANALOG_GLOBAL_GAIN_REG CCI_REG8(0x0205)
>> +
>> +/* Clock setup registers */
>> +#define HM1246_PLL1CFG_REG CCI_REG8(0x0303)
>> +#define HM1246_PLL1CFG_MULTIPLIER(x) (((x) & 0xff) << 0)
>> +#define HM1246_PLL2CFG_REG CCI_REG8(0x0305)
>> +#define HM1246_PLL2CFG_PRE_DIV(x) (((x) & 0x1f) << 1)
>> +#define HM1246_PLL2CFG_MULTIPLIER(x) (((x) & 0x01) << 0)
>> +#define HM1246_PLL3CFG_REG CCI_REG8(0x0307)
>> +#define HM1246_PLL3CFG_POST_DIV(x) (((x) & 0x3) << 6)
>> +#define HM1246_PLL3CFG_SYSCLK_DIV(x) (((x) & 0x3) << 4)
>> +#define HM1246_PLL3CFG_PCLK_DIV(x) (((x) & 0x7) << 0)
>> +
>> +/* Frame timing registers */
>> +#define HM1246_FRAME_LENGTH_LINES_REG CCI_REG16(0x0340)
>> +#define HM1246_LINE_LENGTH_PCK_REG CCI_REG16(0x0342)
>> +
>> +/* Image size registers */
>> +#define HM1246_X_ADDR_START_REG CCI_REG16(0x0344)
>> +#define HM1246_Y_ADDR_START_REG CCI_REG16(0x0346)
>> +#define HM1246_X_ADDR_END_REG CCI_REG16(0x0348)
>> +#define HM1246_Y_ADDR_END_REG CCI_REG16(0x034a)
>> +#define HM1246_X_LA_START_REG CCI_REG16(0x0351)
>> +#define HM1246_X_LA_END_REG CCI_REG16(0x0353)
>> +#define HM1246_Y_LA_START_REG CCI_REG16(0x0355)
>> +#define HM1246_Y_LA_END_REG CCI_REG16(0x0357)
>> +
>> +/* Test pattern registers */
>> +#define HM1246_TEST_PATTERN_MODE_REG CCI_REG8(0x0601)
>> +#define HM1246_TEST_PATTERN_MODE_MODE(x) (((x) & 0xf) << 4)
>> +#define HM1246_TEST_PATTERN_MODE_ENABLE BIT(0)
>> +#define HM1246_TEST_DATA_BLUE_REG CCI_REG16(0x0602)
>> +#define HM1246_TEST_DATA_GB_REG CCI_REG16(0x0604)
>> +#define HM1246_TEST_DATA_RED_REG CCI_REG16(0x0606)
>> +#define HM1246_TEST_DATA_GR_REG CCI_REG16(0x0608)
>> +
>> +/* SBC registers */
>> +#define HM1246_SBC_BOOT_REF2_REG CCI_REG8(0x2001)
>> +#define HM1246_SBC_BOOT_REF2_PLL_LOCK BIT(4)
>> +#define HM1246_SBC_CTRL_REG CCI_REG8(0x2003)
>> +#define HM1246_SBC_CTRL_PLL_EN BIT(0)
>> +
>> +/* System registers */
>> +#define HM1246_OUTPUT_PRT_CTRL_REG CCI_REG8(0x2f02)
>> +#define HM1246_POLARITY_CTRL_REG CCI_REG8(0x2f20)
>> +#define HM1246_POLARITY_CTRL_HSYNC BIT(7)
>> +#define HM1246_POLARITY_CTRL_VSYNC BIT(6)
>> +#define HM1246_PCLK_CTRL_REG CCI_REG8(0x2f24)
>> +#define HM1246_PCLK_CTRL_POL BIT(3)
>> +
>> +/* Digital window control & parameter registers */
>> +#define HM1246_DWIN_XOFFSET_REG CCI_REG16(0xd5e4)
>> +#define HM1246_DWIN_XSIZE_REG CCI_REG16(0xd5e6)
>> +#define HM1246_DWIN_YOFFSET_REG CCI_REG16(0xd5e8)
>> +#define HM1246_DWIN_YSIZE_REG CCI_REG16(0xd5ea)
>> +
>> +#define HM1246_MODEL_ID 0x1245
>> +
>> +#define HM1246_NATIVE_WIDTH 1296
>> +#define HM1246_NATIVE_HEIGHT 976
>> +
>> +#define HM1246_VTS_MAX 65535
>> +
>> +#define HM1246_COARSE_INTG_MARGIN 2
>> +#define HM1246_COARSE_INTG_MIN 4
>> +#define HM1246_COARSE_INTG_STEP 1
>> +
>> +#define HM1246_ANALOG_GLOBAL_GAIN_MIN 0x00
>> +#define HM1246_ANALOG_GLOBAL_GAIN_MAX 0xe8
>> +#define HM1246_ANALOG_GLOBAL_GAIN_STEP 0x01
>> +
>> +#define HM1246_XCLK_MIN (6 * HZ_PER_MHZ)
>> +#define HM1246_XCLK_MAX (27 * HZ_PER_MHZ)
>> +
>> +#define HM1246_PCLK_MIN (8 * HZ_PER_MHZ)
>> +#define HM1246_PCLK_MAX (96 * HZ_PER_MHZ)
>> +
>> +#define HM1246_PLL_VCO_MIN (360 * HZ_PER_MHZ)
>> +#define HM1246_PLL_VCO_MAX (680 * HZ_PER_MHZ)
>> +
>> +#define HM1246_PLL_INCLK_MIN (1000 * HZ_PER_KHZ)
>> +#define HM1246_PLL_INCLK_MAX (2500 * HZ_PER_KHZ)
>> +
>> +#define HM1246_PLL_MULTI_L_MIN 1
>> +#define HM1246_PLL_MULTI_L_MAX 256
>> +
>> +#define HM1246_PLL_MULTI_H_MIN 2
>> +#define HM1246_PLL_MULTI_H_MAX 3
>> +
>> +#define HM1246_PLL_MULTI_MIN \
>> + (HM1246_PLL_MULTI_H_MIN * HM1246_PLL_MULTI_L_MIN)
>> +#define HM1246_PLL_MULTI_MAX \
>> + (HM1246_PLL_MULTI_H_MAX * HM1246_PLL_MULTI_L_MAX)
>> +
>> +static const char *const hm1246_test_pattern_menu[] = {
>> + "Disabled",
>> + "Checkboard",
>
> Checkerboard?
>
>> + "Ramp",
>> + "Moving ones",
>> + "Blending color bars",
>> + "Color bars",
>> + "Solid white",
>> + "Solid black",
>> + "Solid red",
>> + "Solid green",
>> + "Solid blue",
>> +};
>> +
>> +static const char *const hm1246_supply_names[] = {
>> + "avdd",
>> + "iovdd",
>> + "dvdd",
>> +};
>> +
>> +struct hm1246 {
>> + struct v4l2_subdev sd;
>> + struct media_pad pad;
>> + struct device *dev;
>> +
>> + struct regulator_bulk_data supplies[ARRAY_SIZE(hm1246_supply_names)];
>> + struct clk *xclk;
>> + unsigned long xclk_freq;
>> + struct reset_control *reset;
>> + unsigned int mbus_flags;
>> + s64 link_frequency;
>> +
>> + struct v4l2_ctrl_handler ctrls;
>> + struct v4l2_ctrl *exposure_ctrl;
>> + struct v4l2_ctrl *hflip_ctrl;
>> + struct v4l2_ctrl *vflip_ctrl;
>> +
>> + struct regmap *regmap;
>> +
>> + bool identified;
>> +};
>> +
>> +static const struct cci_reg_sequence mode_1296x976_raw[] = {
>> + { HM1246_X_LA_START_REG, 60 },
>> + { HM1246_X_LA_END_REG, 1355 },
>> + { HM1246_Y_LA_START_REG, 0 },
>> + { HM1246_Y_LA_END_REG, 975 },
>> + { HM1246_OUTPUT_PRT_CTRL_REG, 0x20 },
>> + { CCI_REG8(0x300a), 0x01 },
>> + { CCI_REG8(0x300b), 0x00 },
>> + { CCI_REG8(0x50f5), 0x01 },
>> + { CCI_REG8(0x50dd), 0x00 },
>> + { CCI_REG8(0x50a1), 0x02 },
>> + { CCI_REG8(0x50aa), 0x1c },
>> + { CCI_REG8(0x50ac), 0xdd },
>> + { CCI_REG8(0x50ad), 0x08 },
>> + { CCI_REG8(0x50ab), 0x04 },
>> + { CCI_REG8(0x50a0), 0x40 },
>> + { CCI_REG8(0x50a2), 0x12 },
>> + { CCI_REG8(0x50ae), 0x30 },
>> + { CCI_REG8(0x50b3), 0x04 },
>> + { CCI_REG8(0x5204), 0x40 },
>> + { CCI_REG8(0x5208), 0x55 },
>> + { CCI_REG8(0x520b), 0x05 },
>> + { CCI_REG8(0x520d), 0x40 },
>> + { CCI_REG8(0x5214), 0x18 },
>> + { CCI_REG8(0x5215), 0x0f },
>> + { CCI_REG8(0x5217), 0x01 },
>> + { CCI_REG8(0x5218), 0x07 },
>> + { CCI_REG8(0x5219), 0x01 },
>> + { CCI_REG8(0x521a), 0x50 },
>> + { CCI_REG8(0x521b), 0x24 },
>> + { CCI_REG8(0x5232), 0x01 },
>> + { CCI_REG8(0x5220), 0x11 },
>> + { CCI_REG8(0x5227), 0x01 },
>> + { CCI_REG8(0x5106), 0xc1 },
>> + { CCI_REG8(0x5115), 0xc0 },
>> + { CCI_REG8(0x5116), 0xc1 },
>> + { CCI_REG8(0x5138), 0x40 },
>> + { CCI_REG8(0x5139), 0x60 },
>> + { CCI_REG8(0x513a), 0x80 },
>> + { CCI_REG8(0x513b), 0xa0 },
>> + { CCI_REG8(0x513c), 0xa1 },
>> + { CCI_REG8(0x513d), 0xa2 },
>> + { CCI_REG8(0x513e), 0xa3 },
>> + { CCI_REG8(0x5140), 0x40 },
>> + { CCI_REG8(0x5141), 0x60 },
>> + { CCI_REG8(0x5142), 0x80 },
>> + { CCI_REG8(0x5143), 0x81 },
>> + { CCI_REG8(0x5144), 0x82 },
>> + { CCI_REG8(0x5145), 0x83 },
>> + { CCI_REG8(0x5146), 0x93 },
>> + { CCI_REG8(0x51c1), 0xc3 },
>> + { CCI_REG8(0x51c5), 0xc3 },
>> + { CCI_REG8(0x51c9), 0xc3 },
>> + { CCI_REG8(0x51cd), 0xc2 },
>> + { CCI_REG8(0x51d1), 0xc1 },
>> + { CCI_REG8(0x51d5), 0xc1 },
>> + { CCI_REG8(0x51d9), 0x81 },
>> + { CCI_REG8(0x51dd), 0x81 },
>> + { CCI_REG8(0x51c2), 0x49 },
>> + { CCI_REG8(0x51c6), 0x49 },
>> + { CCI_REG8(0x51ca), 0x49 },
>> + { CCI_REG8(0x51ce), 0x49 },
>> + { CCI_REG8(0x51d2), 0x49 },
>> + { CCI_REG8(0x51d6), 0x59 },
>> + { CCI_REG8(0x51da), 0x59 },
>> + { CCI_REG8(0x51de), 0x59 },
>> + { CCI_REG8(0x51c3), 0x20 },
>> + { CCI_REG8(0x51c7), 0x38 },
>> + { CCI_REG8(0x51cb), 0x21 },
>> + { CCI_REG8(0x51cf), 0x11 },
>> + { CCI_REG8(0x51d3), 0x11 },
>> + { CCI_REG8(0x51d7), 0x13 },
>> + { CCI_REG8(0x51db), 0x13 },
>> + { CCI_REG8(0x51df), 0x13 },
>> + { CCI_REG8(0x51e0), 0x03 },
>> + { CCI_REG8(0x51e2), 0x03 },
>> + { CCI_REG8(0x51f0), 0x42 },
>> + { CCI_REG8(0x51f1), 0x40 },
>> + { CCI_REG8(0x51f2), 0x4a },
>> + { CCI_REG8(0x51f3), 0x48 },
>> + { CCI_REG8(0x5015), 0x73 },
>> + { CCI_REG8(0x504a), 0x04 },
>> + { CCI_REG8(0x5044), 0x07 },
>> + { CCI_REG8(0x5040), 0x03 },
>> + { CCI_REG8(0x5135), 0xc4 },
>> + { CCI_REG8(0x5136), 0xc5 },
>> + { CCI_REG8(0x5166), 0xc4 },
>> + { CCI_REG8(0x5196), 0xc4 },
>> + { CCI_REG8(0x51c0), 0x10 },
>> + { CCI_REG8(0x51c4), 0x10 },
>> + { CCI_REG8(0x51c8), 0xa0 },
>> + { CCI_REG8(0x51cc), 0xa0 },
>> + { CCI_REG8(0x51d0), 0xa1 },
>> + { CCI_REG8(0x51d4), 0xa5 },
>> + { CCI_REG8(0x51d8), 0xa5 },
>> + { CCI_REG8(0x51dc), 0xa5 },
>> + { CCI_REG8(0x5200), 0xe4 },
>> + { CCI_REG8(0x5209), 0x04 },
>> + { CCI_REG8(0x301b), 0x01 },
>> + { CCI_REG8(0x3130), 0x01 },
>> + { CCI_REG8(0x5013), 0x07 },
>> + { CCI_REG8(0x5016), 0x01 },
>> + { CCI_REG8(0x501d), 0x50 },
>> + { CCI_REG8(0x0350), 0xfe },
>> + { CCI_REG8(0x2f03), 0x15 },
>> + { CCI_REG8(0xd380), 0x00 },
>> + { CCI_REG8(0x3047), 0x7f },
>> + { CCI_REG8(0x304d), 0x34 },
>> + { CCI_REG8(0x3041), 0x4b },
>> + { CCI_REG8(0x3042), 0x2d },
>> + { CCI_REG8(0x3056), 0x64 },
>> + { CCI_REG8(0x3059), 0x1e },
>> + { CCI_REG8(0x305e), 0x10 },
>> + { CCI_REG8(0x305f), 0x10 },
>> + { CCI_REG8(0x306d), 0x10 },
>> + { CCI_REG8(0x306e), 0x0c },
>> + { CCI_REG8(0x3064), 0x50 },
>> + { CCI_REG8(0x3067), 0x78 },
>> + { CCI_REG8(0x3068), 0x4b },
>> + { CCI_REG8(0x306a), 0x78 },
>> + { CCI_REG8(0x306b), 0x4b },
>> + { CCI_REG8(0xd442), 0x3d },
>> + { CCI_REG8(0xd443), 0x06 },
>> + { CCI_REG8(0xd440), 0x63 },
>> + { CCI_REG8(0xd446), 0xb0 },
>> + { CCI_REG8(0xd447), 0x60 },
>> + { CCI_REG8(0xd448), 0x48 },
>> + { CCI_REG8(0xd449), 0x30 },
>> + { CCI_REG8(0xd44a), 0x18 },
>> + { CCI_REG8(0xd360), 0x03 },
>> + { CCI_REG8(0x30ac), 0x10 },
>> + { CCI_REG8(0x30ad), 0x10 },
>> + { CCI_REG8(0x30ae), 0x10 },
>> + { CCI_REG8(0x3040), 0x0b },
>> + { CCI_REG8(0x2002), 0x00 },
>> + { CCI_REG8(0x2000), 0x08 },
>> +};
>> +
>> +struct hm1246_reg_list {
>> + u32 num_of_regs;
>> + const struct cci_reg_sequence *regs;
>> +};
>> +
>> +struct hm1246_mode {
>> + u32 codes[4];
>> + u32 clocks_per_pixel;
>> + struct v4l2_rect rect;
>> + u32 hts;
>> + u32 vts_min;
>> + const struct hm1246_reg_list reg_list;
>> +};
>> +
>> +#define FLIP_FORMAT_INDEX(v, h) ((v ? 2 : 0) | (h ? 1 : 0))
>> +
>> +/* Get the format code of the mode considering current flip setting. */
>> +static u32 hm1246_get_format_code(struct hm1246 *hm1246,
>> + const struct hm1246_mode *hm1246_mode)
>> +{
>> + return hm1246_mode->codes[FLIP_FORMAT_INDEX(hm1246->vflip_ctrl->val,
>> + hm1246->hflip_ctrl->val)];
>> +}
>> +
>> +static const struct hm1246_mode hm1246_modes[] = {
>> + {
>> + .codes = {
>> + [FLIP_FORMAT_INDEX(0, 0)] = MEDIA_BUS_FMT_SBGGR10_1X10,
>> + [FLIP_FORMAT_INDEX(0, 1)] = MEDIA_BUS_FMT_SGBRG10_1X10,
>> + [FLIP_FORMAT_INDEX(1, 0)] = MEDIA_BUS_FMT_SGRBG10_1X10,
>> + [FLIP_FORMAT_INDEX(1, 1)] = MEDIA_BUS_FMT_SRGGB10_1X10,
>> + },
>> + .clocks_per_pixel = 1,
>> + .rect.top = 0,
>> + .rect.left = 0,
>> + .rect.width = 1296,
>> + .rect.height = 976,
>> + .hts = 1420,
>> + .vts_min = 990,
>> + .reg_list = {
>> + .num_of_regs = ARRAY_SIZE(mode_1296x976_raw),
>> + .regs = mode_1296x976_raw,
>> + },
>> + },
>> +};
>> +
>> +static inline struct hm1246 *to_hm1246(struct v4l2_subdev *sd)
>> +{
>> + return container_of_const(sd, struct hm1246, sd);
>> +}
>> +
>> +static const struct hm1246_mode *
>> +hm1246_find_mode_by_mbus_code(struct hm1246 *hm1246, u32 code)
>> +{
>> + for (unsigned int i = 0; i < ARRAY_SIZE(hm1246_modes); i++) {
>> + if (code == hm1246_get_format_code(hm1246, &hm1246_modes[i]))
>> + return &hm1246_modes[i];
>> + }
>> +
>> + return NULL;
>> +}
>> +
>> +static int hm1246_power_on(struct device *dev)
>> +{
>> + struct v4l2_subdev *sd = dev_get_drvdata(dev);
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> + int ret;
>> +
>> + ret = regulator_bulk_enable(ARRAY_SIZE(hm1246_supply_names),
>> + hm1246->supplies);
>> + if (ret) {
>> + dev_err(hm1246->dev, "failed to enable regulators\n");
>> + return ret;
>> + }
>> +
>> + ret = clk_prepare_enable(hm1246->xclk);
>> + if (ret) {
>> + regulator_bulk_disable(ARRAY_SIZE(hm1246_supply_names),
>> + hm1246->supplies);
>> + dev_err(hm1246->dev, "failed to enable clock\n");
>> + return ret;
>> + }
>> +
>> + reset_control_deassert(hm1246->reset);
>> +
>> + /*
>> + * XSHUTDOWN to crystal clock oscillation (tcrystal): 650us (typical)
>> + * Sample bootstrap pin (tsample): 2000us (maximum)
>> + * Built in self test (tbist): 3000us (maximum)
>> + */
>> + fsleep(6 * USEC_PER_MSEC);
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_power_off(struct device *dev)
>> +{
>> + struct v4l2_subdev *sd = dev_get_drvdata(dev);
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> +
>> + reset_control_assert(hm1246->reset);
>> +
>> + clk_disable_unprepare(hm1246->xclk);
>> +
>> + regulator_bulk_disable(ARRAY_SIZE(hm1246_supply_names),
>> + hm1246->supplies);
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_enum_mbus_code(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_state *sd_state,
>> + struct v4l2_subdev_mbus_code_enum *code)
>> +{
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> +
>> + if (code->index >= ARRAY_SIZE(hm1246_modes))
>> + return -EINVAL;
>> +
>> + code->code = hm1246_get_format_code(hm1246, &hm1246_modes[code->index]);
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_enum_frame_size(struct v4l2_subdev *subdev,
>> + struct v4l2_subdev_state *sd_state,
>> + struct v4l2_subdev_frame_size_enum *fse)
>> +{
>> + struct hm1246 *hm1246 = to_hm1246(subdev);
>> + const struct hm1246_mode *mode;
>> +
>> + if (fse->index > 0)
>> + return -EINVAL;
>> +
>> + mode = hm1246_find_mode_by_mbus_code(hm1246, fse->code);
>> + if (!mode)
>> + return -EINVAL;
>> +
>> + fse->min_width = mode->rect.width;
>> + fse->max_width = mode->rect.width;
>> + fse->min_height = mode->rect.height;
>> + fse->max_height = mode->rect.height;
>> +
>> + return 0;
>> +}
>> +
>> +static void hm1246_update_pad_format(struct hm1246 *hm1246,
>> + const struct hm1246_mode *hm1246_mode,
>> + struct v4l2_mbus_framefmt *fmt)
>> +{
>> + fmt->width = hm1246_mode->rect.width;
>> + fmt->height = hm1246_mode->rect.height;
>> + fmt->code = hm1246_get_format_code(hm1246, hm1246_mode);
>> + fmt->field = V4L2_FIELD_NONE;
>> + fmt->colorspace = V4L2_COLORSPACE_RAW;
>> + fmt->ycbcr_enc = V4L2_MAP_YCBCR_ENC_DEFAULT(fmt->colorspace);
>> + fmt->quantization = V4L2_QUANTIZATION_FULL_RANGE;
>> + fmt->xfer_func = V4L2_XFER_FUNC_NONE;
>> +}
>> +
>> +static int hm1246_set_format(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_state *state,
>> + struct v4l2_subdev_format *fmt)
>> +{
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> + struct v4l2_mbus_framefmt *mbus_fmt;
>> + struct v4l2_rect *crop;
>> + const struct hm1246_mode *mode;
>> +
>> + mode = hm1246_find_mode_by_mbus_code(hm1246, fmt->format.code);
>> + if (!mode)
>> + mode = &hm1246_modes[0];
>> +
>> + crop = v4l2_subdev_state_get_crop(state, 0);
>> + *crop = mode->rect;
>> +
>> + hm1246_update_pad_format(hm1246, mode, &fmt->format);
>> + mbus_fmt = v4l2_subdev_state_get_format(state, 0);
>> + *mbus_fmt = fmt->format;
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_get_selection(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_state *state,
>> + struct v4l2_subdev_selection *sel)
>> +{
>> + const struct v4l2_mbus_framefmt *format;
>> + const struct hm1246_mode *mode;
>> +
>> + format = v4l2_subdev_state_get_format(state, 0);
>> + mode = v4l2_find_nearest_size(hm1246_modes, ARRAY_SIZE(hm1246_modes),
>> + rect.width, rect.height, format->width,
>> + format->height);
>> +
>> + switch (sel->target) {
>> + case V4L2_SEL_TGT_CROP:
>> + sel->r = *v4l2_subdev_state_get_crop(state, 0);
>> + return 0;
>> +
>> + case V4L2_SEL_TGT_NATIVE_SIZE:
>> + sel->r.top = 0;
>> + sel->r.left = 0;
>> + sel->r.width = HM1246_NATIVE_WIDTH;
>> + sel->r.height = HM1246_NATIVE_HEIGHT;
>> + return 0;
>> +
>> + case V4L2_SEL_TGT_CROP_DEFAULT:
>> + case V4L2_SEL_TGT_CROP_BOUNDS:
>> + sel->r = mode->rect;
>> + return 0;
>> +
>> + default:
>> + return -EINVAL;
>> + }
>> +}
>> +
>> +static int hm1246_init_state(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_state *state)
>> +{
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> + struct v4l2_subdev_format fmt = {
>> + .which = V4L2_SUBDEV_FORMAT_TRY,
>> + .pad = 0,
>> + .format = {
>> + .code = hm1246_get_format_code(hm1246,
>> + &hm1246_modes[0]),
>> + .width = hm1246_modes[0].rect.width,
>> + .height = hm1246_modes[0].rect.height,
>> + },
>> + };
>> +
>> + hm1246_set_format(sd, state, &fmt);
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_calc_pll(u32 xclk, u32 link_freq, u32 clocks_per_pixel,
>> + u8 *pll1, u8 *pll2, u8 *pll3)
>> +{
>> + static const u8 pclk_div_table[] = { 4, 5, 6, 7, 8, 12, 14, 16 };
>> + static const u8 sysclk_div_table[] = { 1, 2, 3, 4 };
>> + static const u8 post_div_table[] = { 1, 2, 4, 8 };
>> + static const int sysclk_pclk_ratio = 3; /* Recommended value */
>> + u32 pclk, vco_out;
>> + int pclk_div_index, sysclk_div_index, post_div_index;
>> + bool sysclk_pclk_ratio_found = false;
>> +
>> + if (link_freq < HM1246_PCLK_MIN || link_freq > HM1246_PCLK_MAX)
>> + return -EINVAL;
>> +
>> + /*
>> + * In raw mode (1 pixel per clock) the pixel clock is internally
>> + * divided by two.
>> + */
>> + pclk = 2 * link_freq / clocks_per_pixel;
>> +
>> + /* Find suitable PCLK and SYSCLK dividers. */
>> + for (pclk_div_index = 0; pclk_div_index < ARRAY_SIZE(pclk_div_table);
>> + pclk_div_index++) {
>> + for (sysclk_div_index = 0;
>> + sysclk_div_index < ARRAY_SIZE(sysclk_div_table);
>> + sysclk_div_index++) {
>> + if (sysclk_div_table[sysclk_div_index] *
>> + sysclk_pclk_ratio ==
>> + pclk_div_table[pclk_div_index]) {
>> + sysclk_pclk_ratio_found = true;
>> + break;
>> + }
>> + }
>> + if (sysclk_pclk_ratio_found)
>> + break;
>> + }
>> +
>> + if (!sysclk_pclk_ratio_found)
>> + return -EINVAL;
>> +
>> + /* Determine an appropriate post divider. */
>> + for (post_div_index = 0; post_div_index < ARRAY_SIZE(post_div_table);
>> + post_div_index++) {
>> + vco_out = pclk * pclk_div_table[pclk_div_index] *
>> + post_div_table[post_div_index];
>> +
>> + if (vco_out >= HM1246_PLL_VCO_MIN &&
>> + vco_out <= HM1246_PLL_VCO_MAX)
>> + break;
>> + }
>> + if (post_div_index >= ARRAY_SIZE(post_div_table))
>> + return -EINVAL;
>> +
>> + /* Find pre-divider and multiplier values. */
>> + for (u32 div = DIV_ROUND_UP(xclk, HM1246_PLL_INCLK_MAX);
>> + div <= xclk / HM1246_PLL_INCLK_MIN; div++) {
>> + u32 multi, multi_h, multi_l, vco;
>> +
>> + multi = DIV_ROUND_CLOSEST_ULL((u64)vco_out * div, xclk);
>> + if (multi < HM1246_PLL_MULTI_MIN ||
>> + multi > HM1246_PLL_MULTI_MAX)
>> + continue;
>> +
>> + multi_h = multi / (HM1246_PLL_MULTI_H_MIN *
>> + HM1246_PLL_MULTI_L_MAX) +
>> + 2;
>> + multi_l = multi / multi_h;
>> + vco = div_u64((u64)xclk * multi_h * multi_l, div);
>> +
>> + if (vco != vco_out)
>> + continue;
>> +
>> + if (pll1 && pll2 && pll3) {
>> + *pll1 = HM1246_PLL1CFG_MULTIPLIER(multi_l - 1);
>> + *pll2 = HM1246_PLL2CFG_PRE_DIV(div - 1) |
>> + HM1246_PLL2CFG_MULTIPLIER(multi_h - 2);
>> + *pll3 = HM1246_PLL3CFG_POST_DIV(post_div_index) |
>> + HM1246_PLL3CFG_SYSCLK_DIV(sysclk_div_index) |
>> + HM1246_PLL3CFG_PCLK_DIV(pclk_div_index);
>> + }
>> +
>> + return 0;
>> + }
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int hm1246_cci_write_pll(struct hm1246 *hm1246, u8 pll1, u8 pll2,
>> + u8 pll3)
>> +{
>> + const struct cci_reg_sequence pll_regs[] = {
>> + { HM1246_PLL1CFG_REG, pll1 },
>> + { HM1246_PLL2CFG_REG, pll2 },
>> + { HM1246_PLL3CFG_REG, pll3 },
>> + { HM1246_SBC_CTRL_REG, HM1246_SBC_CTRL_PLL_EN },
>> + };
>> +
>> + return cci_multi_reg_write(hm1246->regmap, pll_regs,
>> + ARRAY_SIZE(pll_regs), NULL);
>> +}
>> +
>> +static int hm1246_pll_check_locked(struct hm1246 *hm1246)
>> +{
>> + u64 boot_ref2;
>> + int ret;
>> +
>> + ret = cci_read(hm1246->regmap, HM1246_SBC_BOOT_REF2_REG, &boot_ref2,
>> + NULL);
>> + if (ret)
>> + return ret;
>> +
>> + return (boot_ref2 & HM1246_SBC_BOOT_REF2_PLL_LOCK) ? 0 : -EIO;
>> +}
>> +
>> +static int hm1246_setup_pll(struct hm1246 *hm1246,
>> + const struct hm1246_mode *mode)
>> +{
>> + u8 pll1, pll2, pll3;
>> + int ret;
>> +
>> + ret = hm1246_calc_pll(hm1246->xclk_freq, hm1246->link_frequency,
>> + mode->clocks_per_pixel, &pll1, &pll2, &pll3);
>> + if (ret)
>> + return ret;
>> +
>> + ret = hm1246_cci_write_pll(hm1246, pll1, pll2, pll3);
>> + if (ret)
>> + return ret;
>> +
>> + /* PLL lock time (tpll): 100us (typical) */
>> + fsleep(200);
>> +
>> + return hm1246_pll_check_locked(hm1246);
>> +}
>> +
>> +static int hm1246_cci_write_test_pattern(struct hm1246 *hm1246, u8 mode,
>> + u16 r, u16 g, u16 b)
>> +{
>> + const struct cci_reg_sequence tpg_enable_regs[] = {
>> + { HM1246_TEST_DATA_RED_REG, r },
>> + { HM1246_TEST_DATA_GR_REG, g },
>> + { HM1246_TEST_DATA_GB_REG, g },
>> + { HM1246_TEST_DATA_BLUE_REG, b },
>> + { HM1246_TEST_PATTERN_MODE_REG, mode },
>> + };
>> +
>> + return cci_multi_reg_write(hm1246->regmap, tpg_enable_regs,
>> + ARRAY_SIZE(tpg_enable_regs), NULL);
>> +}
>> +
>> +static int hm1246_test_pattern(struct hm1246 *hm1246, u32 index)
>> +{
>> + static const u16 RGBMIN = 0, RGBMAX = 0x3ff;
>> + static const struct tp {
>> + int pattern;
>> + u16 r, g, b;
>> + } tps[] = {
>> + /* Disabled */
>> + [0] = { .pattern = 0, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
>> + /* Checkboard pattern */
>> + [1] = { .pattern = 0, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
>> + /* Ramp */
>> + [2] = { .pattern = 1, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
>> + /* Moving ones */
>> + [3] = { .pattern = 2, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
>> + /* Blending color bars */
>> + [4] = { .pattern = 3, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
>> + /* Color bars */
>> + [5] = { .pattern = 4, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
>> + /* Solid white */
>> + [6] = { .pattern = 15, .r = RGBMAX, .g = RGBMAX, .b = RGBMAX },
>> + /* Solid black */
>> + [7] = { .pattern = 15, .r = RGBMIN, .g = RGBMIN, .b = RGBMIN },
>> + /* Solid red */
>> + [8] = { .pattern = 15, .r = RGBMAX, .g = RGBMIN, .b = RGBMIN },
>> + /* Solid green */
>> + [9] = { .pattern = 15, .r = RGBMIN, .g = RGBMAX, .b = RGBMIN },
>> + /* Solid blue */
>> + [10] = { .pattern = 15, .r = RGBMIN, .g = RGBMIN, .b = RGBMAX },
>> + };
>> + u8 mode;
>> +
>> + if (index >= ARRAY_SIZE(tps))
>> + return -EINVAL;
>> +
>> + mode = HM1246_TEST_PATTERN_MODE_MODE(tps[index].pattern);
>> + if (index)
>> + mode |= HM1246_TEST_PATTERN_MODE_ENABLE;
>> +
>> + return hm1246_cci_write_test_pattern(hm1246, mode, tps[index].r,
>> + tps[index].g, tps[index].b);
>> +}
>> +
>> +static int hm1246_set_ctrl(struct v4l2_ctrl *ctrl)
>> +{
>> + struct hm1246 *hm1246 =
>> + container_of_const(ctrl->handler, struct hm1246, ctrls);
>> + struct v4l2_subdev_state *state;
>> + const struct v4l2_mbus_framefmt *format;
>> + u32 val;
>> + bool needs_cmu_update = true;
>> + int ret;
>> +
>> + state = v4l2_subdev_get_locked_active_state(&hm1246->sd);
>> + format = v4l2_subdev_state_get_format(state, 0);
>> +
>> + if (ctrl->id == V4L2_CID_VBLANK) {
>> + s64 exposure_max;
>> +
>> + exposure_max =
>> + format->height + ctrl->val - HM1246_COARSE_INTG_MARGIN;
>> + ret = __v4l2_ctrl_modify_range(hm1246->exposure_ctrl,
>> + hm1246->exposure_ctrl->minimum,
>> + exposure_max,
>> + hm1246->exposure_ctrl->step,
>> + exposure_max);
>> +
>> + if (ret) {
>> + dev_err(hm1246->dev, "exposure ctrl range update failed\n");
>> + return ret;
>> + }
>> + }
>> +
>> + if (!pm_runtime_get_if_active(hm1246->dev))
>> + return 0;
>> +
>> + ret = 0;
>> + switch (ctrl->id) {
>> + case V4L2_CID_EXPOSURE:
>> + cci_write(hm1246->regmap, HM1246_COARSE_INTG_REG, ctrl->val,
>> + &ret);
>> + break;
>> +
>> + case V4L2_CID_ANALOGUE_GAIN:
>> + cci_write(hm1246->regmap, HM1246_ANALOG_GLOBAL_GAIN_REG,
>> + ctrl->val, &ret);
>> + break;
>> +
>> + case V4L2_CID_VBLANK:
>> + val = format->height + ctrl->val;
>> + cci_write(hm1246->regmap, HM1246_FRAME_LENGTH_LINES_REG, val,
>> + &ret);
>> + break;
>> +
>> + case V4L2_CID_HFLIP:
>> + case V4L2_CID_VFLIP:
>> + val = 0;
>> + if (hm1246->hflip_ctrl->val)
>> + val |= HM1246_IMAGE_ORIENTATION_HFLIP;
>> + if (hm1246->vflip_ctrl->val)
>> + val |= HM1246_IMAGE_ORIENTATION_VFLIP;
>> +
>> + cci_write(hm1246->regmap, HM1246_IMAGE_ORIENTATION_REG, val,
>> + &ret);
>> + break;
>> +
>> + case V4L2_CID_TEST_PATTERN:
>> + ret = hm1246_test_pattern(hm1246, ctrl->val);
>> + needs_cmu_update = false;
>> + break;
>> +
>> + default:
>> + ret = -EINVAL;
>> + needs_cmu_update = false;
>> + break;
>> + }
>> +
>> + if (needs_cmu_update)
>> + cci_write(hm1246->regmap, HM1246_CMU_UPDATE_REG, 0, &ret);
>> +
>> + pm_runtime_put(hm1246->dev);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct v4l2_ctrl_ops hm1246_ctrl_ops = {
>> + .s_ctrl = hm1246_set_ctrl,
>> +};
>> +
>> +static int hm1246_identify_module(struct hm1246 *hm1246)
>> +{
>> + u64 model_id;
>> + int ret;
>> +
>> + if (hm1246->identified)
>> + return 0;
>> +
>> + ret = cci_read(hm1246->regmap, HM1246_MODEL_ID_REG, &model_id, NULL);
>> + if (ret)
>> + return ret;
>> +
>> + if (model_id != HM1246_MODEL_ID) {
>> + dev_err(hm1246->dev, "model id mismatch: 0x%llx!=0x%x\n",
>> + model_id, HM1246_MODEL_ID);
>> + return -ENXIO;
>> + }
>> +
>> + hm1246->identified = true;
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_setup_moderegs(struct hm1246 *hm1246,
>> + const struct hm1246_mode *mode)
>> +{
>> + const struct hm1246_reg_list *reg_list = &mode->reg_list;
>> + const struct cci_reg_sequence modeaw[] = {
>> + { HM1246_X_ADDR_START_REG, mode->rect.left },
>> + { HM1246_Y_ADDR_START_REG, mode->rect.top },
>> + { HM1246_X_ADDR_END_REG, mode->rect.width - 1 },
>> + { HM1246_Y_ADDR_END_REG, mode->rect.height - 1 },
>> + { HM1246_DWIN_XOFFSET_REG, mode->rect.left },
>> + { HM1246_DWIN_YOFFSET_REG, mode->rect.top },
>> + { HM1246_DWIN_XSIZE_REG, mode->rect.width },
>> + { HM1246_DWIN_YSIZE_REG, mode->rect.height },
>> + { HM1246_LINE_LENGTH_PCK_REG, mode->hts },
>> + };
>> + int ret = 0;
>> +
>> + cci_multi_reg_write(hm1246->regmap, modeaw, ARRAY_SIZE(modeaw), &ret);
>> + cci_multi_reg_write(hm1246->regmap, reg_list->regs,
>> + reg_list->num_of_regs, &ret);
>> +
>> + return ret;
>> +}
>> +
>> +static int hm1246_setup_bus(struct hm1246 *hm1246)
>> +{
>> + u64 polarity_ctrl = 0, pclk_ctrl = 0;
>> + int ret = 0;
>> +
>> + if (hm1246->mbus_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW)
>> + polarity_ctrl |= HM1246_POLARITY_CTRL_HSYNC;
>> +
>> + if (hm1246->mbus_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW)
>> + polarity_ctrl |= HM1246_POLARITY_CTRL_VSYNC;
>> +
>> + cci_write(hm1246->regmap, HM1246_POLARITY_CTRL_REG, polarity_ctrl,
>> + &ret);
>> +
>> + /*
>> + * If the clock output polarity flag PCLK_CTRL[3] is set (high), the
>> + * data lines change state on the falling edge of PCLK and should
>> + * therefore be sampled on the rising edge.
>> + * This is different than described in the data sheet.
>> + */
>> + if (hm1246->mbus_flags & V4L2_MBUS_PCLK_SAMPLE_RISING)
>> + pclk_ctrl |= HM1246_PCLK_CTRL_POL;
>> +
>> + cci_write(hm1246->regmap, HM1246_PCLK_CTRL_REG, pclk_ctrl, &ret);
>> +
>> + return ret;
>> +}
>> +
>> +static int hm1246_enable_streams(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_state *state, u32 pad,
>> + u64 streams_mask)
>> +{
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> + const struct v4l2_mbus_framefmt *format;
>> + const struct hm1246_mode *mode;
>> + int ret;
>> +
>> + format = v4l2_subdev_state_get_format(state, 0);
>> + mode = v4l2_find_nearest_size(hm1246_modes, ARRAY_SIZE(hm1246_modes),
>> + rect.width, rect.height, format->width,
>> + format->height);
>> +
>> + ret = pm_runtime_resume_and_get(hm1246->dev);
>> + if (ret)
>> + return ret;
>> +
>> + ret = hm1246_identify_module(hm1246);
>> + if (ret)
>> + goto err_rpm_put;
>> +
>> + ret = hm1246_setup_pll(hm1246, mode);
>> + if (ret) {
>> + dev_err(hm1246->dev, "failed to setup PLL\n");
>> + goto err_rpm_put;
>> + }
>> +
>> + ret = hm1246_setup_moderegs(hm1246, mode);
>> + if (ret)
>> + goto err_rpm_put;
>> +
>> + ret = hm1246_setup_bus(hm1246);
>> + if (ret)
>> + goto err_rpm_put;
>> +
>> + ret = __v4l2_ctrl_handler_setup(&hm1246->ctrls);
>> + if (ret) {
>> + dev_err(hm1246->dev, "failed to setup v4l2 controls\n");
>> + goto err_rpm_put;
>> + }
>> +
>> + ret = cci_write(hm1246->regmap, HM1246_MODE_SELECT_REG,
>> + HM1246_MODE_SELECT_STREAM, NULL);
>> + if (ret)
>> + goto err_rpm_put;
>> +
>> + /*
>> + * Since mirroring may change the actual pixel format, it must not be
>> + * changed during streaming.
>> + */
>> + __v4l2_ctrl_grab(hm1246->vflip_ctrl, true);
>> + __v4l2_ctrl_grab(hm1246->hflip_ctrl, true);
>> +
>> + return 0;
>> +
>> +err_rpm_put:
>> + pm_runtime_put_autosuspend(hm1246->dev);
>> +
>> + return ret;
>> +}
>> +
>> +static int hm1246_disable_streams(struct v4l2_subdev *sd,
>> + struct v4l2_subdev_state *state, u32 pad,
>> + u64 streams_mask)
>> +{
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> + int ret;
>> +
>> + ret = cci_write(hm1246->regmap, HM1246_MODE_SELECT_REG,
>> + HM1246_MODE_SELECT_STANDBY, NULL);
>> +
>> + __v4l2_ctrl_grab(hm1246->vflip_ctrl, false);
>> + __v4l2_ctrl_grab(hm1246->hflip_ctrl, false);
>> +
>> + pm_runtime_put_autosuspend(hm1246->dev);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct v4l2_subdev_video_ops hm1246_video_ops = {
>> + .s_stream = v4l2_subdev_s_stream_helper,
>> +};
>> +
>> +static const struct v4l2_subdev_pad_ops hm1246_subdev_pad_ops = {
>> + .enum_mbus_code = hm1246_enum_mbus_code,
>> + .enum_frame_size = hm1246_enum_frame_size,
>> + .get_fmt = v4l2_subdev_get_fmt,
>> + .set_fmt = hm1246_set_format,
>> + .get_selection = hm1246_get_selection,
>> + .enable_streams = hm1246_enable_streams,
>> + .disable_streams = hm1246_disable_streams,
>> +};
>> +
>> +static const struct v4l2_subdev_ops hm1246_subdev_ops = {
>> + .video = &hm1246_video_ops,
>> + .pad = &hm1246_subdev_pad_ops,
>> +};
>> +
>> +static const struct v4l2_subdev_internal_ops hm1246_internal_ops = {
>> + .init_state = hm1246_init_state,
>> +};
>> +
>> +static int hm1246_get_regulators(struct device *dev, struct hm1246 *hm1246)
>> +{
>> + unsigned int i;
>> +
>> + for (i = 0; i < ARRAY_SIZE(hm1246_supply_names); i++)
>
> You could declare i here.
>
>> + hm1246->supplies[i].supply = hm1246_supply_names[i];
>> +
>> + return devm_regulator_bulk_get(dev, ARRAY_SIZE(hm1246_supply_names),
>> + hm1246->supplies);
>> +}
>> +
>> +static int hm1246_parse_fwnode(struct hm1246 *hm1246)
>> +{
>> + struct fwnode_handle *endpoint;
>> + struct v4l2_fwnode_endpoint bus_cfg = {
>> + .bus_type = V4L2_MBUS_PARALLEL,
>> + };
>> + int ret;
>> +
>> + endpoint = fwnode_graph_get_endpoint_by_id(dev_fwnode(hm1246->dev),
>> + 0, 0,
>> + FWNODE_GRAPH_ENDPOINT_NEXT);
>> + if (!endpoint)
>> + return dev_err_probe(hm1246->dev, -EINVAL,
>> + "missing endpoint node\n");
>
> You can drop this check now; v4l2_fwnode_endpoint_alloc_parse() already
> does it.
>
>> +
>> + ret = v4l2_fwnode_endpoint_alloc_parse(endpoint, &bus_cfg);
>> + fwnode_handle_put(endpoint);
>> + if (ret)
>> + return dev_err_probe(hm1246->dev, ret,
>> + "parsing endpoint node failed\n");
>> +
>> + hm1246->mbus_flags = bus_cfg.bus.parallel.flags;
>> +
>> + if (bus_cfg.nr_of_link_frequencies == 1)
>
> I'd test for non-zero number: having more than one isn't an issue.
>
>> + hm1246->link_frequency = bus_cfg.link_frequencies[0];
>> +
>> + v4l2_fwnode_endpoint_free(&bus_cfg);
>> +
>> + if (!hm1246->link_frequency)
>> + return dev_err_probe(hm1246->dev, -EINVAL,
>> + "one link frequency expected\n");
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_init_controls(struct hm1246 *hm1246)
>> +{
>> + const struct hm1246_mode *mode = &hm1246_modes[0];
>> + struct v4l2_fwnode_device_properties props;
>> + struct v4l2_ctrl_handler *ctrl_hdlr = &hm1246->ctrls;
>> + struct v4l2_ctrl *ctrl;
>> + s64 pixel_rate, exposure_max, vblank_min, hblank;
>> + int ret;
>> +
>> + ret = v4l2_fwnode_device_parse(hm1246->dev, &props);
>> + if (ret)
>> + return ret;
>> +
>> + v4l2_ctrl_handler_init(ctrl_hdlr, 11);
>> +
>> + hm1246->hflip_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_HFLIP, 0, 1, 1, 0);
>> + if (hm1246->hflip_ctrl)
>> + hm1246->hflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
>> +
>> + hm1246->vflip_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_VFLIP, 0, 1, 1, 0);
>> + if (hm1246->vflip_ctrl)
>> + hm1246->vflip_ctrl->flags |= V4L2_CTRL_FLAG_MODIFY_LAYOUT;
>> +
>> + v4l2_ctrl_cluster(2, &hm1246->hflip_ctrl);
>> +
>> + ctrl = v4l2_ctrl_new_int_menu(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_LINK_FREQ,
>> + 0, 0,
>> + &hm1246->link_frequency);
>> + if (ctrl)
>> + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> + pixel_rate = div_u64(hm1246->link_frequency, mode->clocks_per_pixel);
>> + ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_PIXEL_RATE,
>> + pixel_rate, pixel_rate, 1,
>> + pixel_rate);
>> +
>> + vblank_min = mode->vts_min - mode->rect.height;
>> + ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_VBLANK, vblank_min,
>> + HM1246_VTS_MAX - mode->rect.height,
>> + 1, vblank_min);
>> +
>> + hblank = mode->hts - mode->rect.width;
>> + ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_HBLANK, hblank, hblank,
>> + 1, hblank);
>> + if (ctrl)
>> + ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
>> +
>> + v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops, V4L2_CID_ANALOGUE_GAIN,
>> + HM1246_ANALOG_GLOBAL_GAIN_MIN,
>> + HM1246_ANALOG_GLOBAL_GAIN_MAX,
>> + HM1246_ANALOG_GLOBAL_GAIN_STEP,
>> + HM1246_ANALOG_GLOBAL_GAIN_MIN);
>> +
>> + exposure_max = mode->vts_min - HM1246_COARSE_INTG_MARGIN;
>> + hm1246->exposure_ctrl = v4l2_ctrl_new_std(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_EXPOSURE,
>> + HM1246_COARSE_INTG_MIN,
>> + exposure_max,
>> + HM1246_COARSE_INTG_STEP,
>> + exposure_max);
>> +
>> + v4l2_ctrl_new_std_menu_items(ctrl_hdlr, &hm1246_ctrl_ops,
>> + V4L2_CID_TEST_PATTERN,
>> + ARRAY_SIZE(hm1246_test_pattern_menu) - 1,
>> + 0, 0, hm1246_test_pattern_menu);
>> +
>> + v4l2_ctrl_new_fwnode_properties(ctrl_hdlr, &hm1246_ctrl_ops, &props);
>> +
>> + if (ctrl_hdlr->error) {
>> + v4l2_ctrl_handler_free(ctrl_hdlr);
>> + return ctrl_hdlr->error;
>> + }
>> +
>> + hm1246->sd.ctrl_handler = ctrl_hdlr;
>> +
>> + return 0;
>> +}
>> +
>> +static int hm1246_probe(struct i2c_client *client)
>> +{
>> + struct hm1246 *hm1246;
>> + int ret;
>> +
>> + hm1246 = devm_kzalloc(&client->dev, sizeof(*hm1246), GFP_KERNEL);
>> + if (!hm1246)
>> + return -ENOMEM;
>> +
>> + hm1246->dev = &client->dev;
>> +
>> + ret = hm1246_parse_fwnode(hm1246);
>> + if (ret)
>> + return ret;
>> +
>> + hm1246->regmap = devm_cci_regmap_init_i2c(client, 16);
>> + if (IS_ERR(hm1246->regmap))
>> + return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->regmap),
>> + "failed to init CCI\n");
>> +
>> + hm1246->xclk = devm_v4l2_sensor_clk_get(hm1246->dev, NULL);
>> + if (IS_ERR(hm1246->xclk))
>> + return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->xclk),
>> + "failed to get xclk\n");
>> +
>> + hm1246->xclk_freq = clk_get_rate(hm1246->xclk);
>> + if (hm1246->xclk_freq < HM1246_XCLK_MIN ||
>> + hm1246->xclk_freq > HM1246_XCLK_MAX)
>> + return dev_err_probe(hm1246->dev, -EINVAL,
>> + "xclk frequency out of range: %luHz\n",
>> + hm1246->xclk_freq);
>> +
>> + for (unsigned int i = 0; i < ARRAY_SIZE(hm1246_modes); i++) {
>> + ret = hm1246_calc_pll(hm1246->xclk_freq, hm1246->link_frequency,
>> + hm1246_modes[i].clocks_per_pixel,
>> + NULL, NULL, NULL);
>> + if (ret)
>> + return dev_err_probe(hm1246->dev, ret,
>> + "no PLL setup for %lld Hz\n",
>> + hm1246->link_frequency);
>> + }
>> +
>> + ret = hm1246_get_regulators(hm1246->dev, hm1246);
>> + if (ret)
>> + return dev_err_probe(hm1246->dev, ret,
>> + "failed to get regulators\n");
>> +
>> + hm1246->reset = devm_reset_control_get_optional(hm1246->dev, NULL);
>> + if (IS_ERR(hm1246->reset))
>> + return dev_err_probe(hm1246->dev, PTR_ERR(hm1246->reset),
>> + "failed to get reset\n");
>> + reset_control_assert(hm1246->reset);
>> +
>> + v4l2_i2c_subdev_init(&hm1246->sd, client, &hm1246_subdev_ops);
>> + hm1246->sd.internal_ops = &hm1246_internal_ops;
>> +
>> + ret = hm1246_init_controls(hm1246);
>> + if (ret)
>> + return dev_err_probe(hm1246->dev, ret,
>> + "failed to init controls\n");
>> +
>> + hm1246->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
>> + hm1246->pad.flags = MEDIA_PAD_FL_SOURCE;
>> + hm1246->sd.entity.function = MEDIA_ENT_F_CAM_SENSOR;
>> +
>> + ret = media_entity_pads_init(&hm1246->sd.entity, 1, &hm1246->pad);
>> + if (ret) {
>> + dev_err_probe(hm1246->dev, ret, "failed to init media pads\n");
>> + goto err_v4l2_ctrl_handler_free;
>> + }
>> +
>> + hm1246->sd.state_lock = hm1246->ctrls.lock;
>> + ret = v4l2_subdev_init_finalize(&hm1246->sd);
>> + if (ret) {
>> + dev_err_probe(hm1246->dev, ret, "failed to init v4l2 subdev\n");
>> + goto err_media_entity_cleanup;
>> + }
>> +
>> + pm_runtime_enable(hm1246->dev);
>> + pm_runtime_set_autosuspend_delay(hm1246->dev, 1000);
>> + pm_runtime_use_autosuspend(hm1246->dev);
>> + pm_runtime_idle(hm1246->dev);
>
> No need to call pm_runtime_idle() as the device hasn't been resumed.
>
> This Runtime PM flow only works on DT in any case. I think I'd add a
> dependency to DT.
>
>> +
>> + ret = v4l2_async_register_subdev_sensor(&hm1246->sd);
>> + if (ret) {
>> + dev_err_probe(hm1246->dev, ret,
>> + "failed to register v4l2 subdev\n");
>> + goto err_subdev_cleanup;
>> + }
>> +
>> + return 0;
>> +
>> +err_subdev_cleanup:
>> + v4l2_subdev_cleanup(&hm1246->sd);
>> + pm_runtime_disable(hm1246->dev);
>> + pm_runtime_set_suspended(hm1246->dev);
>> +
>> +err_media_entity_cleanup:
>> + media_entity_cleanup(&hm1246->sd.entity);
>> +
>> +err_v4l2_ctrl_handler_free:
>> + v4l2_ctrl_handler_free(&hm1246->ctrls);
>> +
>> + return ret;
>> +}
>> +
>> +static void hm1246_remove(struct i2c_client *client)
>> +{
>> + struct v4l2_subdev *sd = i2c_get_clientdata(client);
>> + struct hm1246 *hm1246 = to_hm1246(sd);
>> +
>> + v4l2_async_unregister_subdev(&hm1246->sd);
>> + v4l2_subdev_cleanup(sd);
>> + media_entity_cleanup(&hm1246->sd.entity);
>> + v4l2_ctrl_handler_free(&hm1246->ctrls);
>> +
>> + pm_runtime_disable(&client->dev);
>> + if (!pm_runtime_status_suspended(&client->dev)) {
>> + hm1246_power_off(hm1246->dev);
>> + pm_runtime_set_suspended(&client->dev);
>> + }
>> +}
>> +
>> +static const struct of_device_id hm1246_of_match[] = {
>> + { .compatible = "himax,hm1246" },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, hm1246_of_match);
>> +
>> +static DEFINE_RUNTIME_DEV_PM_OPS(hm1246_pm_ops,
>> + hm1246_power_off, hm1246_power_on, NULL);
>> +
>> +static struct i2c_driver hm1246_i2c_driver = {
>> + .driver = {
>> + .of_match_table = hm1246_of_match,
>> + .pm = pm_ptr(&hm1246_pm_ops),
>> + .name = "hm1246",
>> + },
>> + .probe = hm1246_probe,
>> + .remove = hm1246_remove,
>> +};
>> +module_i2c_driver(hm1246_i2c_driver);
>> +
>> +MODULE_DESCRIPTION("Himax HM1246 camera driver");
>> +MODULE_AUTHOR("Matthias Fend <matthias.fend@emfend.at>");
>> +MODULE_LICENSE("GPL");
>>
>
^ permalink raw reply
* Re: [PATCH v11 3/6] mmc: sdhci-msm: Set ICE clk to TURBO at sdhci ICE init
From: Abhinaba Rakshit @ 2026-06-22 7:54 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Bjorn Andersson, Konrad Dybcio, Manivannan Sadhasivam,
James E.J. Bottomley, Martin K. Petersen, Adrian Hunter,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Neeraj Soni, Harshal Dev, Kuldeep Singh, linux-arm-msm,
linux-kernel, linux-scsi, linux-mmc, devicetree
In-Reply-To: <06999033-1c2f-4203-bdcc-d8e94ed281b7@oss.qualcomm.com>
On Thu, Jun 18, 2026 at 02:59:02PM +0200, Konrad Dybcio wrote:
> On 6/8/26 11:47 PM, Abhinaba Rakshit wrote:
> > MMC controller lacks a clock scaling mechanism, unlike the UFS
> > controller. By default, the MMC controller is set to TURBO mode
> > during probe, but the ICE clock remains at XO frequency,
> > leading to read/write performance degradation on eMMC.
> >
> > To address this, set the ICE clock to TURBO during sdhci_msm_ice_init
> > to align it with the controller clock. This ensures consistent
> > performance and avoids mismatches between the controller
> > and ICE clock frequencies.
> >
> > For platforms where ICE is represented as a separate device,
> > use the OPP framework to vote for TURBO mode, maintaining
> > proper voltage and power domain constraints.
> >
> > Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
> > Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> > Reviewed-by: Adrian Hunter <adrian.hunter@intel.com>
> > Signed-off-by: Abhinaba Rakshit <abhinaba.rakshit@oss.qualcomm.com>
> > ---
> > drivers/mmc/host/sdhci-msm.c | 24 ++++++++++++++++++++++++
> > 1 file changed, 24 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
> > index 0882ce74e0c9bdddd98341a67b97bcef74078e0c..b655bcb5b90c0677bbe3dc6140de488038fe5ee8 100644
> > --- a/drivers/mmc/host/sdhci-msm.c
> > +++ b/drivers/mmc/host/sdhci-msm.c
> > @@ -1901,6 +1901,8 @@ static void sdhci_msm_set_clock(struct sdhci_host *host, unsigned int clock)
> > #ifdef CONFIG_MMC_CRYPTO
> >
> > static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops; /* forward decl */
> > +static int sdhci_msm_ice_scale_clk(struct sdhci_msm_host *msm_host, unsigned long target_freq,
> > + bool round_ceil); /* forward decl */
>
> Can the definition simply be moved upwards?
Yes, we can do that.
I used a forward declaration because it appeared that sdhci_msm_ice_init()
was intentionally kept as the first ICE-related function in the file.
If that is not a requirement, I'm fine to move sdhci_msm_ice_scale_clk()
above and remove the forward declaration.
Abhinaba Rakshit
^ permalink raw reply
* Re: [PATCH v3 2/2] iio: adc: add Axiado SARADC driver
From: sashiko-bot @ 2026-06-22 7:54 UTC (permalink / raw)
To: Petar Stepanovic; +Cc: devicetree, conor+dt, robh
In-Reply-To: <20260622-axiado-ax3000-ax3005-saradc-v3-2-e57c7c7ae675@axiado.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Incorrect error code passed to dev_err_probe() causes probe to incorrectly succeed on regulator failure, leading to a zombie device.
--
commit e9d35c0c4f9ed7b92e86089b5cd8263bb84313d2
Author: Petar Stepanovic <pstepanovic@axiado.com>
iio: adc: add Axiado SARADC driver
This patch adds support for the SARADC controller found on Axiado AX3000
and AX3005 SoCs. The driver supports single-shot voltage reads through
the IIO subsystem, using SoC match data to determine the available input
channels.
> diff --git a/drivers/iio/adc/axiado_saradc.c b/drivers/iio/adc/axiado_saradc.c
> new file mode 100644
> index 0000000000000..12efe814318c5
> --- /dev/null
> +++ b/drivers/iio/adc/axiado_saradc.c
[ ... ]
> +static int axiado_saradc_probe(struct platform_device *pdev)
> +{
[ ... ]
> + info->clk_rate = clk_get_rate(info->clk);
> + if (!info->clk_rate)
> + return dev_err_probe(dev, -EINVAL, "invalid clock rate\n");
> +
> + ret = devm_regulator_get_enable_read_voltage(dev, "vref");
> + if (ret < 0)
> + return dev_err_probe(dev, info->vref_uV,
> + "failed to get vref voltage\n");
[Severity: High]
Does this error path incorrectly return 0 on failure?
Because info was allocated via devm_iio_device_alloc(), info->vref_uV is 0
before being assigned. Passing info->vref_uV to dev_err_probe() instead of
ret causes dev_err_probe() to return 0.
Will this result in the probe function returning success on regulator
failure, leaving the driver bound to a non-functional device and
silently dropping valid probe deferrals?
> + info->vref_uV = ret;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-axiado-ax3000-ax3005-saradc-v3-0-e57c7c7ae675@axiado.com?part=2
^ permalink raw reply
* Re: [PATCH v3 1/2] dt-bindings: iio: adc: add Axiado AX3000/AX3005 SARADC
From: sashiko-bot @ 2026-06-22 7:53 UTC (permalink / raw)
To: Petar Stepanovic; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260622-axiado-ax3000-ax3005-saradc-v3-1-e57c7c7ae675@axiado.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Missing IIO mailing list in MAINTAINERS entry.
--
commit 474cfbabb6190ddd4a26c0a176ce44e7335b5716
Author: Petar Stepanovic <pstepanovic@axiado.com>
dt-bindings: iio: adc: add Axiado AX3000/AX3005 SARADC
This commit documents the compatible strings, register regions, clocks, and
other properties for the 10-bit SAR ADC controller on the Axiado AX3000
and AX3005 SoCs. It also adds a new entry to the MAINTAINERS file.
[ ... ]
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 3d6db8cb608f3..963ac23a0a21e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -4336,6 +4336,13 @@ S: Orphan
> F: Documentation/devicetree/bindings/sound/axentia,*
> F: sound/soc/atmel/tse850-pcm5142.c
>
> +AXIADO SARADC DRIVER
> +M: Petar Stepanovic <pstepanovic@axiado.com>
> +M: Akhila Kavi <akavi@axiado.com>
> +M: Prasad Bolisetty <pbolisetty@axiado.com>
> +S: Maintained
> +F: Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
[Severity: Low]
Should this entry include the IIO subsystem mailing list?
Typically, IIO ADC drivers direct patches and discussions to the IIO
mailing list by including an "L: linux-iio@vger.kernel.org" entry in
their MAINTAINERS block.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260622-axiado-ax3000-ax3005-saradc-v3-0-e57c7c7ae675@axiado.com?part=1
^ permalink raw reply
* Re: Re: [PATCH net-next v8 3/6] net: stmmac: eic7700: make RGMII delay properties optional
From: Andrew Lunn @ 2026-06-22 7:52 UTC (permalink / raw)
To: 李志
Cc: Maxime Chevallier, devicetree, andrew+netdev, davem, edumazet,
kuba, robh, krzk+dt, conor+dt, netdev, pabeni, mcoquelin.stm32,
alexandre.torgue, rmk+kernel, pjw, palmer, aou, alex, linux-riscv,
linux-stm32, linux-arm-kernel, linux-kernel, ningyu, linmin,
pinkesh.vaghela, pritesh.patel, weishangjuan, horms, lee
In-Reply-To: <512b77d5.993b.19eed207fc9.Coremail.lizhi2@eswincomputing.com>
> I'm preparing a v9 of the series. The next revision will address the
> issues reported by Sashiko review, mainly DT binding schema and DTS
> warnings.
>
> Before I post v9, I'd like to check whether you have any concerns or
> suggestions regarding the driver changes.
From what i remember, i think the patch was O.K, but i've looked at
100s of other patches since then. The commit message sounds like the
basic design is correct.
Andrew
^ permalink raw reply
* Re: [PATCH v5 1/2] dt-bindings: arm: xen: Convert to DT schema
From: Krzysztof Kozlowski @ 2026-06-22 7:48 UTC (permalink / raw)
To: Tejas Mutalikdesai; +Cc: devicetree, robh, krzk+dt, conor+dt, sstabellini
In-Reply-To: <20260618151147.9438-1-tejasmutalikdesai@gmail.com>
On Thu, Jun 18, 2026 at 08:41:46PM +0530, Tejas Mutalikdesai wrote:
> Convert the Xen ARM device tree binding documentation from the legacy
> plain-text format (Documentation/devicetree/bindings/arm/xen.txt) to
> the DT schema format, as required by the modern DT binding process.
>
> The "hypervisor" node is named without a unit-address. The name is part
> of the Xen ABI and is matched verbatim by the kernel using strcmp() in
> arch/arm/xen/enlighten.c and arch/arm64/kernel/acpi.c, so $nodename uses
> 'const: hypervisor'. The node has a reg but no unit-address, so dtc emits
Honestly, this is a violation of DT spec, chapter 2.2.1.1:
"The unit-address must match the first address specified in the reg
property of the node."
I understand that you did not introduce this, but you do introduce DTC
warning into bindings, which I think we do not allow. dt_binding_check
must be warning free.
If I understood correctly this broken behavior was introduced in commit
9b08aaa3199a4dffca73c7cdec813b483b5b2d3b. Without any explanation why a
correct method of matching/finding by compatible is changed into
INCORRECT (nodename is not proper ABI) finding by node name and ignoring
DT Spec.
That commit message is simply terrible. Tells one thing - move some code
- but does something completely different - introduces ABI for node
name! ABI which is heavily discouraged and plain wrong.
I don't have enough of words to express the poor quality of that commit.
That said, IMO, the Xen/driver code should be changed to conform to DT
spec. If Xen is not interested in conforming to DT spec, then we should
not have in it upstream Linux kernel. This is that simple. Xen does not
get exceptions.
I also get that task might be something more than you signed up for,
thus the binding should stay unconverted till someone wants to fixup
this broken Xen code.
Best regards,
Krzysztof
^ permalink raw reply
* [PATCH v3 2/2] iio: adc: add Axiado SARADC driver
From: Petar Stepanovic @ 2026-06-22 7:47 UTC (permalink / raw)
To: Akhila Kavi, Prasad Bolisetty, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Harshit Shah
Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel,
Petar Stepanovic
In-Reply-To: <20260622-axiado-ax3000-ax3005-saradc-v3-0-e57c7c7ae675@axiado.com>
Add support for the SARADC controller found on Axiado AX3000 and
AX3005 SoCs.
The driver supports single-shot voltage reads through the IIO
subsystem. The number of available input channels is selected from
the SoC match data, allowing AX3000 and AX3005 variants to use the
same driver.
Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
---
MAINTAINERS | 1 +
drivers/iio/adc/Kconfig | 10 ++
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/axiado_saradc.c | 245 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 257 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 932bba890780..e6dadfa65ee0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4318,6 +4318,7 @@ M: Akhila Kavi <akavi@axiado.com>
M: Prasad Bolisetty <pbolisetty@axiado.com>
S: Maintained
F: Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
+F: drivers/iio/adc/axiado_saradc.c
AXIS ARTPEC ARM64 SoC SUPPORT
M: Jesper Nilsson <jesper.nilsson@axis.com>
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index a9dedbb8eb46..a9ba600a5f64 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -631,6 +631,16 @@ config AT91_SAMA5D2_ADC
To compile this driver as a module, choose M here: the module will be
called at91-sama5d2_adc.
+config AXIADO_SARADC
+ tristate "Axiado SARADC driver"
+ depends on ARCH_AXIADO || COMPILE_TEST
+ help
+ Say yes here to build support for the SARADC found in Axiado
+ SoCs.
+
+ To compile this driver as a module, choose M here: the module
+ will be called axiado_saradc.
+
config AXP20X_ADC
tristate "X-Powers AXP20X and AXP22X ADC driver"
depends on MFD_AXP20X
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 097357d146ba..96de0ce1d90a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_ADI_AXI_ADC) += adi-axi-adc.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
+obj-$(CONFIG_AXIADO_SARADC) += axiado_saradc.o
obj-$(CONFIG_AXP20X_ADC) += axp20x_adc.o
obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
obj-$(CONFIG_BCM_IPROC_ADC) += bcm_iproc_adc.o
diff --git a/drivers/iio/adc/axiado_saradc.c b/drivers/iio/adc/axiado_saradc.c
new file mode 100644
index 000000000000..12efe814318c
--- /dev/null
+++ b/drivers/iio/adc/axiado_saradc.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2026 Axiado Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/units.h>
+
+#include <linux/iio/iio.h>
+
+/* Register offsets */
+#define AX_SARADC_GLOBAL_CTRL_REG 0x0004
+#define AX_SARADC_MANUAL_CTRL_REG 0x0008
+#define AX_SARADC_DOUT_REG 0x001C
+
+/* GLOBAL_CTRL register fields */
+#define AX_SARADC_GLOBAL_CTRL_CH_EN_MASK GENMASK(31, 16)
+#define AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK GENMASK(6, 5)
+#define AX_SARADC_GLOBAL_CTRL_MODE_MASK GENMASK(4, 3)
+#define AX_SARADC_GLOBAL_CTRL_PD BIT(2)
+#define AX_SARADC_GLOBAL_CTRL_ENABLE BIT(0)
+
+/* GLOBAL_CTRL register values */
+#define AX_SARADC_GLOBAL_CTRL_SAMPLE_16 \
+ FIELD_PREP(AX_SARADC_GLOBAL_CTRL_SAMPLE_MASK, 0)
+
+#define AX_SARADC_GLOBAL_CTRL_MODE_MANUAL \
+ FIELD_PREP(AX_SARADC_GLOBAL_CTRL_MODE_MASK, 1)
+
+/* MANUAL_CTRL register fields */
+#define AX_SARADC_MANUAL_CTRL_ENABLE BIT(0)
+#define AX_SARADC_MANUAL_CTRL_CH_SEL_MASK GENMASK(4, 1)
+
+#define AX_SARADC_MANUAL_CTRL_EN(ch) \
+ (AX_SARADC_MANUAL_CTRL_ENABLE | \
+ FIELD_PREP(AX_SARADC_MANUAL_CTRL_CH_SEL_MASK, ch))
+
+#define AX_RESOLUTION_BITS 10
+#define AX_SARADC_CONV_CYCLES 13
+#define AX_SARADC_CONV_DELAY_MARGIN_US 10
+
+struct axiado_saradc {
+ void __iomem *regs;
+ struct clk *clk;
+ struct mutex lock; /* Serializes ADC conversions. */
+ unsigned long clk_rate;
+ int vref_uV;
+};
+
+static int axiado_saradc_conversion(struct axiado_saradc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ unsigned long usecs;
+
+ guard(mutex)(&info->lock);
+
+ /* Select the channel to be used and trigger conversion */
+ writel(AX_SARADC_MANUAL_CTRL_EN(chan->channel),
+ info->regs + AX_SARADC_MANUAL_CTRL_REG);
+
+ /* Hardware requires 13 conversion cycles at clk_rate */
+ usecs = DIV_ROUND_UP(AX_SARADC_CONV_CYCLES * USEC_PER_SEC,
+ info->clk_rate);
+ fsleep(usecs + AX_SARADC_CONV_DELAY_MARGIN_US);
+
+ *val = readl(info->regs + AX_SARADC_DOUT_REG) &
+ GENMASK(AX_RESOLUTION_BITS - 1, 0);
+
+ /* Stop manual conversion */
+ writel(0, info->regs + AX_SARADC_MANUAL_CTRL_REG);
+
+ return 0;
+}
+
+static int axiado_saradc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct axiado_saradc *info = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = axiado_saradc_conversion(info, chan, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = info->vref_uV / (MICRO / MILLI);
+ *val2 = AX_RESOLUTION_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info axiado_saradc_iio_info = {
+ .read_raw = axiado_saradc_read_raw,
+};
+
+struct axiado_saradc_soc_data {
+ const char *name;
+ unsigned int num_channels;
+};
+
+static const struct axiado_saradc_soc_data ax3000_saradc_data = {
+ .name = "ax3000_saradc",
+ .num_channels = 16,
+};
+
+static const struct axiado_saradc_soc_data ax3005_saradc_data = {
+ .name = "ax3005_saradc",
+ .num_channels = 8,
+};
+
+#define AX_SARADC_CH(_index, _id) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (_index), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .datasheet_name = (_id), \
+ }
+
+static const struct iio_chan_spec axiado_saradc_iio_channels[] = {
+ AX_SARADC_CH(0, "adc0"), AX_SARADC_CH(1, "adc1"),
+ AX_SARADC_CH(2, "adc2"), AX_SARADC_CH(3, "adc3"),
+ AX_SARADC_CH(4, "adc4"), AX_SARADC_CH(5, "adc5"),
+ AX_SARADC_CH(6, "adc6"), AX_SARADC_CH(7, "adc7"),
+ AX_SARADC_CH(8, "adc8"), AX_SARADC_CH(9, "adc9"),
+ AX_SARADC_CH(10, "adc10"), AX_SARADC_CH(11, "adc11"),
+ AX_SARADC_CH(12, "adc12"), AX_SARADC_CH(13, "adc13"),
+ AX_SARADC_CH(14, "adc14"), AX_SARADC_CH(15, "adc15"),
+};
+
+static void axiado_saradc_disable(void *data)
+{
+ struct axiado_saradc *info = data;
+
+ writel(AX_SARADC_GLOBAL_CTRL_PD, info->regs + AX_SARADC_GLOBAL_CTRL_REG);
+}
+
+static int axiado_saradc_probe(struct platform_device *pdev)
+{
+ const struct axiado_saradc_soc_data *soc_data;
+ struct device *dev = &pdev->dev;
+ struct axiado_saradc *info;
+ struct iio_dev *indio_dev;
+ u32 regval;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ info = iio_priv(indio_dev);
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->clk = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(info->clk))
+ return PTR_ERR(info->clk);
+
+ info->clk_rate = clk_get_rate(info->clk);
+ if (!info->clk_rate)
+ return dev_err_probe(dev, -EINVAL, "invalid clock rate\n");
+
+ ret = devm_regulator_get_enable_read_voltage(dev, "vref");
+ if (ret < 0)
+ return dev_err_probe(dev, info->vref_uV,
+ "failed to get vref voltage\n");
+ info->vref_uV = ret;
+
+ soc_data = device_get_match_data(dev);
+ if (!soc_data)
+ return dev_err_probe(dev, -EINVAL, "failed to get match data\n");
+
+ ret = devm_mutex_init(dev, &info->lock);
+ if (ret)
+ return ret;
+
+ regval = FIELD_PREP(AX_SARADC_GLOBAL_CTRL_CH_EN_MASK,
+ GENMASK(soc_data->num_channels - 1, 0)) |
+ AX_SARADC_GLOBAL_CTRL_SAMPLE_16 |
+ AX_SARADC_GLOBAL_CTRL_MODE_MANUAL |
+ AX_SARADC_GLOBAL_CTRL_ENABLE;
+
+ writel(AX_SARADC_GLOBAL_CTRL_PD, info->regs + AX_SARADC_GLOBAL_CTRL_REG);
+ writel(regval, info->regs + AX_SARADC_GLOBAL_CTRL_REG);
+
+ ret = devm_add_action_or_reset(dev, axiado_saradc_disable, info);
+ if (ret)
+ return ret;
+
+ indio_dev->name = soc_data->name;
+ indio_dev->info = &axiado_saradc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = axiado_saradc_iio_channels;
+ indio_dev->num_channels = soc_data->num_channels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id axiado_saradc_match[] = {
+ {
+ .compatible = "axiado,ax3000-saradc",
+ .data = &ax3000_saradc_data,
+ },
+ {
+ .compatible = "axiado,ax3005-saradc",
+ .data = &ax3005_saradc_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axiado_saradc_match);
+
+static struct platform_driver axiado_saradc_driver = {
+ .driver = {
+ .name = "axiado-saradc",
+ .of_match_table = axiado_saradc_match,
+ },
+ .probe = axiado_saradc_probe,
+};
+module_platform_driver(axiado_saradc_driver);
+
+MODULE_AUTHOR("AXIADO CORPORATION");
+MODULE_DESCRIPTION("AXIADO SARADC driver");
+MODULE_LICENSE("GPL");
--
2.34.1
^ permalink raw reply related
* [PATCH v3 1/2] dt-bindings: iio: adc: add Axiado AX3000/AX3005 SARADC
From: Petar Stepanovic @ 2026-06-22 7:47 UTC (permalink / raw)
To: Akhila Kavi, Prasad Bolisetty, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Harshit Shah
Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel,
Petar Stepanovic, Conor Dooley
In-Reply-To: <20260622-axiado-ax3000-ax3005-saradc-v3-0-e57c7c7ae675@axiado.com>
The Axiado AX3000 and AX3005 SoCs include a 10-bit SAR ADC controller.
AX3000 supports 16 input channels, while AX3005 supports 8 input
channels.
Document the compatible strings, register region, clock, reference
voltage supply, and IIO channel cells.
Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
Acked-by: Conor Dooley <conor.dooley@microchip.com>
---
.../bindings/iio/adc/axiado,ax3000-saradc.yaml | 63 ++++++++++++++++++++++
MAINTAINERS | 7 +++
2 files changed, 70 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
new file mode 100644
index 000000000000..b910852aa56f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/axiado,ax3000-saradc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Axiado AX3000/AX3005 Successive Approximation Register ADC
+
+description:
+ The Axiado AX3000/AX3005 SAR ADC is a 10-bit ADC with sixteen input
+ channels on AX3000 and eight input channels on AX3005.
+
+maintainers:
+ - Petar Stepanovic <pstepanovic@axiado.com>
+ - Akhila Kavi <akavi@axiado.com>
+ - Prasad Bolisetty <pbolisetty@axiado.com>
+
+properties:
+ compatible:
+ enum:
+ - axiado,ax3000-saradc
+ - axiado,ax3005-saradc
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: saradc
+
+ '#io-channel-cells':
+ const: 1
+
+ vref-supply:
+ description: Reference voltage regulator supplying the ADC
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - '#io-channel-cells'
+ - vref-supply
+
+additionalProperties: false
+
+examples:
+ - |
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ adc@806a0000 {
+ compatible = "axiado,ax3000-saradc";
+ reg = <0x0 0x806a0000 0x0 0x400>;
+ clocks = <&pclk>;
+ clock-names = "saradc";
+ vref-supply = <&vref_reg>;
+ #io-channel-cells = <1>;
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index b2040011a386..932bba890780 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4312,6 +4312,13 @@ S: Orphan
F: Documentation/devicetree/bindings/sound/axentia,*
F: sound/soc/atmel/tse850-pcm5142.c
+AXIADO SARADC DRIVER
+M: Petar Stepanovic <pstepanovic@axiado.com>
+M: Akhila Kavi <akavi@axiado.com>
+M: Prasad Bolisetty <pbolisetty@axiado.com>
+S: Maintained
+F: Documentation/devicetree/bindings/iio/adc/axiado,ax3000-saradc.yaml
+
AXIS ARTPEC ARM64 SoC SUPPORT
M: Jesper Nilsson <jesper.nilsson@axis.com>
M: Lars Persson <lars.persson@axis.com>
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/2] iio: adc: Add Axiado SARADC driver
From: Petar Stepanovic @ 2026-06-22 7:47 UTC (permalink / raw)
To: Akhila Kavi, Prasad Bolisetty, Jonathan Cameron, David Lechner,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Harshit Shah
Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel,
Petar Stepanovic, Conor Dooley
This series adds support for the SAR ADC controller found on Axiado
AX3000 and AX3005 SoCs.
A new driver is needed because this SAR ADC controller is a SoC-specific
hardware block used on Axiado SoCs. It has its own register layout,
channel enable handling, conversion control, and data readout sequence,
and it does not match any existing upstream IIO ADC driver.
AX3000 provides sixteen input channels, while AX3005 provides eight
input channels. The driver uses SoC match data to select the number of
available channels for each compatible.
The driver supports single-shot voltage reads through the IIO subsystem
and uses the reference voltage regulator for scale calculation.
The datasheet is not publicly available. Public high-level product
information is available at:
https://axiado.com/products/#AX3080
The register definitions and programming sequence used by this driver
are based on Axiado internal SoC documentation.
Signed-off-by: Petar Stepanovic <pstepanovic@axiado.com>
---
Changes in v3:
- Fixed vref regulator error handling.
- Added linux/units.h and used MICRO / MILLI in scale calculation.
- Reordered struct axiado_saradc members to improve the structure
layout.
- Fixed indentation/alignment of multi-line FIELD_PREP(), GENMASK(), and
writel() expressions.
- Removed the blank line before module_platform_driver().
- Link to v2: https://lore.kernel.org/r/20260611-axiado-ax3000-ax3005-saradc-v2-0-913c9de7c64c@axiado.com
Changes in v2:
- Fixed the devicetree example node name to use the generic ADC node name.
- Removed the explicit `depends on OF` from Kconfig.
- Cleaned up and reordered header includes.
- Added missing includes for `bits.h`, `clk.h`, `cleanup.h`, and `err.h`.
- Removed unused `linux/kernel.h` include.
- Renamed register offset macros to use the `_REG` suffix.
- Renamed register bitfield macros to include the register name prefix.
- Added separate macros for `GLOBAL_CTRL` and `MANUAL_CTRL` register
fields and values.
- Replaced `iowrite32()` / `ioread32()` with `writel()` / `readl()`.
- Moved ADC conversion locking into `axiado_saradc_conversion()` using
`guard(mutex)`.
- Replaced `usleep_range()` with `fsleep()`.
- Renamed `vref_uv` to `vref_uV`.
- Added SoC-specific device names in `axiado_saradc_soc_data`.
- Used the fixed SoC-specific name for `indio_dev->name`.
- Removed unused buffered scan configuration from IIO channels.
- Added a managed cleanup action to disable the SARADC hardware on driver
unbind or probe failure.
- Switched to a local `struct device *dev` helper in probe.
- Used `devm_mutex_init()` for mutex initialization.
- Simplified error handling by using `dev_err_probe()`.
- Updated probe variable declarations to follow reverse Christmas tree
order.
- Fixed the `of_device_id` terminator style.
- Replaced `KBUILD_MODNAME` with a fixed driver name string.
- Link to v1: https://lore.kernel.org/r/20260528-axiado-ax3000-ax3005-saradc-v1-0-345dd5f6608a@axiado.com
---
Petar Stepanovic (2):
dt-bindings: iio: adc: add Axiado AX3000/AX3005 SARADC
iio: adc: add Axiado SARADC driver
.../bindings/iio/adc/axiado,ax3000-saradc.yaml | 63 ++++++
MAINTAINERS | 8 +
drivers/iio/adc/Kconfig | 10 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/axiado_saradc.c | 245 +++++++++++++++++++++
5 files changed, 327 insertions(+)
---
base-commit: 51f0c0b8545b23963afd5d43a8f56ee05bfa54da
change-id: 20260508-axiado-ax3000-ax3005-saradc-151aed5d25da
Best regards,
--
Petar Stepanovic <pstepanovic@axiado.com>
^ permalink raw reply
* Re: [PATCH v11 5/6] arm64: dts: qcom: monaco: Add OPP-table for ICE UFS and ICE eMMC nodes
From: Abhinaba Rakshit @ 2026-06-22 7:38 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Bjorn Andersson, Konrad Dybcio, Manivannan Sadhasivam,
James E.J. Bottomley, Martin K. Petersen, Adrian Hunter,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Neeraj Soni, Harshal Dev, Kuldeep Singh, linux-arm-msm,
linux-kernel, linux-scsi, linux-mmc, devicetree
In-Reply-To: <d8fd7888-cf7d-47e2-8e77-3ba705c88502@oss.qualcomm.com>
On Thu, Jun 18, 2026 at 03:04:57PM +0200, Konrad Dybcio wrote:
> On 6/8/26 11:47 PM, Abhinaba Rakshit wrote:
> > Qualcomm Inline Crypto Engine (ICE) platform driver now, supports
> > an optional OPP-table.
> >
> > Add OPP-table for ICE UFS and ICE eMMC device nodes for Monaco
> > platform.
> >
> > Signed-off-by: Abhinaba Rakshit <abhinaba.rakshit@oss.qualcomm.com>
> > ---
> > arch/arm64/boot/dts/qcom/monaco.dtsi | 37 ++++++++++++++++++++++++++++++++++++
> > 1 file changed, 37 insertions(+)
> >
> > diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi
> > index a1b6e6211b84d0d5008231c55613a0ccd61b9450..d9298d8b7874b8669b2cded2a28a99dce6eadbda 100644
> > --- a/arch/arm64/boot/dts/qcom/monaco.dtsi
> > +++ b/arch/arm64/boot/dts/qcom/monaco.dtsi
> > @@ -2742,6 +2742,27 @@ ice: crypto@1d88000 {
> > clock-names = "core",
> > "iface";
> > power-domains = <&gcc GCC_UFS_PHY_GDSC>;
> > +
> > + operating-points-v2 = <&ice_opp_table>;
> > +
> > + ice_opp_table: opp-table {
> > + compatible = "operating-points-v2";
> > +
> > + opp-75000000 {
> > + opp-hz = /bits/ 64 <75000000>;
> > + required-opps = <&rpmhpd_opp_svs_l1>;
> > + };
> > +
> > + opp-201600000 {
> > + opp-hz = /bits/ 64 <201600000>;
> > + required-opps = <&rpmhpd_opp_svs_l1>;
> > + };
>
> Since 75 MHz and 201.6 Mhz require the same power level, is the former
> OPP any useful?
Yes, both use the same power requirements. However recommended by the ICE team,
the DT should include all opp/freq supported by the hardware.
Abhinaba Rakshit
^ permalink raw reply
* Re: [PATCH v5 2/2] hw_random: timeriomem-rng: add configurable read width and data mask
From: Krzysztof Kozlowski @ 2026-06-22 7:37 UTC (permalink / raw)
To: Jad Keskes
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivia Mackall,
Herbert Xu, Alexander Clouter, devicetree, linux-crypto,
linux-kernel
In-Reply-To: <20260618120110.36439-2-inasj268@gmail.com>
On Thu, Jun 18, 2026 at 01:01:10PM +0100, Jad Keskes wrote:
> The TODO for supporting read sizes other than 32 bits and masking has
> been sitting in this driver since 2009. Implement it.
And who - which driver/platform - uses or needs that? We are not really
doing things because someone wrote TODO in 2009. Quite likely notes
from 2009 are way outdated...
>
> Add reg-io-width (1, 2, or 4 bytes) and mask support. The read loop
> dispatches on width using readb/readw/readl so a configured 1-byte
> access does not trigger a bus error on hardware that rejects 32-bit
> reads to that address. The mask is ANDed with the value before storing.
>
> These are platform properties, not runtime policy -- width depends on
> SoC integration, mask reflects which output bits carry entropy.
>
> The alignment check in probe is updated to verify the resource is
> aligned to the configured width instead of hardcoding 4-byte alignment.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v5 1/2] dt-bindings: rng: timeriomem_rng: add reg-io-width and mask properties
From: Krzysztof Kozlowski @ 2026-06-22 7:36 UTC (permalink / raw)
To: Jad Keskes
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Olivia Mackall,
Herbert Xu, Alexander Clouter, devicetree, linux-crypto,
linux-kernel
In-Reply-To: <20260618120110.36439-1-inasj268@gmail.com>
On Thu, Jun 18, 2026 at 01:01:09PM +0100, Jad Keskes wrote:
> Add optional reg-io-width (1, 2, or 4 bytes) and mask properties.
> reg-io-width selects the bus access size. mask is ANDed with the raw
> register value to allow only the entropy-bearing bits through.
You should explain here why. Why are you doing this? Why do we want
this?
>
> Update the example to show a 1-byte configuration.
>
> Signed-off-by: Jad Keskes <inasj268@gmail.com>
> ---
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v11 1/6] soc: qcom: ice: Add OPP-based clock scaling support for ICE
From: Abhinaba Rakshit @ 2026-06-22 7:34 UTC (permalink / raw)
To: Konrad Dybcio
Cc: Bjorn Andersson, Konrad Dybcio, Manivannan Sadhasivam,
James E.J. Bottomley, Martin K. Petersen, Adrian Hunter,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Neeraj Soni, Harshal Dev, Kuldeep Singh, linux-arm-msm,
linux-kernel, linux-scsi, linux-mmc, devicetree
In-Reply-To: <d1232243-2f23-423b-84ac-4463eac79f9a@oss.qualcomm.com>
On Thu, Jun 18, 2026 at 03:01:54PM +0200, Konrad Dybcio wrote:
> On 6/8/26 11:47 PM, Abhinaba Rakshit wrote:
> > Register optional operation-points-v2 table for ICE device
> > during device probe. Attach the OPP-table with only the ICE
> > core clock. Since, dtbinding is on a transition phase to include
> > iface clock and clock-names, attaching the opp-table to core clock
> > remains optional such that it does not cause probe failures.
> >
> > Introduce clock scaling API qcom_ice_scale_clk which scale ICE
> > core clock based on the target frequency provided and if a valid
> > OPP-table is registered. Use round_ceil passed to decide on the
> > rounding of the clock freq against OPP-table. Clock scaling is
> > disabled when a valid OPP-table is not registered.
> >
> > This ensures when an ICE-device specific OPP table is available,
> > use the PM OPP framework to manage frequency scaling and maintain
> > proper power-domain constraints.
> >
> > Also, ensure to drop the votes in suspend to prevent power/thermal
> > retention. Subsequently restore the frequency in resume from
> > core_clk_freq which stores the last ICE core clock operating frequency.
> >
> > Reviewed-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
> > Signed-off-by: Abhinaba Rakshit <abhinaba.rakshit@oss.qualcomm.com>
> > ---
>
> [...]
>
> > @@ -335,6 +342,11 @@ int qcom_ice_suspend(struct qcom_ice *ice)
> > {
> > clk_disable_unprepare(ice->iface_clk);
> > clk_disable_unprepare(ice->core_clk);
> > +
> > + /* Drop the clock votes while suspend */
> > + if (ice->has_opp)
> > + dev_pm_opp_set_rate(ice->dev, 0);
>
> The PM core will quiesce the vote as the device suspends, this is
> unnecessary. Similarly, the rate restore logic will become unnecessary.
> Especially since dev_pm_opp_set_rate(0) does not actually do any rate
> setting.
This section was earlier discussed in the patchset v4:
https://lore.kernel.org/all/7b219a50-6971-4a0c-a465-418f8abd5556@oss.qualcomm.com/
The intention here was to drop the RPMh votes once the device goes to suspend same
as the storage drivers such as mmc drivers does:
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/drivers/mmc/host/sdhci-msm.c#n2946
This was done to leave the hanging votes *on* for unused clocks.
However, I get your point, due to mean to say that once device goes to suspend
and GDSC power-domain will be turned OFF, it will automatically quiesce the
performance votes?
Abhinaba Rakshit
^ permalink raw reply
* Re: [PATCH v6 2/8] dt-bindings: clock: qcom,glymur-tcsr: Add mahua support
From: Krzysztof Kozlowski @ 2026-06-22 7:32 UTC (permalink / raw)
To: Qiang Yu
Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Brian Masney,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Taniya Das,
Konrad Dybcio, linux-arm-msm, linux-clk, devicetree, linux-kernel,
krishna.chundru
In-Reply-To: <20260621-tcsr_qref_0622-v6-2-c939c22ded0c@oss.qualcomm.com>
On Sun, Jun 21, 2026 at 10:11:25PM -0700, Qiang Yu wrote:
> Mahua shares the same QREF TX/RPT/RX component naming as Glymur, but
> has a different topology: a single QREF block fed by REFGEN3 only,
> rather than the two independent blocks fed by REFGEN3 and REFGEN4 on
> Glymur.
>
> Add qcom,mahua-tcsr compatible and document its required supply
> properties.
>
> Signed-off-by: Qiang Yu <qiang.yu@oss.qualcomm.com>
> ---
> .../bindings/clock/qcom,glymur-tcsr.yaml | 68 ++++++++++++++++------
> 1 file changed, 50 insertions(+), 18 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/clock/qcom,glymur-tcsr.yaml b/Documentation/devicetree/bindings/clock/qcom,glymur-tcsr.yaml
> index 16fc6ab87f9b..2b6422627165 100644
> --- a/Documentation/devicetree/bindings/clock/qcom,glymur-tcsr.yaml
> +++ b/Documentation/devicetree/bindings/clock/qcom,glymur-tcsr.yaml
> @@ -20,7 +20,9 @@ description: |
> properties:
> compatible:
> items:
> - - const: qcom,glymur-tcsr
> + - enum:
> + - qcom,glymur-tcsr
You could have made this enum in the first patch, so you would avoid
changing the same line twice. Usually adding line in patch #1 and then
removing it in patch #2 is indication of something to improve.
> + - qcom,mahua-tcsr
> - const: syscon
>
> clocks:
> @@ -41,9 +43,11 @@ properties:
> vdda-qrefrpt2-0p9-supply: true
> vdda-qrefrpt3-0p9-supply: true
> vdda-qrefrpt4-0p9-supply: true
> + vdda-qrefrpt5-0p9-supply: true
> vdda-qrefrx0-0p9-supply: true
> vdda-qrefrx1-0p9-supply: true
> vdda-qrefrx2-0p9-supply: true
> + vdda-qrefrx3-0p9-supply: true
> vdda-qrefrx4-0p9-supply: true
> vdda-qrefrx5-0p9-supply: true
> vdda-qreftx0-0p9-supply: true
> @@ -54,26 +58,54 @@ properties:
> vdda-refgen4-0p9-supply: true
> vdda-refgen4-1p2-supply: true
>
> +allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + const: qcom,glymur-tcsr
> + then:
> + required:
> + - vdda-qrefrpt0-0p9-supply
> + - vdda-qrefrpt1-0p9-supply
> + - vdda-qrefrpt2-0p9-supply
> + - vdda-qrefrpt3-0p9-supply
> + - vdda-qrefrpt4-0p9-supply
> + - vdda-qrefrx0-0p9-supply
> + - vdda-qrefrx1-0p9-supply
> + - vdda-qrefrx2-0p9-supply
> + - vdda-qrefrx4-0p9-supply
> + - vdda-qrefrx5-0p9-supply
> + - vdda-qreftx0-0p9-supply
> + - vdda-qreftx0-1p2-supply
> + - vdda-qreftx1-0p9-supply
> + - vdda-refgen3-0p9-supply
> + - vdda-refgen3-1p2-supply
> + - vdda-refgen4-0p9-supply
> + - vdda-refgen4-1p2-supply
It is fine, although this you could as well keep like that in first
patch and mention in commit msg, that binding will grow, thus you
already define if:then: block to accommodate future changes.
Best regards,
Krzysztof
^ permalink raw reply
* RE: [PATCH 0/7] soc: aspeed: Add AST2600 eSPI controller support
From: YH Chung @ 2026-06-22 7:31 UTC (permalink / raw)
To: YH Chung, Shulzhenko, Oleksandr, Arnd Bergmann, Andrew Jeffery,
Conor Dooley
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Joel Stanley,
Ryan Chen, Philipp Zabel, devicetree@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-aspeed@lists.ozlabs.org, linux-kernel@vger.kernel.org,
openbmc@lists.ozlabs.org, maciej.lawniczak@intel.com, Mark Brown
In-Reply-To: <KL1PR0601MB427604A7136947D020CF272D90002@KL1PR0601MB4276.apcprd06.prod.outlook.com>
Hi Shulzhenko, Arnd, Andrew
Please allow me to summarize the potential approaches we have discussed so
far for upstreaming the eSPI driver:
(1) Reuse the existing SPI subsystem and treat eSPI packets as pure signals.
(2) Maintain the driver under the SoC subsystem, since there is currently no
eSPI subsystem.
(3) Create a new eSPI subsystem and rewrite the eSPI driver accordingly.
For option 1, we do not think this would be a good fit, because eSPI has
clearly defined semantics for each channel, and our hardware exposes
different sets of registers for each of them.
For option 2, we think this would be a practical short-term solution, and we
can expose the per-channel functionality through the existing GPIO, MCTP, and
MTD subsystems for the VW, OOB, and flash channels, respectively.
However, this may need to be revisited once an eSPI subsystem becomes
available in-tree.
For option 3, our concern is that there is currently no other eSPI hardware
or driver in-tree that could serve as a reference for defining a more generic
interface. Creating a new subsystem at this stage may therefore be somewhat
over-engineered, since it would only have one user.
We understand that Nuvoton may be the next likely vendor to support eSPI in
their BMC SoCs. Since drivers/soc patches for Nuvoton BMCs also go through
the BMC tree, we expect there would be an opportunity to reconsider whether
an independent eSPI subsystem is needed once another user appears.
Based on the above, we would like to prepare and submit the next revision
with the driver located under drivers/soc.
Could you please let us know if there are any concerns or suggestions that we
have not covered, or whether we could send the next revision for review?
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox