Devicetree
 help / color / mirror / Atom feed
* [PATCH v4 05/16] spi: spi-mem: add execute_tuning callback and spi_mem_execute_tuning()
From: Santhosh Kumar K @ 2026-06-18  7:37 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt, miquel.raynal, richard,
	vigneshr, pratyush, mwalle, takahiro.kuwano
  Cc: linux-spi, devicetree, linux-kernel, linux-mtd, praneeth,
	u-kumar1, a-dutta, s-k6
In-Reply-To: <20260618073725.84733-1-s-k6@ti.com>

SPI memory controllers that support high-speed operating modes often
require a tuning procedure to calibrate internal timing before operating
at maximum frequency. There is currently no standard spi-mem interface
for drivers to trigger this procedure.

Add an execute_tuning callback to struct spi_controller_mem_ops. The
callback receives a mandatory read op template and an optional write op
template. On success the controller sets op->max_freq in each provided
template to the validated clock rate.

Add the corresponding spi_mem_execute_tuning() wrapper that validates
inputs and returns -EOPNOTSUPP when the controller has not implemented
the callback, allowing callers to handle controllers that do not support
tuning gracefully.

Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Santhosh Kumar K <s-k6@ti.com>
---
 drivers/spi/spi-mem.c       | 31 +++++++++++++++++++++++++++++++
 include/linux/spi/spi-mem.h | 14 ++++++++++++++
 2 files changed, 45 insertions(+)

diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index e20eca1b8245..571a7dd9c2a4 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -660,6 +660,37 @@ u64 spi_mem_calc_op_duration(struct spi_mem *mem, struct spi_mem_op *op)
 }
 EXPORT_SYMBOL_GPL(spi_mem_calc_op_duration);
 
+/**
+ * spi_mem_execute_tuning() - Execute controller tuning procedure
+ * @mem: the SPI memory device
+ * @read_op: read operation template (mandatory)
+ * @write_op: write operation template (optional, may be NULL)
+ *
+ * Requests the controller to perform tuning for high-speed operation
+ * using the provided op templates. On success the controller callback
+ * sets @read_op->max_freq (and @write_op->max_freq when non-NULL) to
+ * the validated clock rate.
+ *
+ * Return: 0 on success, -EINVAL if @mem or @read_op is NULL,
+ *         -EOPNOTSUPP if controller doesn't support tuning,
+ *         or a controller-specific error code on failure.
+ */
+int spi_mem_execute_tuning(struct spi_mem *mem, struct spi_mem_op *read_op,
+			   struct spi_mem_op *write_op)
+{
+	struct spi_controller *ctlr;
+
+	if (!mem || !read_op)
+		return -EINVAL;
+
+	ctlr = mem->spi->controller;
+	if (!ctlr->mem_ops || !ctlr->mem_ops->execute_tuning)
+		return -EOPNOTSUPP;
+
+	return ctlr->mem_ops->execute_tuning(mem, read_op, write_op);
+}
+EXPORT_SYMBOL_GPL(spi_mem_execute_tuning);
+
 static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
 				      u64 offs, size_t len, void *buf)
 {
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index f660bb2e9f85..ef5a6d60bae9 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -346,6 +346,15 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
  * @poll_status: poll memory device status until (status & mask) == match or
  *               when the timeout has expired. It fills the data buffer with
  *               the last status value.
+ * @execute_tuning: run the controller tuning procedure using the provided
+ *		    read and optional write op templates. On success, set
+ *		    @read_op->max_freq (and @write_op->max_freq when non-NULL)
+ *		    to the validated clock rate. Return a negative errno on
+ *		    error. Return -EOPNOTSUPP if the controller has no tuning
+ *		    capability at all. Return 0 with @read_op->max_freq left at
+ *		    zero to signal that this specific op cannot be PHY-tuned
+ *		    (e.g. a hardware erratum blocks it) but another variant may
+ *		    succeed; the caller will iterate remaining op variants.
  *
  * This interface should be implemented by SPI controllers providing an
  * high-level interface to execute SPI memory operation, which is usually the
@@ -376,6 +385,8 @@ struct spi_controller_mem_ops {
 			   unsigned long initial_delay_us,
 			   unsigned long polling_rate_us,
 			   unsigned long timeout_ms);
+	int (*execute_tuning)(struct spi_mem *mem, struct spi_mem_op *read_op,
+			      struct spi_mem_op *write_op);
 };
 
 /**
@@ -465,6 +476,9 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
 void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op);
 u64 spi_mem_calc_op_duration(struct spi_mem *mem, struct spi_mem_op *op);
 
+int spi_mem_execute_tuning(struct spi_mem *mem, struct spi_mem_op *read_op,
+			   struct spi_mem_op *write_op);
+
 bool spi_mem_supports_op(struct spi_mem *mem,
 			 const struct spi_mem_op *op);
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 04/16] spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops
From: Santhosh Kumar K @ 2026-06-18  7:37 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt, miquel.raynal, richard,
	vigneshr, pratyush, mwalle, takahiro.kuwano
  Cc: linux-spi, devicetree, linux-kernel, linux-mtd, praneeth,
	u-kumar1, a-dutta, s-k6
In-Reply-To: <20260618073725.84733-1-s-k6@ti.com>

When a device exposes both a conservative base speed (spi-max-frequency)
and a maximum post-configuration speed (spi-max-post-config-frequency),
operations validated after controller configuration must run at the
higher rate while all others are capped at the base rate.

Extend spi_mem_adjust_op_freq() with a bypass: if op->max_freq equals
post_config_max_speed_hz (the value written by execute_tuning on
success), return immediately leaving op->max_freq unchanged. All other
ops are capped to max_speed_hz, the always-reachable base rate. This
integrates the policy into the single existing frequency-adjustment
point so exec_op(), supports_op(), and calc_op_duration() all behave
consistently.

Signed-off-by: Santhosh Kumar K <s-k6@ti.com>
---
 drivers/spi/spi-mem.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index a88b9f038356..e20eca1b8245 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -591,9 +591,18 @@ EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
  * Some chips have per-op frequency limitations and must adapt the maximum
  * speed. This function allows SPI mem drivers to set @op->max_freq to the
  * maximum supported value.
+ *
+ * When @mem->spi->post_config_max_speed_hz is set, ops with @op->max_freq
+ * equal to that value are treated as post-configuration ops (e.g. PHY-tuned)
+ * and are allowed to run at the full post-config rate. All other ops are
+ * capped to @mem->spi->max_speed_hz, the always-reachable base rate.
  */
 void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op)
 {
+	if (mem->spi->post_config_max_speed_hz &&
+	    op->max_freq == mem->spi->post_config_max_speed_hz)
+		return;
+
 	if (!op->max_freq || op->max_freq > mem->spi->max_speed_hz)
 		op->max_freq = mem->spi->max_speed_hz;
 }
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 02/16] spi: dt-bindings: add spi-phy-pattern-partition property
From: Santhosh Kumar K @ 2026-06-18  7:37 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt, miquel.raynal, richard,
	vigneshr, pratyush, mwalle, takahiro.kuwano
  Cc: linux-spi, devicetree, linux-kernel, linux-mtd, praneeth,
	u-kumar1, a-dutta, s-k6
In-Reply-To: <20260618073725.84733-1-s-k6@ti.com>

Add spi-phy-pattern-partition, a per-device phandle property on the
flash sub-node that allows the DT author to directly reference the
partition holding the PHY tuning pattern. Used to locate the pattern
data during PHY tuning when the device cannot load the pattern
dynamically.

"Read PHY tuning" works by reading a known data pattern from the device
repeatedly while sweeping controller delay parameters until the
capture window is stable. For SPI NAND, the driver loads the pattern
into the page cache once using write-to-cache opcodes, then reads it
during the sweep. SPI NOR devices have no equivalent opcode, so the
pattern must be pre-programmed in a dedicated flash partition. One
partition per device is required to keep the procedure unambiguous
when multiple devices share a bus.

Signed-off-by: Santhosh Kumar K <s-k6@ti.com>
---
 .../bindings/spi/cdns,qspi-nor.yaml           | 19 +++++++++++++++++++
 .../bindings/spi/spi-peripheral-props.yaml    |  7 +++++++
 2 files changed, 26 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml
index 891f578b5ac4..c6f1b1d1251d 100644
--- a/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml
+++ b/Documentation/devicetree/bindings/spi/cdns,qspi-nor.yaml
@@ -204,10 +204,29 @@ examples:
         flash@0 {
             compatible = "jedec,spi-nor";
             reg = <0x0>;
+            #address-cells = <1>;
+            #size-cells = <1>;
             cdns,read-delay = <4>;
             cdns,tshsl-ns = <60>;
             cdns,tsd2d-ns = <60>;
             cdns,tchsh-ns = <60>;
             cdns,tslch-ns = <60>;
+            spi-phy-pattern-partition = <&phy_pattern>;
+
+            partitions {
+                compatible = "fixed-partitions";
+                #address-cells = <1>;
+                #size-cells = <1>;
+
+                partition@0 {
+                    label = "data";
+                    reg = <0x0 0x3fc0000>;
+                };
+
+                phy_pattern: partition@3fc0000 {
+                    label = "phy-pattern";
+                    reg = <0x3fc0000 0x40000>;
+                };
+            };
         };
     };
diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
index ece86f65930f..38708f8197f9 100644
--- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
@@ -123,6 +123,13 @@ properties:
     description:
       Delay, in microseconds, after a write transfer.
 
+  spi-phy-pattern-partition:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      Phandle to the flash partition holding the pre-programmed PHY tuning
+      pattern. Used when the device cannot load the pattern dynamically during
+      PHY tuning.
+
   stacked-memories:
     description: Several SPI memories can be wired in stacked mode.
       This basically means that either a device features several chip
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 01/16] spi: dt-bindings: add spi-max-post-config-frequency property
From: Santhosh Kumar K @ 2026-06-18  7:37 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt, miquel.raynal, richard,
	vigneshr, pratyush, mwalle, takahiro.kuwano
  Cc: linux-spi, devicetree, linux-kernel, linux-mtd, praneeth,
	u-kumar1, a-dutta, s-k6
In-Reply-To: <20260618073725.84733-1-s-k6@ti.com>

Add spi-max-post-config-frequency, a generic uint32 property for SPI
peripherals that support two distinct clock rates: a conservative rate
always reachable without controller configuration, and a higher rate
reachable only after controller-side configuration such as PHY tuning.

When both properties are present, spi-max-frequency gives the
conservative pre-configuration rate and spi-max-post-config-frequency
gives the higher post-configuration target.

Signed-off-by: Santhosh Kumar K <s-k6@ti.com>
---
 .../devicetree/bindings/spi/spi-peripheral-props.yaml       | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
index 880a9f624566..ece86f65930f 100644
--- a/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
+++ b/Documentation/devicetree/bindings/spi/spi-peripheral-props.yaml
@@ -45,6 +45,12 @@ properties:
     description:
       Maximum SPI clocking speed of the device in Hz.
 
+  spi-max-post-config-frequency:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description:
+      Maximum SPI clock frequency in Hz achievable post controller-side
+      configuration.
+
   spi-cs-setup-delay-ns:
     description:
       Delay in nanoseconds to be introduced by the controller after CS is
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 03/16] spi: parse spi-max-post-config-frequency into post_config_max_speed_hz
From: Santhosh Kumar K @ 2026-06-18  7:37 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt, miquel.raynal, richard,
	vigneshr, pratyush, mwalle, takahiro.kuwano
  Cc: linux-spi, devicetree, linux-kernel, linux-mtd, praneeth,
	u-kumar1, a-dutta, s-k6
In-Reply-To: <20260618073725.84733-1-s-k6@ti.com>

Add post_config_max_speed_hz to struct spi_device and parse it from
the spi-max-post-config-frequency DT property in of_spi_parse_dt().

This supports SPI devices that operate at two distinct clock rates: a
conservative rate always reachable without controller configuration,
and a higher rate achievable only after controller-side configuration
such as PHY tuning. With both properties set, spi-max-frequency gives
the conservative pre-configuration rate and post_config_max_speed_hz
carries the post-configuration target for the SPI-MEM layer.

Zero when not set, preserving existing behaviour.

Signed-off-by: Santhosh Kumar K <s-k6@ti.com>
---
 drivers/spi/spi.c       | 2 ++
 include/linux/spi/spi.h | 3 +++
 2 files changed, 5 insertions(+)

diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 76e3563c523f..36951ab47a2f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -2599,6 +2599,8 @@ static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi,
 	/* Device speed */
 	if (!of_property_read_u32(nc, "spi-max-frequency", &value))
 		spi->max_speed_hz = value;
+	if (!of_property_read_u32(nc, "spi-max-post-config-frequency", &value))
+		spi->post_config_max_speed_hz = value;
 
 	/* Device CS delays */
 	of_spi_parse_dt_cs_delay(nc, &spi->cs_setup, "spi-cs-setup-delay-ns");
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index f6ed93eff00b..2d90ec91450a 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -139,6 +139,8 @@ extern void spi_transfer_cs_change_delay_exec(struct spi_message *msg,
  * @max_speed_hz: Maximum clock rate to be used with this chip
  *	(on this board); may be changed by the device's driver.
  *	The spi_transfer.speed_hz can override this for each transfer.
+ * @post_config_max_speed_hz: Maximum clock rate achievable after controller
+ *	configuration (e.g. PHY tuning); zero when not assigned.
  * @bits_per_word: Data transfers involve one or more words; word sizes
  *	like eight or 12 bits are common.  In-memory wordsizes are
  *	powers of two bytes (e.g. 20 bit samples use 32 bits).
@@ -191,6 +193,7 @@ struct spi_device {
 	struct device		dev;
 	struct spi_controller	*controller;
 	u32			max_speed_hz;
+	u32			post_config_max_speed_hz;
 	u8			bits_per_word;
 	bool			rt;
 #define SPI_NO_TX		BIT(31)		/* No transmit wire */
-- 
2.34.1


^ permalink raw reply related

* [PATCH v4 00/16]  spi: cadence-quadspi: add PHY tuning support
From: Santhosh Kumar K @ 2026-06-18  7:37 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt, miquel.raynal, richard,
	vigneshr, pratyush, mwalle, takahiro.kuwano
  Cc: linux-spi, devicetree, linux-kernel, linux-mtd, praneeth,
	u-kumar1, a-dutta, s-k6

This series implements PHY tuning support for the Cadence QSPI controller
to enable reliable high-speed operations. Without PHY tuning, controllers
use conservative timing that limits performance. PHY tuning calibrates
RX/TX delay lines to find optimal data capture timing windows, enabling
operation up to the controller's maximum frequency.

Background:
High-speed SPI memory controllers require precise timing calibration for
reliable operation. At higher frequencies, board-to-board variations make
fixed timing parameters inadequate. The Cadence QSPI controller includes
a PHY interface with programmable delay lines (0-127 taps) for RX and TX
paths, but these require runtime calibration to find the valid timing
window.

Approach:
Add SDR/DDR PHY tuning algorithms for the Cadence controller:

SDR Mode Tuning (1D search):
 - Searches for two consecutive valid RX delay windows
 - Selects the larger window and uses its midpoint for maximum margin
 - TX delay fixed at maximum (127) as it's less critical in SDR

DDR Mode Tuning (2D search):
 - Finds RX boundaries (rxlow/rxhigh) using TX window sweeps
 - Finds TX boundaries (txlow/txhigh) at fixed RX positions
 - Defines valid region corners and detects gaps via binary search
 - Applies temperature compensation for optimal point selection
 - Handles single or dual passing regions with different strategies

Patch description:
Infrastructure (1-5):
 - Patch 1:   Add spi-max-post-config-frequency to describe maximum
              frequency achievable post controller configuration
 - Patch 2:   Add spi-phy-pattern-partition phandle for
              NOR flash PHY tuning pattern location
 - Patch 3:   Parse spi-max-post-config-frequency in spi.c; adds
              spi_device.post_config_max_speed_hz (0 when not set
              keeping all existing DT fully compatible)
 - Patch 4:   Extend spi_mem_adjust_op_freq() with a bypass: if
              op->max_freq equals post_config_max_speed_hz, return
              immediately leaving op->max_freq unchanged. All other
              ops are capped to max_speed_hz
 - Patch 5:   Add execute_tuning callback to spi_controller_mem_ops and
              spi_mem_execute_tuning() wrapper in SPI-MEM core

Cadence QSPI Implementation (6-12):
 - Patch 6:   Move cqspi_readdata_capture() earlier (preparatory)
 - Patch 7:   Add DQS bit to cqspi_readdata_capture() (preparatory)
 - Patch 8:   Add complete PHY tuning support: DLL management, pattern
              verification (NOR via spi-phy-pattern-partition phandle,
              NAND via write-to-cache), SDR 1D and DDR 2D search
              algorithms with temperature compensation, AM654-specific
              execute_tuning entry point;
 - Patch 9:   Reject 2-byte-address DDR operations via a new
              CQSPI_NO_2BYTE_ADDR_PHY_DDR quirk flag to work around
              AM654 OSPI erratum i2383
 - Patch 10:  Refactor direct read path for PHY support (preparatory)
 - Patch 11:  Enable PHY for direct reads, split the transfer into an
              unaligned head, a 16-byte-aligned middle section with PHY
              active, and an unaligned tail
 - Patch 12:  Enable PHY for indirect writes of at least                        
              CQSPI_PHY_MIN_INDIRECT_WRITE_LEN bytes

MTD core (13-16):
 - Patch 13:  Extract spinand_select_op_variant() into a shared helper
              spinand_op_find_best() with a skip_mask
 - Patch 14:  Negotiate optimal PHY operating point before dirmap
              creation
 - Patch 15:  Extract spi_nor_spimem_get_read_op() helper (preparatory)
 - Patch 16:  Execute PHY tuning in spi_nor_probe() before creating
              dirmaps

Testing:
This series was tested on TI's
AM62Ax SK with OSPI NAND flash and
AM62Px SK with OSPI NOR flash:

Read throughput:
|----------------------------------------|
|                | non-PHY   | PHY       |
|----------------------------------------|
| OSPI NOR (8D)  | 37.5 MB/s | 216 MB/s  |
|----------------------------------------|
| OSPI NAND (8S) | 9.2 MB/s  | 35.1 MB/s |
|----------------------------------------|

Write throughput:
|----------------------------------------|
|                | non-PHY   | PHY       |
|----------------------------------------|
| OSPI NAND (8S) | 6 MB/s    | 9.2 MB/s  |
|----------------------------------------|

Test log: https://gist.github.com/santhosh21/fe98754e52970287eb9011154100b62d
Repo: https://github.com/santhosh21/linux/commits/phy_tuning_v4/

Changes in v4:
 - Add spi-max-post-config-frequency instead of extending spi-max-frequency
   to accept an optional second value
 - Replace spi_mem_apply_base_freq_cap() with spi_mem_adjust_op_freq() extension
 - For SPI NOR/NAND, execute PHY tuning before the dirmap creation
 - For SPI NAND, execute PHY tuning across all operation variants available,
   perform duration comparison, and select the best resulting variant
   by taking controller-specific restrictions into account
 - Move i2383 check from cqspi_supports_mem_op() to cqspi_am654_ospi_execute_tuning()
 - Rename cdns,phy-pattern-partition to spi-phy-pattern-partition,
   cqspi_phy_enable to cqspi_tune_phy and f_pdata->use_phy to use_tuned_phy
 - Remove redundant spi-max-frequency parsing in driver cqspi_of_get_flash_pdata()
 - Extract DMA refactoring into a preparatory patch
 - Rebase on v7.1
 - Collect tags from Miquel
 - Link to v3: https://lore.kernel.org/linux-spi/20260527175527.2247679-1-s-k6@ti.com/

Changes in v3:
 - Drop spi-has-dqs DT property; DQS is now enabled automatically when
   the selected read operation uses DDR signalling (dtr flags in the op)
 - Extend spi-max-frequency to accept an optional second value forming a
   [base-freq, max-freq] pair; the presence of two values signals PHY
   tuning intent and encodes both the conservative base speed and the
   calibration target in one property
 - Add base_speed_hz to struct spi_device (spi.c/spi.h) and parse the
   two-element array there; single-value DT is fully backward-compatible
 - Move frequency enforcement from the cadence driver to core: new
   spi_mem_apply_base_freq_cap() called from spi_mem_exec_op() replaces
   the per-driver cqspi_op_matches_tuned() and non_phy_clk_rate field
 - Propagate the tuned max_freq to dirmap op templates after
   execute_tuning() succeeds; store persistent op templates in
   spi_nor.max_read_op and spinand.{max_read,max_write}_op so the
   frequency writeback survives across the probe call
 - Replace NOR pattern partition lookup by name with a
   cdns,phy-pattern-partition DT phandle pointing directly to the
   partition node
 - Add CQSPI_NO_2BYTE_ADDR_PHY_DDR quirk and reject 2-byte-address DDR
   ops in cqspi_supports_mem_op() to work around AM654 erratum i2383
 - Remove RFC tag
 - Rebase on v7.1-rc5
 - Collect tags from Miquel
 - Link to v2: https://lore.kernel.org/linux-spi/20260113141617.1905039-1-s-k6@ti.com/

Changes in v2:
 - Restructure the .execute_tuning() call from spi-mem clients instead
   of mtdcore with best read_op and write_op (optional) passed
 - Add compatible-specific .execute_tuning() call which can be called by
   spi_mem_execute_tuning() if exists
 - Handle tuning requirement check by controller instead of spi-mem
   clients
 - Add support to write the phy_pattern to cache if relevant write_op
   is passed or get the partition offset which contains the phy_pattern
 - Add tuning algorithm for DDR mode
 - Add support for DQS
 - Restrict PHY frequency to tuned operations
 - Link to v1: https://lore.kernel.org/linux-spi/20250811193219.731851-1-s-k6@ti.com/

Signed-off-by: Santhosh Kumar K <s-k6@ti.com>

Pratyush Yadav (1):
  mtd: spi-nor: extract read op template construction into helper

Santhosh Kumar K (15):
  spi: dt-bindings: add spi-max-post-config-frequency property
  spi: dt-bindings: add spi-phy-pattern-partition property
  spi: parse spi-max-post-config-frequency into post_config_max_speed_hz
  spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops
  spi: spi-mem: add execute_tuning callback and spi_mem_execute_tuning()
  spi: cadence-quadspi: move cqspi_readdata_capture earlier
  spi: cadence-quadspi: add DQS support to read data capture
  spi: cadence-quadspi: add PHY tuning support
  spi: cadence-quadspi: skip DDR PHY tuning for 2-byte-address ops
    (i2383)
  spi: cadence-quadspi: refactor direct read path for PHY support
  spi: cadence-quadspi: enable PHY for direct reads
  spi: cadence-quadspi: enable PHY for indirect writes
  mtd: spinand: extract variant ranking logic into
    spinand_op_find_best()
  mtd: spinand: negotiate optimal PHY operating point before dirmap
    creation
  mtd: spi-nor: run PHY tuning after init and update dirmap frequency

 .../bindings/spi/cdns,qspi-nor.yaml           |   19 +
 .../bindings/spi/spi-peripheral-props.yaml    |   13 +
 drivers/mtd/nand/spi/core.c                   |  246 +-
 drivers/mtd/spi-nor/core.c                    |   80 +-
 drivers/spi/spi-cadence-quadspi.c             | 2265 +++++++++++++++--
 drivers/spi/spi-mem.c                         |   40 +
 drivers/spi/spi.c                             |    2 +
 include/linux/mtd/spi-nor.h                   |    3 +
 include/linux/mtd/spinand.h                   |   11 +
 include/linux/spi/spi-mem.h                   |   14 +
 include/linux/spi/spi.h                       |    3 +
 11 files changed, 2493 insertions(+), 203 deletions(-)

-- 
2.34.1

^ permalink raw reply

* Re: [PATCH v3 2/2] drm/tiny: add support for PIXPAPER 4.26 monochrome e-ink panel
From: Thomas Zimmermann @ 2026-06-18  7:30 UTC (permalink / raw)
  To: LiangCheng Wang, Devarsh Thakkar, Maarten Lankhorst,
	Maxime Ripard, David Airlie, Simona Vetter, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Wig Cheng
  Cc: dri-devel, devicetree, linux-kernel, Tomi Valkeinen
In-Reply-To: <20260618023338.26630-1-zaq14760@gmail.com>

Hi

Am 18.06.26 um 04:33 schrieb LiangCheng Wang:
> Hi Thomas,
>
> Thanks for the review, and no worries about the timing.
>
> Before I spin a v4 for these comments, I'd like to confirm the overall
> direction, since it affects whether this should remain a standalone driver
> at all.
>
> In parallel, Devarsh Thakkar is adding a generic Solomon SSD16xx e-paper
> driver (panel-ssd16xx.c, currently v1 in review). The PIXPAPER 4.26 uses an
> SSD1677, which is part of that family; Devarsh has said he will add SSD1677
> support in the next revision (v2) of his series, after which this panel
> could be supported there as a panel entry rather than as a separate driver.
> That work isn't posted yet, but I had agreed that consolidating under
> panel-ssd16xx.c is the better long-term direction.
>
> I'd appreciate your guidance on how to proceed -- whether it is better to
> keep iterating on this standalone driver, or to hold it and add the
> PIXPAPER 4.26 panel to panel-ssd16xx.c once that driver supports SSD1677.
> I'm happy to go whichever way you prefer.

I see. It is usually preferable to have only a single driver for a 
controller. If there are specific features of the Pixpaper 4.26, they 
should be added to the ssd16xx driver if possible.

There are exceptions from this rule, of course. If the Pixpaper turns 
out to be significantly different, we can always reconsider.

Best regards
Thomas

>
> Regards,
> LiangCheng

-- 
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)



^ permalink raw reply

* Re: [PATCH v7 4/4] arm64: tegra: Reorder reg and reg-names to match bindings
From: Manivannan Sadhasivam @ 2026-06-18  7:27 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Bjorn Helgaas, Lorenzo Pieralisi, Krzysztof Wilczyński,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Karthikeyan Mitran, Hou Zhiqiang,
	Thomas Petazzoni, Pali Rohár, Michal Simek, Kevin Xie,
	Aksh Garg, linux-pci, devicetree, linux-tegra, linux-kernel,
	linux-arm-kernel, Thierry Reding
In-Reply-To: <20260617-tegra264-pcie-v7-4-eae7ae964629@nvidia.com>

On Wed, Jun 17, 2026 at 06:01:31PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> The ECAM region cannot be the first entry in the "reg" property, because
> in that case the unit-address wouldn't match the first entry. The order
> of the nodes can also not be changed to match the ECAM entry because the
> ECAM region is global and outside of any of the control busses.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>

Acked-by: Manivannan Sadhasivam <mani@kernel.org>

- Mani

> ---
> Changes in v5:
> - rebase onto v7.1-rc1
> 
> Changes in v4:
> - revert ECAM "reg" entry order
> 
> Changes in v2:
> - order ECAM "reg" entry before others
> ---
>  arch/arm64/boot/dts/nvidia/tegra264.dtsi | 48 ++++++++++++++++----------------
>  1 file changed, 24 insertions(+), 24 deletions(-)
> 
> diff --git a/arch/arm64/boot/dts/nvidia/tegra264.dtsi b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> index 8f4350c7793b..4c701abd25a8 100644
> --- a/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> +++ b/arch/arm64/boot/dts/nvidia/tegra264.dtsi
> @@ -3513,11 +3513,11 @@ cmdqv4: cmdqv@b200000 {
>  
>  		pci@c000000 {
>  			compatible = "nvidia,tegra264-pcie";
> -			reg = <0xd0 0xb0000000 0x0 0x10000000>,
> -			      <0x00 0x0c000000 0x0 0x00004000>,
> +			reg = <0x00 0x0c000000 0x0 0x00004000>,
>  			      <0x00 0x0c004000 0x0 0x00001000>,
> -			      <0x00 0x0c005000 0x0 0x00001000>;
> -			reg-names = "ecam", "xal", "xtl", "xtl-pri";
> +			      <0x00 0x0c005000 0x0 0x00001000>,
> +			      <0xd0 0xb0000000 0x0 0x10000000>;
> +			reg-names = "xal", "xtl", "xtl-pri", "ecam";
>  			#address-cells = <3>;
>  			#size-cells = <2>;
>  			device_type = "pci";
> @@ -3893,12 +3893,12 @@ gpio_uphy: gpio@8300000 {
>  
>  		pci@8400000 {
>  			compatible = "nvidia,tegra264-pcie";
> -			reg = <0xa8 0xb0000000 0x0 0x10000000>,
> -			      <0x00 0x08400000 0x0 0x00004000>,
> +			reg = <0x00 0x08400000 0x0 0x00004000>,
>  			      <0x00 0x08404000 0x0 0x00001000>,
>  			      <0x00 0x08405000 0x0 0x00001000>,
> -			      <0x00 0x08410000 0x0 0x00010000>;
> -			reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl";
> +			      <0x00 0x08410000 0x0 0x00010000>,
> +			      <0xa8 0xb0000000 0x0 0x10000000>;
> +			reg-names = "xal", "xtl", "xtl-pri", "xpl", "ecam";
>  			#address-cells = <3>;
>  			#size-cells = <2>;
>  			device_type = "pci";
> @@ -3925,12 +3925,12 @@ pci@8400000 {
>  
>  		pci@8420000 {
>  			compatible = "nvidia,tegra264-pcie";
> -			reg = <0xb0 0xb0000000 0x0 0x10000000>,
> -			      <0x00 0x08420000 0x0 0x00004000>,
> +			reg = <0x00 0x08420000 0x0 0x00004000>,
>  			      <0x00 0x08424000 0x0 0x00001000>,
>  			      <0x00 0x08425000 0x0 0x00001000>,
> -			      <0x00 0x08430000 0x0 0x00010000>;
> -			reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl";
> +			      <0x00 0x08430000 0x0 0x00010000>,
> +			      <0xb0 0xb0000000 0x0 0x10000000>;
> +			reg-names = "xal", "xtl", "xtl-pri", "xpl", "ecam";
>  			#address-cells = <3>;
>  			#size-cells = <2>;
>  			device_type = "pci";
> @@ -3957,12 +3957,12 @@ pci@8420000 {
>  
>  		pci@8440000 {
>  			compatible = "nvidia,tegra264-pcie";
> -			reg = <0xb8 0xb0000000 0x0 0x10000000>,
> -			      <0x00 0x08440000 0x0 0x00004000>,
> +			reg = <0x00 0x08440000 0x0 0x00004000>,
>  			      <0x00 0x08444000 0x0 0x00001000>,
>  			      <0x00 0x08445000 0x0 0x00001000>,
> -			      <0x00 0x08450000 0x0 0x00010000>;
> -			reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl";
> +			      <0x00 0x08450000 0x0 0x00010000>,
> +			      <0xb8 0xb0000000 0x0 0x10000000>;
> +			reg-names = "xal", "xtl", "xtl-pri", "xpl", "ecam";
>  			#address-cells = <3>;
>  			#size-cells = <2>;
>  			device_type = "pci";
> @@ -3989,12 +3989,12 @@ pci@8440000 {
>  
>  		pci@8460000 {
>  			compatible = "nvidia,tegra264-pcie";
> -			reg = <0xc0 0xb0000000 0x0 0x10000000>,
> -			      <0x00 0x08460000 0x0 0x00004000>,
> +			reg = <0x00 0x08460000 0x0 0x00004000>,
>  			      <0x00 0x08464000 0x0 0x00001000>,
>  			      <0x00 0x08465000 0x0 0x00001000>,
> -			      <0x00 0x08470000 0x0 0x00010000>;
> -			reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl";
> +			      <0x00 0x08470000 0x0 0x00010000>,
> +			      <0xc0 0xb0000000 0x0 0x10000000>;
> +			reg-names = "xal", "xtl", "xtl-pri", "xpl", "ecam";
>  			#address-cells = <3>;
>  			#size-cells = <2>;
>  			device_type = "pci";
> @@ -4021,12 +4021,12 @@ pci@8460000 {
>  
>  		pci@8480000 {
>  			compatible = "nvidia,tegra264-pcie";
> -			reg = <0xc8 0xb0000000 0x0 0x10000000>,
> -			      <0x00 0x08480000 0x0 0x00004000>,
> +			reg = <0x00 0x08480000 0x0 0x00004000>,
>  			      <0x00 0x08484000 0x0 0x00001000>,
>  			      <0x00 0x08485000 0x0 0x00001000>,
> -			      <0x00 0x08490000 0x0 0x00010000>;
> -			reg-names = "ecam", "xal", "xtl", "xtl-pri", "xpl";
> +			      <0x00 0x08490000 0x0 0x00010000>,
> +			      <0xc8 0xb0000000 0x0 0x10000000>;
> +			reg-names = "xal", "xtl", "xtl-pri", "xpl", "ecam";
>  			#address-cells = <3>;
>  			#size-cells = <2>;
>  			device_type = "pci";
> 
> -- 
> 2.54.0
> 

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply

* Re: [PATCH v7 3/4] PCI: tegra: Add Tegra264 support
From: Manivannan Sadhasivam @ 2026-06-18  7:26 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Bjorn Helgaas, Lorenzo Pieralisi, Krzysztof Wilczyński,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Thierry Reding,
	Jonathan Hunter, Karthikeyan Mitran, Hou Zhiqiang,
	Thomas Petazzoni, Pali Rohár, Michal Simek, Kevin Xie,
	Aksh Garg, linux-pci, devicetree, linux-tegra, linux-kernel,
	linux-arm-kernel, Thierry Reding, Manikanta Maddireddy
In-Reply-To: <20260617-tegra264-pcie-v7-3-eae7ae964629@nvidia.com>

On Wed, Jun 17, 2026 at 06:01:30PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> Add a driver for the PCIe controller found on NVIDIA Tegra264 SoCs. The
> driver is very small, with its main purpose being to set up the address
> translation registers and then creating a standard PCI host using ECAM.
> 
> Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v7:
> - select PCI_ECAM to satisfy the build dependency (Jonathan Hunter)
> - remove pre-silicon support patch to avoid extra build dependency
> 
> Changes in v6:
> - remove unneeded pm_runtime_disable() call (Sashiko)
> - do not use noirq suspend/resume callbacks (Sashiko)
> - wrap PM ops in pm_ptr() macro (Sashiko)
> - use standard wait times with msleep() (Lukas Wunner)
> - properly check errors for wake IRQs
> - fix build failures /o\
> 
> Changes in v5:
> - make PCIE_TEGRA264 symbol tristate
> - drop dependency on PCI_MSI
> - reorganize tegra264_pcie struct
> - use standard wake-gpios property
> - rename tegra264_pcie_bpmp_set_rp_state() to tegra264_pcie_power_off()
> - use dev_err() instead of dev_info() for some error messages
> - add clarifying comment as to why bandwidth requests aren't fatal
> - address some compiler warnings on 32-bit physical address platforms
> - drop needless comments
> - explicitly deinitialize controller on suspend
> - use devm_pm_runtime_active_enabled()
> - rename "free" label to "free_ecam"
> - use dev_err_probe() in more places
> - reselect default pin state during resume, not probe
> - return early on absence of wake GPIO
> - simplify BW value calculation
> 
> Changes in v2:
> - specify generations applicable for PCI_TEGRA driver to avoid confusion
> - drop SPDX-FileCopyrightText tag
> - rename link_state to link_up to clarify meaning
> - replace memset() by an empty initializer
> - sanity-check only enable BAR regions
> - bring PCI link out of reset in case firmware didn't
> - use common wait times instead of defining our own
> - use core helpers to parse and print PCI link speed
> - fix multi-line comment
> - use dev_err_probe() more ubiquitously
> - fix probe sequence and error cleanup
> - use DEFINE_NOIRQ_DEV_PM_OPS() to avoid warnings for !PM_SUSPEND
> - reuse more standard registers and remove unused register definitions
> - use %pe and ERR_PTR() to print symbolic errors
> - add signed-off-by from Manikanta as the original author
> - add myself as author after significantly modifying the driver
> 
> pcie: remove pre-silicon conditionals
> ---
>  drivers/pci/controller/Kconfig         |  10 +-
>  drivers/pci/controller/Makefile        |   1 +
>  drivers/pci/controller/pcie-tegra264.c | 538 +++++++++++++++++++++++++++++++++
>  3 files changed, 548 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig
> index 2247709ef6d6..3045c8aecc7e 100644
> --- a/drivers/pci/controller/Kconfig
> +++ b/drivers/pci/controller/Kconfig
> @@ -255,7 +255,15 @@ config PCI_TEGRA
>  	select IRQ_MSI_LIB
>  	help
>  	  Say Y here if you want support for the PCIe host controller found
> -	  on NVIDIA Tegra SoCs.
> +	  on NVIDIA Tegra SoCs (Tegra20 through Tegra186).
> +
> +config PCIE_TEGRA264
> +	tristate "NVIDIA Tegra264 PCIe controller"
> +	depends on ARCH_TEGRA || COMPILE_TEST
> +	select PCI_ECAM
> +	help
> +	  Say Y here if you want support for the PCIe host controller found
> +	  on NVIDIA Tegra264 SoCs.
>  
>  config PCIE_RCAR_HOST
>  	bool "Renesas R-Car PCIe controller (host mode)"
> diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile
> index ac8db283f0fe..d478743b5142 100644
> --- a/drivers/pci/controller/Makefile
> +++ b/drivers/pci/controller/Makefile
> @@ -7,6 +7,7 @@ obj-$(CONFIG_PCI_HYPERV_INTERFACE) += pci-hyperv-intf.o
>  obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
>  obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
>  obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
> +obj-$(CONFIG_PCIE_TEGRA264) += pcie-tegra264.o
>  obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
>  obj-$(CONFIG_PCIE_RCAR_HOST) += pcie-rcar.o pcie-rcar-host.o
>  obj-$(CONFIG_PCIE_RCAR_EP) += pcie-rcar.o pcie-rcar-ep.o
> diff --git a/drivers/pci/controller/pcie-tegra264.c b/drivers/pci/controller/pcie-tegra264.c
> new file mode 100644
> index 000000000000..e2d295ea4403
> --- /dev/null
> +++ b/drivers/pci/controller/pcie-tegra264.c
> @@ -0,0 +1,538 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * PCIe host controller driver for Tegra264 SoC
> + *
> + * Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/init.h>
> +#include <linux/interconnect.h>
> +#include <linux/interrupt.h>
> +#include <linux/iopoll.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/of.h>
> +#include <linux/of_pci.h>
> +#include <linux/of_platform.h>
> +#include <linux/pci-ecam.h>
> +#include <linux/pci.h>
> +#include <linux/pinctrl/consumer.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/pm_wakeirq.h>
> +
> +#include <soc/tegra/bpmp.h>
> +#include <soc/tegra/bpmp-abi.h>
> +#include <soc/tegra/fuse.h>
> +
> +#include "../pci.h"
> +
> +/* XAL registers */
> +#define XAL_RC_ECAM_BASE_HI			0x00
> +#define XAL_RC_ECAM_BASE_LO			0x04
> +#define XAL_RC_ECAM_BUSMASK			0x08
> +#define XAL_RC_IO_BASE_HI			0x0c
> +#define XAL_RC_IO_BASE_LO			0x10
> +#define XAL_RC_IO_LIMIT_HI			0x14
> +#define XAL_RC_IO_LIMIT_LO			0x18
> +#define XAL_RC_MEM_32BIT_BASE_HI		0x1c
> +#define XAL_RC_MEM_32BIT_BASE_LO		0x20
> +#define XAL_RC_MEM_32BIT_LIMIT_HI		0x24
> +#define XAL_RC_MEM_32BIT_LIMIT_LO		0x28
> +#define XAL_RC_MEM_64BIT_BASE_HI		0x2c
> +#define XAL_RC_MEM_64BIT_BASE_LO		0x30
> +#define XAL_RC_MEM_64BIT_LIMIT_HI		0x34
> +#define XAL_RC_MEM_64BIT_LIMIT_LO		0x38
> +#define XAL_RC_BAR_CNTL_STANDARD		0x40
> +#define XAL_RC_BAR_CNTL_STANDARD_IOBAR_EN	BIT(0)
> +#define XAL_RC_BAR_CNTL_STANDARD_32B_BAR_EN	BIT(1)
> +#define XAL_RC_BAR_CNTL_STANDARD_64B_BAR_EN	BIT(2)
> +
> +/* XTL registers */
> +#define XTL_RC_PCIE_CFG_LINK_STATUS		0x5a
> +
> +#define XTL_RC_MGMT_PERST_CONTROL		0x218
> +#define XTL_RC_MGMT_PERST_CONTROL_PERST_O_N	BIT(0)
> +
> +#define XTL_RC_MGMT_CLOCK_CONTROL		0x47c
> +#define XTL_RC_MGMT_CLOCK_CONTROL_PEX_CLKREQ_I_N_PIN_USE_CONV_TO_PRSNT	BIT(9)
> +
> +struct tegra264_pcie {
> +	struct device *dev;
> +
> +	/* I/O memory */
> +	void __iomem *xal;
> +	void __iomem *xtl;
> +	void __iomem *ecam;
> +
> +	/* bridge configuration */
> +	struct pci_config_window *cfg;
> +	struct pci_host_bridge *bridge;
> +
> +	/* wake IRQ */
> +	struct gpio_desc *wake_gpio;
> +	unsigned int wake_irq;
> +
> +	/* BPMP and bandwidth management */
> +	struct icc_path *icc_path;
> +	struct tegra_bpmp *bpmp;
> +	u32 ctl_id;
> +
> +	bool link_up;
> +};
> +
> +static int tegra264_pcie_parse_dt(struct tegra264_pcie *pcie)
> +{
> +	struct device *dev = pcie->dev;
> +	int err;
> +
> +	pcie->wake_gpio = devm_gpiod_get_optional(dev, "wake", GPIOD_IN);
> +	if (IS_ERR(pcie->wake_gpio))
> +		return PTR_ERR(pcie->wake_gpio);
> +
> +	if (!pcie->wake_gpio)
> +		return 0;
> +
> +	err = gpiod_to_irq(pcie->wake_gpio);
> +	if (err < 0)
> +		return dev_err_probe(dev, err, "failed to get wake IRQ\n");
> +
> +	pcie->wake_irq = (unsigned int)err;
> +
> +	err = devm_device_init_wakeup(dev);
> +	if (err < 0)
> +		return dev_err_probe(dev, err, "failed to initialize wakeup\n");
> +
> +	err = devm_pm_set_wake_irq(dev, pcie->wake_irq);
> +	if (err < 0)
> +		return dev_err_probe(dev, err, "failed to set wakeup IRQ\n");
> +

I'd really like to get rid of custom WAKE# handling in the controller drivers.
Krishna is trying to add generic WAKE# handling in the PCI core and I'd suggest
you to take a look at the patches:
https://lore.kernel.org/linux-pci/20260511-wakeirq_support-v10-2-c10af9c9eb8c@oss.qualcomm.com/

But this also means that you need to use switch to Root Port binding to move the
Root Port properties out of the controller node. This is something we are
mandating for the new controllers. Not a big change though...

Reference:

Documentation/devicetree/bindings/pci/spacemit,k1-pcie-host.yaml#n80

> +	return 0;
> +}
> +
> +static void tegra264_pcie_power_off(struct tegra264_pcie *pcie)
> +{
> +	struct tegra_bpmp_message msg = {};
> +	struct mrq_pcie_request req = {};
> +	int err;
> +
> +	req.cmd = CMD_PCIE_RP_CONTROLLER_OFF;
> +	req.rp_ctrlr_off.rp_controller = pcie->ctl_id;
> +
> +	msg.mrq = MRQ_PCIE;
> +	msg.tx.data = &req;
> +	msg.tx.size = sizeof(req);
> +
> +	err = tegra_bpmp_transfer(pcie->bpmp, &msg);
> +	if (err)
> +		dev_err(pcie->dev, "failed to turn off PCIe #%u: %pe\n",
> +			pcie->ctl_id, ERR_PTR(err));
> +
> +	if (msg.rx.ret)
> +		dev_err(pcie->dev, "failed to turn off PCIe #%u: %d\n",
> +			pcie->ctl_id, msg.rx.ret);
> +}
> +
> +static void tegra264_pcie_icc_set(struct tegra264_pcie *pcie)
> +{
> +	u32 value, speed, width;
> +	int err;
> +
> +	value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
> +	speed = FIELD_GET(PCI_EXP_LNKSTA_CLS, value);
> +	width = FIELD_GET(PCI_EXP_LNKSTA_NLW, value);
> +
> +	value = Mbps_to_icc(width * PCIE_SPEED2MBS_ENC(pcie_link_speed[speed]));
> +
> +	/*
> +	 * We don't want to error out here because a boot-critical device
> +	 * could be connected to this root port. Failure to set the bandwidth
> +	 * request may have an adverse impact on performance, but it is not
> +	 * generally fatal, so we opt to continue regardless so that users
> +	 * get a chance to fix things.
> +	 */
> +	err = icc_set_bw(pcie->icc_path, value, value);
> +	if (err < 0)
> +		dev_err(pcie->dev,
> +			"failed to request bandwidth (%u MBps): %pe\n",
> +			value, ERR_PTR(err));
> +}
> +
> +/*
> + * The various memory regions used by the controller (I/O, memory, ECAM) are
> + * set up during early boot and have hardware-level protections in place. If
> + * the DT ranges don't match what's been setup, the controller won't be able
> + * to write the address endpoints properly, so make sure to validate that DT
> + * and firmware programming agree on these ranges.
> + */
> +static bool tegra264_pcie_check_ranges(struct platform_device *pdev)
> +{
> +	struct tegra264_pcie *pcie = platform_get_drvdata(pdev);
> +	struct device_node *np = pcie->dev->of_node;
> +	struct of_pci_range_parser parser;
> +	phys_addr_t phys, limit, hi, lo;
> +	struct of_pci_range range;
> +	struct resource *res;
> +	bool status = true;
> +	u32 value;
> +	int err;
> +
> +	err = of_pci_range_parser_init(&parser, np);
> +	if (err < 0)
> +		return false;
> +
> +	for_each_of_pci_range(&parser, &range) {
> +		unsigned int addr_hi, addr_lo, limit_hi, limit_lo, enable;
> +		unsigned long type = range.flags & IORESOURCE_TYPE_BITS;
> +		phys_addr_t start, end, mask;
> +		const char *region = NULL;
> +
> +		end = range.cpu_addr + range.size - 1;
> +		start = range.cpu_addr;
> +
> +		switch (type) {
> +		case IORESOURCE_IO:
> +			addr_hi = XAL_RC_IO_BASE_HI;
> +			addr_lo = XAL_RC_IO_BASE_LO;
> +			limit_hi = XAL_RC_IO_LIMIT_HI;
> +			limit_lo = XAL_RC_IO_LIMIT_LO;
> +			enable = XAL_RC_BAR_CNTL_STANDARD_IOBAR_EN;
> +			mask = SZ_64K - 1;
> +			region = "I/O";
> +			break;
> +
> +		case IORESOURCE_MEM:
> +			if (range.flags & IORESOURCE_PREFETCH) {
> +				addr_hi = XAL_RC_MEM_64BIT_BASE_HI;
> +				addr_lo = XAL_RC_MEM_64BIT_BASE_LO;
> +				limit_hi = XAL_RC_MEM_64BIT_LIMIT_HI;
> +				limit_lo = XAL_RC_MEM_64BIT_LIMIT_LO;
> +				enable = XAL_RC_BAR_CNTL_STANDARD_64B_BAR_EN;
> +				region = "prefetchable memory";
> +			} else {
> +				addr_hi = XAL_RC_MEM_32BIT_BASE_HI;
> +				addr_lo = XAL_RC_MEM_32BIT_BASE_LO;
> +				limit_hi = XAL_RC_MEM_32BIT_LIMIT_HI;
> +				limit_lo = XAL_RC_MEM_32BIT_LIMIT_LO;
> +				enable = XAL_RC_BAR_CNTL_STANDARD_32B_BAR_EN;
> +				region = "memory";
> +			}
> +
> +			mask = SZ_1M - 1;
> +			break;
> +		}
> +
> +		/* not interested in anything that's not I/O or memory */
> +		if (!region)
> +			continue;
> +
> +		/* don't check regions that haven't been enabled */
> +		value = readl(pcie->xal + XAL_RC_BAR_CNTL_STANDARD);
> +		if ((value & enable) == 0)
> +			continue;
> +
> +		hi = readl(pcie->xal + addr_hi);
> +		lo = readl(pcie->xal + addr_lo);
> +		phys = ((hi << 16) << 16) | lo;
> +
> +		hi = readl(pcie->xal + limit_hi);
> +		lo = readl(pcie->xal + limit_lo);
> +		limit = ((hi << 16) << 16) | lo | mask;
> +
> +		if (phys != start || limit != end) {
> +			dev_err(pcie->dev,
> +				"%s region mismatch: %pap-%pap -> %pap-%pap\n",
> +				region, &phys, &limit, &start, &end);
> +			status = false;
> +		}
> +	}
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecam");
> +	if (!res)
> +		return false;
> +
> +	hi = readl(pcie->xal + XAL_RC_ECAM_BASE_HI);
> +	lo = readl(pcie->xal + XAL_RC_ECAM_BASE_LO);
> +	phys = ((hi << 16) << 16) | lo;
> +
> +	value = readl(pcie->xal + XAL_RC_ECAM_BUSMASK);
> +	limit = phys + ((value + 1) << 20) - 1;
> +
> +	if (phys != res->start || limit != res->end) {
> +		dev_err(pcie->dev,
> +			"ECAM region mismatch: %pap-%pap -> %pap-%pap\n",
> +			&phys, &limit, &res->start, &res->end);
> +		status = false;
> +	}
> +
> +	return status;
> +}
> +
> +static bool tegra264_pcie_link_up(struct tegra264_pcie *pcie,
> +				  enum pci_bus_speed *speed)
> +{
> +	u16 value = readw(pcie->ecam + XTL_RC_PCIE_CFG_LINK_STATUS);
> +
> +	if (value & PCI_EXP_LNKSTA_DLLLA) {
> +		if (speed)
> +			*speed = pcie_link_speed[FIELD_GET(PCI_EXP_LNKSTA_CLS,
> +							   value)];
> +
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static void tegra264_pcie_init(struct tegra264_pcie *pcie)
> +{
> +	enum pci_bus_speed speed;
> +	unsigned int i;
> +	u32 value;
> +
> +	/* bring the endpoint out of reset */
> +	value = readl(pcie->xtl + XTL_RC_MGMT_PERST_CONTROL);
> +	value |= XTL_RC_MGMT_PERST_CONTROL_PERST_O_N;
> +	writel(value, pcie->xtl + XTL_RC_MGMT_PERST_CONTROL);
> +
> +	for (i = 0; i < PCIE_LINK_WAIT_MAX_RETRIES; i++) {
> +		if (tegra264_pcie_link_up(pcie, NULL))
> +			break;
> +
> +		msleep(PCIE_LINK_WAIT_SLEEP_MS);
> +	}
> +
> +	if (tegra264_pcie_link_up(pcie, &speed)) {
> +		msleep(PCIE_RESET_CONFIG_WAIT_MS);
> +		dev_info(pcie->dev, "PCIe #%u link is up (speed: %s)\n",
> +			 pcie->ctl_id, pci_speed_string(speed));
> +		tegra264_pcie_icc_set(pcie);
> +		pcie->link_up = true;
> +	} else {
> +		dev_info(pcie->dev, "PCIe #%u link is down\n", pcie->ctl_id);
> +
> +		value = readl(pcie->xtl + XTL_RC_MGMT_CLOCK_CONTROL);
> +
> +		/*
> +		 * Set link state only when link fails and no hot-plug feature
> +		 * is present.
> +		 */
> +		if ((value & XTL_RC_MGMT_CLOCK_CONTROL_PEX_CLKREQ_I_N_PIN_USE_CONV_TO_PRSNT) == 0) {
> +			dev_info(pcie->dev,
> +				 "PCIe #%u link is down and not hotplug-capable, turning off\n",
> +				 pcie->ctl_id);
> +			tegra264_pcie_power_off(pcie);
> +			pcie->link_up = false;
> +		} else {
> +			pcie->link_up = true;
> +		}
> +	}
> +}
> +
> +static void tegra264_pcie_deinit(struct tegra264_pcie *pcie)
> +{
> +	u32 value;
> +
> +	/* take the endpoint into reset */
> +	value = readl(pcie->xtl + XTL_RC_MGMT_PERST_CONTROL);
> +	value &= ~XTL_RC_MGMT_PERST_CONTROL_PERST_O_N;
> +	writel(value, pcie->xtl + XTL_RC_MGMT_PERST_CONTROL);
> +}
> +
> +static int tegra264_pcie_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct pci_host_bridge *bridge;
> +	struct tegra264_pcie *pcie;
> +	struct resource_entry *bus;
> +	struct resource *res;
> +	int err;
> +
> +	bridge = devm_pci_alloc_host_bridge(dev, sizeof(struct tegra264_pcie));
> +	if (!bridge)
> +		return dev_err_probe(dev, -ENOMEM,
> +				     "failed to allocate host bridge\n");
> +
> +	pcie = pci_host_bridge_priv(bridge);
> +	platform_set_drvdata(pdev, pcie);
> +	pcie->bridge = bridge;
> +	pcie->dev = dev;
> +
> +	err = tegra264_pcie_parse_dt(pcie);
> +	if (err < 0)
> +		return dev_err_probe(dev, err, "failed to parse device tree\n");
> +
> +	pcie->xal = devm_platform_ioremap_resource_byname(pdev, "xal");
> +	if (IS_ERR(pcie->xal))
> +		return dev_err_probe(dev, PTR_ERR(pcie->xal),
> +				     "failed to map XAL memory\n");
> +
> +	pcie->xtl = devm_platform_ioremap_resource_byname(pdev, "xtl-pri");
> +	if (IS_ERR(pcie->xtl))
> +		return dev_err_probe(dev, PTR_ERR(pcie->xtl),
> +				     "failed to map XTL-PRI memory\n");
> +
> +	bus = resource_list_first_type(&bridge->windows, IORESOURCE_BUS);
> +	if (!bus)
> +		return dev_err_probe(dev, -ENODEV,
> +				     "failed to get bus resources\n");
> +
> +	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ecam");
> +	if (!res)
> +		return dev_err_probe(dev, -ENXIO,
> +				     "failed to get ECAM resource\n");
> +
> +	pcie->icc_path = devm_of_icc_get(dev, "write");
> +	if (IS_ERR(pcie->icc_path))
> +		return dev_err_probe(dev, PTR_ERR(pcie->icc_path),
> +				     "failed to get ICC\n");
> +
> +	pcie->bpmp = tegra_bpmp_get_with_id(dev, &pcie->ctl_id);
> +	if (IS_ERR(pcie->bpmp))
> +		return dev_err_probe(dev, PTR_ERR(pcie->bpmp),
> +				     "failed to get BPMP\n");
> +
> +	err = devm_pm_runtime_set_active_enabled(dev);

I belive this has to come after pm_runtime_get_sync() because at this point, the
controller is not enabled.

> +	if (err < 0) {
> +		dev_err_probe(dev, err, "failed to enable runtime PM\n");
> +		goto put_bpmp;
> +	}
> +
> +	err = pm_runtime_get_sync(dev);
> +	if (err < 0) {
> +		dev_err_probe(dev, err, "failed to power on device\n");
> +		goto put_bpmp;
> +	}
> +
> +	/* sanity check that programmed ranges match what's in DT */
> +	if (!tegra264_pcie_check_ranges(pdev)) {
> +		err = -EINVAL;
> +		goto put_pm;
> +	}
> +
> +	pcie->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops);
> +	if (IS_ERR(pcie->cfg)) {
> +		err = dev_err_probe(dev, PTR_ERR(pcie->cfg),
> +				    "failed to create ECAM\n");
> +		goto put_pm;
> +	}
> +
> +	bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops;
> +	bridge->sysdata = pcie->cfg;
> +	pcie->ecam = pcie->cfg->win;
> +
> +	tegra264_pcie_init(pcie);
> +
> +	if (!pcie->link_up)
> +		return 0;

So not hotplug support? Also, you do not want the driver to error out? I'm
wondering what's the use then?

> +
> +	err = pci_host_probe(bridge);
> +	if (err < 0) {
> +		dev_err_probe(dev, err, "failed to register host\n");
> +		goto free_ecam;
> +	}
> +
> +	return 0;
> +
> +free_ecam:

Nit: Prefix 'err' for the labels.

> +	pci_ecam_free(pcie->cfg);
> +put_pm:
> +	pm_runtime_put_sync(dev);
> +put_bpmp:
> +	tegra_bpmp_put(pcie->bpmp);
> +
> +	return err;
> +}
> +
> +static void tegra264_pcie_remove(struct platform_device *pdev)
> +{
> +	struct tegra264_pcie *pcie = platform_get_drvdata(pdev);
> +
> +	/*
> +	 * If we undo tegra264_pcie_init() then link goes down and need
> +	 * controller reset to bring up the link again. Remove intention is
> +	 * to clean up the root bridge and re-enumerate during bind.

But the controller will be consuming power even if PCIe is not used. Do you
really want that? Can't tegra264_pcie_init() handle the initialization? I'm
wondering how tegra264_pcie_deinit() in tegra264_pcie_suspend() works then.

> +	 */
> +	pci_lock_rescan_remove();
> +	pci_stop_root_bus(pcie->bridge->bus);
> +	pci_remove_root_bus(pcie->bridge->bus);
> +	pci_unlock_rescan_remove();
> +
> +	pm_runtime_put_sync(&pdev->dev);
> +	tegra_bpmp_put(pcie->bpmp);
> +	pci_ecam_free(pcie->cfg);
> +}
> +
> +static int tegra264_pcie_suspend(struct device *dev)
> +{
> +	struct tegra264_pcie *pcie = dev_get_drvdata(dev);
> +	int err;
> +
> +	tegra264_pcie_deinit(pcie);
> +
> +	if (pcie->wake_gpio && device_may_wakeup(dev)) {
> +		err = enable_irq_wake(pcie->wake_irq);
> +		if (err < 0)
> +			dev_err(dev, "failed to enable wake IRQ: %pe\n",
> +				ERR_PTR(err));
> +	}
> +
> +	return 0;
> +}
> +
> +static int tegra264_pcie_resume(struct device *dev)
> +{
> +	struct tegra264_pcie *pcie = dev_get_drvdata(dev);
> +	int err;
> +
> +	err = pinctrl_pm_select_default_state(dev);
> +	if (err < 0)
> +		dev_err(dev, "failed to configure sideband pins: %pe\n",
> +			ERR_PTR(err));

Please remind me if you justified this manual pinctrl handling before.

> +
> +	if (pcie->wake_gpio && device_may_wakeup(dev)) {
> +		err = disable_irq_wake(pcie->wake_irq);
> +		if (err < 0)
> +			dev_err(dev, "failed to disable wake IRQ: %pe\n",
> +				ERR_PTR(err));
> +	}
> +
> +	if (pcie->link_up == false)
> +		return 0;

How is this possible? If 'pcie->link_up' was 'false' during probe(), then it is
going to stay until tegra264_pcie_init() is called below.

- Mani

-- 
மணிவண்ணன் சதாசிவம்

^ permalink raw reply

* Re: [PATCH V2 3/3] dmaengine: zynqmp_dma: Guard IRQ handler against spurious interrupts
From: sashiko-bot @ 2026-06-18  7:26 UTC (permalink / raw)
  To: Golla Nagendra; +Cc: vkoul, Frank.Li, robh, dmaengine, conor+dt, devicetree
In-Reply-To: <20260618071056.2024286-4-nagendra.golla@amd.com>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

New issues:
- [High] The interrupt handler drops valid interrupts when CONFIG_PM is disabled.

Pre-existing issues:
- [High] Memory and runtime PM reference leaks on error paths in zynqmp_dma_alloc_chan_resources().
- [High] Missing synchronize_irq() before tasklet_kill() allows the tasklet to run after channel resources and PM references are freed.
--

commit 62db01b8d81ad56c8fb04b0d8f2adecdae634703
Author: Golla Nagendra <nagendra.golla@amd.com>

dmaengine: zynqmp_dma: Guard IRQ handler against spurious interrupts

This commit adds a pm_runtime_get_if_active() check in the
zynqmp_dma_irq_handler() to safely handle spurious interrupts. It
returns IRQ_NONE immediately if the device is not runtime-active,
preventing potential synchronous external aborts.

> diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
> index a9dfec3c0ca3d..ce9163138be74 100644
> --- a/drivers/dma/xilinx/zynqmp_dma.c
> +++ b/drivers/dma/xilinx/zynqmp_dma.c
> @@ -730,6 +730,9 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data)
>  	u32 isr, imr, status;
>  	irqreturn_t ret = IRQ_NONE;
>  
> +	if (pm_runtime_get_if_active(chan->dev) <= 0)
> +		return IRQ_NONE;

[Severity: High]
When CONFIG_PM is disabled, the pm_runtime_get_if_active() inline stub
automatically returns -EINVAL. Does this check unconditionally drop valid
interrupts in non-PM configurations?

Because -EINVAL is less than or equal to 0, the handler might always return
IRQ_NONE. This could potentially lead to an unacknowledged interrupt storm
or the kernel permanently disabling the IRQ line.

[ ... ]

[Severity: High]
This isn't a bug introduced by this patch, but do the error paths in
zynqmp_dma_alloc_chan_resources() leak the runtime PM reference?

drivers/dma/xilinx/zynqmp_dma.c:zynqmp_dma_alloc_chan_resources() {
    ...
    chan->sw_desc_pool = kzalloc_objs(*desc, ZYNQMP_DMA_NUM_DESCS);
    if (!chan->sw_desc_pool)
        return -ENOMEM;
    ...
    if (!chan->desc_pool_v)
        return -ENOMEM;
    ...
}

If pm_runtime_resume_and_get() is called earlier, returning directly
on memory allocation failures without calling pm_runtime_put() will
leak the PM reference and previously allocated memory.


[Severity: High]
This is a pre-existing issue, but does zynqmp_dma_synchronize() miss
a required IRQ serialization step?

drivers/dma/xilinx/zynqmp_dma.c:zynqmp_dma_synchronize() {
        struct zynqmp_dma_chan *chan = to_chan(dchan);

        tasklet_kill(&chan->tasklet);
}

If zynqmp_dma_irq_handler() is executing concurrently and gets preempted
just before calling tasklet_schedule(), tasklet_kill() might see no
scheduled tasklet and return immediately. 

When the IRQ handler resumes and schedules the tasklet, could it execute
asynchronously after channel resources and PM references are freed,
potentially causing a synchronous external abort? 

Would calling synchronize_irq() first resolve this?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260618071056.2024286-1-nagendra.golla@amd.com?part=3

^ permalink raw reply

* Re: [PATCH V2 2/3] dmaengine: zynqmp_dma: Add per-channel reset support
From: sashiko-bot @ 2026-06-18  7:22 UTC (permalink / raw)
  To: Golla Nagendra; +Cc: vkoul, robh, conor+dt, Frank.Li, dmaengine, devicetree
In-Reply-To: <20260618071056.2024286-3-nagendra.golla@amd.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] Using pm_runtime_put_noidle() before pm_runtime_disable() leaves hardware active and leaks clock references on probe deferral.
--

commit 0e5952f6551c9a8d3b55c9a149969bfbe79c7c9c
Author: Golla Nagendra <nagendra.golla@amd.com>

dmaengine: zynqmp_dma: Add per-channel reset support

This commit adds per-channel reset support for Versal Gen 2 and Versal Net
SoCs using device_reset_optional() during channel probe. It also adds a call
to pm_runtime_put_noidle() in the probe error path to balance the usage
counter.

> diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
> --- a/drivers/dma/xilinx/zynqmp_dma.c
> +++ b/drivers/dma/xilinx/zynqmp_dma.c
[ ... ]
> @@ -1152,6 +1158,7 @@ static int zynqmp_dma_probe(struct platform_device *pdev)
>  err_disable_pm:
>  	if (!pm_runtime_enabled(zdev->dev))
>  		zynqmp_dma_runtime_suspend(zdev->dev);
> +	pm_runtime_put_noidle(zdev->dev);

[Severity: High]
Does using pm_runtime_put_noidle() here instead of pm_runtime_put_sync()
cause a clock leak during probe deferral?

If device_reset_optional() returns -EPROBE_DEFER, pm_runtime_put_noidle()
decrements the usage counter but does not suspend the device. The device
status remains active. When pm_runtime_disable() is called and the probe
fails, the driver core handles the failure by forcefully setting the status
to suspended without calling the driver's runtime suspend callback.

This leaves clk_main and clk_apb enabled. On the next probe retry,
pm_runtime_resume_and_get() will double-enable the clocks.

Should pm_runtime_put_sync() be used before pm_runtime_disable() to ensure
the device is synchronously suspended and clocks are properly disabled?

>  	pm_runtime_disable(zdev->dev);
>  	return ret;
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260618071056.2024286-1-nagendra.golla@amd.com?part=2

^ permalink raw reply

* Re: [PATCH 11/11] ARM: dts: ux500: Remove DB8500 EPOD regulators
From: sashiko-bot @ 2026-06-18  7:20 UTC (permalink / raw)
  To: Linus Walleij
  Cc: dmaengine, robh, devicetree, dri-devel, vkoul, Frank.Li, conor+dt
In-Reply-To: <20260618-ux500-power-domains-v7-1-v1-11-eb5e50b1a588@kernel.org>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Critical] Removing the EPOD regulator nodes and consumer supply properties breaks backwards compatibility with older kernels.
- [Low] Removed EPOD regulators and `epod-supply` property are still marked as required in their respective YAML binding schemas, causing `make dtbs_check` to fail.
--

commit cc40a973d5076813fb2d1de31c4ef7f4d0231e78
Author: Linus Walleij <linusw@kernel.org>

ARM: dts: ux500: Remove DB8500 EPOD regulators

This commit deletes the obsolete DB8500 EPOD regulator nodes from the device
tree. It retains only the VAPE and VSMPS2 compatibility regulators.

> diff --git a/arch/arm/boot/dts/st/ste-dbx5x0.dtsi b/arch/arm/boot/dts/st/ste-dbx5x0.dtsi
> index fd6a075e4c93c..18727953a8630 100644
> --- a/arch/arm/boot/dts/st/ste-dbx5x0.dtsi
> +++ b/arch/arm/boot/dts/st/ste-dbx5x0.dtsi
> @@ -663,97 +663,17 @@ thermal: thermal@801573c0 {
>  				#thermal-sensor-cells = <0>;
>  			};
>  
> -			/*
> -			 * TODO: Delete these bogus regulators and replace with power
> -			 * domains.
> -			 */
>  			db8500-prcmu-regulators {
>  				compatible = "stericsson,db8500-prcmu-regulator";

[Severity: Low]
Does this commit also need to update the YAML binding schemas? 

It appears that Documentation/devicetree/bindings/mfd/stericsson,db8500-prcmu.yaml
still mandates the presence of deleted EPOD nodes (e.g., db8500_varm, db8500_sga) 
in its required: block, which could cause dtbs_check to fail.

[ ... ]

> @@ -1215,8 +1127,6 @@ gpu@a0300000 {
>  			clocks = <&prcmu_clk PRCMU_ACLK>, <&prcmu_clk PRCMU_SGACLK>;
>  			clock-names = "bus", "core";
>  			power-domains = <&pm_domains DOMAIN_SGA>;
> -			/* TODO: delete and replace with power-domain handling */
> -			mali-supply = <&db8500_sga_reg>;
>  		};

[Severity: Critical]
Does removing the mali-supply property and its target regulator break 
backwards compatibility with older kernels? 

Device trees are considered a stable ABI, and older kernels lacking the updated
ste-ux500-pm-domain driver might rely entirely on the legacy 
db8500-prcmu-regulator driver to power these components. 

By aggressively removing these regulators, older kernels might fall back to dummy
regulators and leave the physical hardware unpowered, leading to crashes when
drivers attempt to access unclocked and unpowered hardware registers.

>  
>  		mcde@a0350000 {
> @@ -1224,8 +1134,6 @@ mcde@a0350000 {
>  			reg = <0xa0350000 0x1000>;
>  			interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
>  			power-domains = <&pm_domains DOMAIN_B2R2_MCDE>;
> -			/* TODO: delete and replace with power-domain handling */
> -			epod-supply = <&db8500_b2r2_mcde_reg>;

[Severity: Critical]
Similar to the mali-supply removal above, does dropping the epod-supply 
property break the display subsystem power management on older kernels that 
rely on it?

[Severity: Low]
Additionally, is epod-supply still required by the display subsystem binding 
schema? Documentation/devicetree/bindings/display/ste,mcde.yaml seems to still 
mandate epod-supply in its required: block.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260618-ux500-power-domains-v7-1-v1-0-eb5e50b1a588@kernel.org?part=11

^ permalink raw reply

* Re: [PATCH v3 1/5] clk: renesas: rzv2h-cpg: Use per-SoC PLL reference frequency for calculations
From: Geert Uytterhoeven @ 2026-06-18  7:17 UTC (permalink / raw)
  To: Lad, Prabhakar
  Cc: Michael Turquette, Stephen Boyd, Brian Masney, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Magnus Damm, linux-kernel,
	linux-renesas-soc, linux-clk, devicetree, Biju Das,
	Fabrizio Castro, Lad Prabhakar
In-Reply-To: <CA+V-a8uixCr+LZ+2SrX1b2p3nNwkq8=ctdpzx=BF9OcR9eJW1w@mail.gmail.com>

Hi Prabhakar,

On Wed, 17 Jun 2026 at 22:17, Lad, Prabhakar <prabhakar.csengg@gmail.com> wrote:
> On Wed, Jun 17, 2026 at 10:48 AM Geert Uytterhoeven
> <geert@linux-m68k.org> wrote:
> > On Mon, 15 Jun 2026 at 12:48, Prabhakar <prabhakar.csengg@gmail.com> wrote:
> > > From: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> > >
> > > Introduce a per-SoC PLL reference input frequency parameter to avoid
> > > relying on a hardcoded 24MHz constant during PLL configuration math.
> > >
> > > Add an input_fref member to struct rzv2h_pll_limits. In the core
> > > calculation helper rzv2h_get_pll_pars(), derive the base input clock
> > > rate from limits->input_fref, utilizing the conditional ternary operator
> > > to fall back to 24MHz if the struct field is left uninitialized (0), and
> > > drop the obsolete macro RZ_V2H_OSC_CLK_IN_MEGA.
> > >
> > > This abstraction permits the reuse of the common PLL divider logic on
> > > newer SoC platforms like the RZ/T2H, which feature a 48 MHz PLL reference
> > > clock input instead of the 24 MHz signal used by RZ/V2H(P), without
> > > disrupting existing platforms.
> > >
> > > Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
> >
> > Thanks for your patch!
> >
> > > --- a/include/linux/clk/renesas.h
> > > +++ b/include/linux/clk/renesas.h
> > > @@ -53,6 +53,9 @@ static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { }
> > >   * various parameters used to configure a PLL. These limits ensure
> > >   * the PLL operates within valid and stable ranges.
> > >   *
> > > + * @input_fref: Reference input frequency to the PLL (in MHz). If set
> >
> > "in Hz", as pointed out by Sashiko.
> >
> Agreed.
>
> > > + * to 0, a default value of 24MHz is used.
> > > + *
> I'll also replace `24MHz` to `24000000 Hz`.

Personally, I find "24 MHz" easier to read.
Counting zeroes without thousands-grouping can be hard...
https://lore.kernel.org/CAMuHMdUp3fOkAttk2FKJTh5svaiMwgu4JZ8utPeHghU4TQLE-w@mail.gmail.com

Gr{oetje,eeting}s,

                        Geert

-- 
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
                                -- Linus Torvalds

^ permalink raw reply

* Re: [PATCH v3 1/8] dt-bindings: remoteproc: qcom,pas: add thermal mitigation properties
From: Daniel Lezcano @ 2026-06-18  7:17 UTC (permalink / raw)
  To: Krzysztof Kozlowski, Gaurav Kohli
  Cc: Bjorn Andersson, Mathieu Poirier, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Amit Kucheria,
	Manivannan Sadhasivam, Konrad Dybcio, Kees Cook,
	Gustavo A. R. Silva, cros-qcom-dts-watchers, linux-arm-msm,
	linux-remoteproc, devicetree, linux-kernel, linux-pm,
	linux-hardening, Manaf Meethalavalappu Pallikunhi
In-Reply-To: <c32e263c-ba4e-4899-a935-e129de0f1269@kernel.org>

On 6/16/26 06:21, Krzysztof Kozlowski wrote:
> On 15/06/2026 14:30, Daniel Lezcano wrote:
>> Hi Gaurav,
>>>>> thanks for review, shall i use driver data, which is basically pas
>>>>> data structure like below:
>>>>>
>>>>> static const struct qcom_pas_data {
>>>>>       .crash_reason_smem = 601,
>>>>>       .firmware_name = "cdsp.mdt",
>>>>>       .tmd_names = (const char *[]){"xyz", NULL},
>>>>>       .num_tmds = 1,
>>>>>
>>>>> Is something like above acceptable? and this will also help to filter
>>>>> tmd names as well?
>>>>
>>>>
>>>> How the thermal framework will bind the thermal zone with the TMD ?
>>>> (node pointer, id) ?
>>>>
>>>
>>> Hi Daniel,
>>>
>>> thanks for review.
>>>
>>> With id only, in this case instead of taking tmd names from device tree,
>>> qmi_tmd will take tmd name from pas_data(driver) and register with the
>>> cooling framework with id only. Please let us know if this looks fine.
>> May be I'm missing something but:
>>
>>    - The QMI TMD returns a list of names, not ids
>>    - The QMI TMD may return the list in different order than assumed
>>    - The cooling map index points to the name of the TMD in the DT
>>    - This name is used to match the name in the aformentionned list
>>    - The index in the list and the id in the DT can differ
>>
>> Krzysztof , I don't get why having the TMD names as properties is wrong,
>> they describes the existing TMDs on the system and the cooling maps
>> index points to the one to be connected with thermal zone.
> 
> 
> 'xxx-names' have a fixed meaning in DT by convention - assign
> identifiable strings to the 'xxx'. I miss the property 'tmd' in such
> case - its definition and meaning. Where is it?
> 
> But maybe you just want list of strings, so I am open to discuss it - I
> don't understand the need for this property and commit and property
> description tell me nothing.
> 
> Really, this commit message is basically non-existing. It explains what
> it did and provides that much explanation WHY:
> 
> "- tmd-names (thermal mitigation device names)"
> 
> Really? This is the explanation why this change is being made, why this
> property is needed?
> 
> So sure, describe the problem being solved and WHY this problem is being
> solved that way. Maybe it will fit DT.

Ok, I understand

Let me try to clarify.

When the QMI TMD protocol is initialized, the list of available TMDs 
provided by the service is requested. They are identified by a *string* 
not a numerical id.

We can not assume the list is always in the same order because the QMI 
TMD is a separate subsystem and the interface is the protocol. The 
protocol does not refer to any TMD with an index but its name. 
Hardcoding an index here is not possible.

The name identifies the TMD we connect to and in return we receive a 
handler to use when sending the QMI messages.

That is for the driver part.

I understand for the DT, it is possible to not give the tmd-names 
because the it can go into the driver's data structure.

But the thermal framework won't be able to associate a cooling device 
(one TMD) with a thermal zone because the cooling mapping must point to 
a cooling device in the DT.

Initially, Gaurav sent a description where each TMD was a child node of 
the remote proc node. And you rightfully told us it is no longer the way 
to go as stated in the documentation. And you suggested to replace the 
child nodes with an array with the tmd-names and add an index in the 
cooling map pointing to this array.

The thermal framework has been extended to support this new format and 
associate the cooling device with the thermal zone. This change is now 
upstream for v7.2 [1]

The resulting implementation is the driver gets the tmd-names array 
property. For each name, it connects to the QMI TMD and register the 
cooling device with the index corresponding to the name position on the 
tmd-names array.

On the other side, the thermal framework parses the cooling-map, gets 
the index and do the association (binding).

The tmd-names array property replaces the child nodes and allows to do 
the mapping between the string based identifier with a numerical id.

I hope that clarifies the approach.

   -- Daniel

[1] 
https://lore.kernel.org/all/20260526140802.1059293-12-daniel.lezcano@oss.qualcomm.com/

^ permalink raw reply

* [PATCH V2 3/3] dmaengine: zynqmp_dma: Guard IRQ handler against spurious interrupts
From: Golla Nagendra @ 2026-06-18  7:10 UTC (permalink / raw)
  To: vkoul, Frank.Li, michal.simek, robh, krzk+dt, conor+dt,
	nagendra.golla, jay.buddhabhatti, harini.katakam, m.tretter,
	radhey.shyam.pandey, abin.joseph, kees, sakari.ailus
  Cc: git, dmaengine, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20260618071056.2024286-1-nagendra.golla@amd.com>

Add pm_runtime_get_if_active() check in zynqmp_dma_irq_handler() to
safely handle spurious interrupts that may arrive while the device is
runtime-suspended. Without this guard, a spurious interrupt could cause
the handler to access hardware registers (ISR, IMR) with clocks gated,
potentially leading to a synchronous external abort and kernel crash.

When the device is not runtime-active, pm_runtime_get_if_active()
returns false without incrementing the usage counter, and the handler
returns IRQ_NONE immediately. When the device is active, it increments
the usage counter to prevent a concurrent runtime suspend during
register access, and pm_runtime_put() releases the reference afterward.

Signed-off-by: Golla Nagendra <nagendra.golla@amd.com>
---
 drivers/dma/xilinx/zynqmp_dma.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index a9dfec3c0ca3..ce9163138be7 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -730,6 +730,9 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data)
 	u32 isr, imr, status;
 	irqreturn_t ret = IRQ_NONE;
 
+	if (pm_runtime_get_if_active(chan->dev) <= 0)
+		return IRQ_NONE;
+
 	isr = readl(chan->regs + ZYNQMP_DMA_ISR);
 	imr = readl(chan->regs + ZYNQMP_DMA_IMR);
 	status = isr & ~imr;
@@ -756,6 +759,8 @@ static irqreturn_t zynqmp_dma_irq_handler(int irq, void *data)
 		ret = IRQ_HANDLED;
 	}
 
+	pm_runtime_put(chan->dev);
+
 	return ret;
 }
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH V2 2/3] dmaengine: zynqmp_dma: Add per-channel reset support
From: Golla Nagendra @ 2026-06-18  7:10 UTC (permalink / raw)
  To: vkoul, Frank.Li, michal.simek, robh, krzk+dt, conor+dt,
	nagendra.golla, jay.buddhabhatti, harini.katakam, m.tretter,
	radhey.shyam.pandey, abin.joseph, kees, sakari.ailus
  Cc: git, dmaengine, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20260618071056.2024286-1-nagendra.golla@amd.com>

Versal Gen 2 and Versal Net SoCs expose a dedicated reset line per
ZDMA channel, replacing the earlier approach where a single reset
was shared across all channels. Add reset handling in the channel
probe path using device_reset_optional() to trigger a reset pulse
on the channel during initialization.

Platforms without per-channel reset continue to work unaffected
since device_reset_optional() returns 0 when no reset is specified.

add pm_runtime_put_noidle() in the probe error path before
pm_runtime_disable() to balance the usage counter incremented by
pm_runtime_resume_and_get(). This is particularly important since
device_reset_optional() can return -EPROBE_DEFER, causing the
kernel to retry probe() and leak one PM usage count per retry
without the put.

Signed-off-by: Golla Nagendra <nagendra.golla@amd.com>
---
 drivers/dma/xilinx/zynqmp_dma.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c
index f6a812e49ddc..a9dfec3c0ca3 100644
--- a/drivers/dma/xilinx/zynqmp_dma.c
+++ b/drivers/dma/xilinx/zynqmp_dma.c
@@ -18,6 +18,7 @@
 #include <linux/clk.h>
 #include <linux/io-64-nonatomic-lo-hi.h>
 #include <linux/pm_runtime.h>
+#include <linux/reset.h>
 
 #include "../dmaengine.h"
 
@@ -916,6 +917,11 @@ static int zynqmp_dma_chan_probe(struct zynqmp_dma_device *zdev,
 	if (IS_ERR(chan->regs))
 		return PTR_ERR(chan->regs);
 
+	err = device_reset_optional(&pdev->dev);
+	if (err)
+		return dev_err_probe(&pdev->dev, err,
+				     "failed to reset channel\n");
+
 	chan->bus_width = ZYNQMP_DMA_BUS_WIDTH_64;
 	chan->dst_burst_len = ZYNQMP_DMA_MAX_DST_BURST_LEN;
 	chan->src_burst_len = ZYNQMP_DMA_MAX_SRC_BURST_LEN;
@@ -1152,6 +1158,7 @@ static int zynqmp_dma_probe(struct platform_device *pdev)
 err_disable_pm:
 	if (!pm_runtime_enabled(zdev->dev))
 		zynqmp_dma_runtime_suspend(zdev->dev);
+	pm_runtime_put_noidle(zdev->dev);
 	pm_runtime_disable(zdev->dev);
 	return ret;
 }
-- 
2.34.1


^ permalink raw reply related

* [PATCH V2 1/3] dt-bindings: dma: xilinx: Add optional resets property for ZDMA
From: Golla Nagendra @ 2026-06-18  7:10 UTC (permalink / raw)
  To: vkoul, Frank.Li, michal.simek, robh, krzk+dt, conor+dt,
	nagendra.golla, jay.buddhabhatti, harini.katakam, m.tretter,
	radhey.shyam.pandey, abin.joseph, kees, sakari.ailus
  Cc: git, dmaengine, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <20260618071056.2024286-1-nagendra.golla@amd.com>

From: Jay Buddhabhatti <jay.buddhabhatti@amd.com>

Newer SoCs such as Versal Gen2 and Versal‑Net expose a reset line
for ZDMA. Older SoCs do not have this provision. Add an optional
resets property to describe this reset.

Signed-off-by: Jay Buddhabhatti <jay.buddhabhatti@amd.com>
Co-developed-by: Golla Nagendra <nagendra.golla@amd.com>
Signed-off-by: Golla Nagendra <nagendra.golla@amd.com>
---
 .../devicetree/bindings/dma/xilinx/xlnx,zynqmp-dma-1.0.yaml    | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dma-1.0.yaml b/Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dma-1.0.yaml
index 2da86037ad79..dff16763e11b 100644
--- a/Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dma-1.0.yaml
+++ b/Documentation/devicetree/bindings/dma/xilinx/xlnx,zynqmp-dma-1.0.yaml
@@ -56,6 +56,9 @@ properties:
   iommus:
     maxItems: 1
 
+  resets:
+    maxItems: 1
+
   power-domains:
     maxItems: 1
 
-- 
2.34.1


^ permalink raw reply related

* [PATCH V2 0/3] dmaengine: zynqmp_dma: Add per-channel reset support
From: Golla Nagendra @ 2026-06-18  7:10 UTC (permalink / raw)
  To: vkoul, Frank.Li, michal.simek, robh, krzk+dt, conor+dt,
	nagendra.golla, jay.buddhabhatti, harini.katakam, m.tretter,
	radhey.shyam.pandey, abin.joseph, kees, sakari.ailus
  Cc: git, dmaengine, devicetree, linux-arm-kernel, linux-kernel

This series adds per-channel reset support to the ZynqMP DMA driver using
the generic reset framework, along with the corresponding dt-bindings
update. It also adds a runtime PM guard in the IRQ handler to handle
spurious interrupts safely.

Patch 1 adds the optional 'resets' property to the ZynqMP DMA dt-binding.

Patch 2 adds reset control handling in the channel probe path to assert
and deassert the channel reset during initialization.

Patch 3 adds a pm_runtime_get_if_active() check in the IRQ handler to
avoid accessing hardware registers when the device is runtime-suspended,
which could occur on spurious interrupts.

Changes in V2:
- Added patch 3 to guard IRQ handler against spurious interrupts

Golla Nagendra (2):
  dmaengine: zynqmp_dma: Add per-channel reset support
  dmaengine: zynqmp_dma: Guard IRQ handler against spurious interrupts

Jay Buddhabhatti (1):
  dt-bindings: dma: xilinx: Add optional resets property for ZDMA

 .../bindings/dma/xilinx/xlnx,zynqmp-dma-1.0.yaml     |  3 +++
 drivers/dma/xilinx/zynqmp_dma.c                      | 12 ++++++++++++
 2 files changed, 15 insertions(+)

-- 
2.34.1


^ permalink raw reply

* Re: [PATCH v9 4/5] iio: adc: versal-sysmon: add threshold event support
From: Andy Shevchenko @ 2026-06-18  7:09 UTC (permalink / raw)
  To: Salih Erim
  Cc: jic23, andy, dlechner, nuno.sa, robh, krzk+dt, conor+dt,
	conall.ogriofa, michal.simek, linux, erimsalih, linux-iio,
	devicetree, linux-kernel
In-Reply-To: <20260617180147.3370346-5-salih.erim@amd.com>

On Wed, Jun 17, 2026 at 07:01:46PM +0100, Salih Erim wrote:
> Add threshold event support for temperature and supply voltage
> channels.
> 
> Temperature events:
>   - Rising threshold with configurable value on the device
>     temperature channel (current max across all satellites)
>   - Per-channel hysteresis as a millicelsius value
>   - Event direction is IIO_EV_DIR_RISING (hysteresis mode)
> 
> Supply voltage events:
>   - Rising/falling threshold per supply channel
>   - Per-channel alarm enable via alarm configuration registers
> 
> The hardware supports both window and hysteresis alarm modes for
> temperature. This driver uses hysteresis mode, where the upper
> threshold triggers the alarm and the lower threshold clears it
> (re-arm point). The hardware has a single ISR bit per temperature
> channel with no indication of which threshold was crossed, so
> hysteresis mode is the natural fit. The lower threshold register
> is computed internally as (upper - hysteresis).
> 
> Hysteresis is stored in the driver as a millicelsius value,
> initialized from the hardware registers at probe. Writing the
> rising threshold or hysteresis recomputes the lower register.
> ALARM_CONFIG is hard-coded to hysteresis mode during init.
> 
> The hardware also provides a separate over-temperature (OT)
> threshold, but it is not exposed through IIO as it serves as a
> hardware safety mechanism for platform shutdown. OT will be
> exposed through the thermal framework in a follow-up series.
> 
> The interrupt handler masks active threshold interrupts (which are
> level-sensitive) and schedules a delayed worker to poll for condition
> clear before unmasking. When no hardware IRQ is available, event
> specs are not attached and interrupt init is skipped, since the
> I2C regmap backend cannot be called from atomic context.
> 
> When disabling a supply channel alarm, the group interrupt remains
> active if any other channel in the same alarm group still has an
> alarm enabled.
> 
> A devm cleanup action masks all interrupts on driver unbind to
> prevent unhandled interrupt storms after the IRQ handler is freed.

A couple of nit-picks below, otherwise
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>

...

>  #include <linux/cleanup.h>
>  #include <linux/device.h>
>  #include <linux/err.h>
> +#include <linux/interrupt.h>

+ limits.h // U16_MAX, et cetera

> +#include <linux/minmax.h>
>  #include <linux/module.h>
>  #include <linux/overflow.h>
>  #include <linux/property.h>

...

> -static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev)
> +static int sysmon_parse_fw(struct iio_dev *indio_dev, struct device *dev, int irq)
>  {
>  	unsigned int num_chan, num_static, idx, temp_chan_idx, volt_chan_idx;
> -	unsigned int num_supply, num_temp;
>  	struct iio_chan_spec *sysmon_channels;
> +	unsigned int num_supply, num_temp;

Stray change, and I would expect to see all num_* on one line, and
all *_idx on another.

	unsigned int num_chan, num_static, num_supply, num_temp;
	unsigned int idx, temp_chan_idx, volt_chan_idx;
	struct iio_chan_spec *sysmon_channels;

TL;DR:
in this patch the above should not be modified as the previous one should
provide already a nice structure.

>  	const char *label;
>  	u32 reg;
>  	int ret;

-- 
With Best Regards,
Andy Shevchenko



^ permalink raw reply

* Re: [PATCH v4 2/3] riscv: track effective hardware PTE A/D updating
From: sashiko-bot @ 2026-06-18  7:02 UTC (permalink / raw)
  To: Yunhui Cui; +Cc: devicetree, conor+dt, robh
In-Reply-To: <20260618064406.14508-3-cuiyunhui@bytedance.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] A hotplugged CPU lacking the Svadu extension is incorrectly allowed to come online when hardware PTE A/D updating is globally enabled but FWFT is not required, leading to silent data corruption.
--

commit e238566a69f2ecd235872518a759dd75d54f71af
Author: Yunhui Cui <cuiyunhui@bytedance.com>

riscv: track effective hardware PTE A/D updating

This commit tracks the runtime state of hardware PTE A/D updating using a
static key. It attempts to enable FWFT for all online harts, falling back
to software A/D if that fails, and ensures hotplugged CPUs match the global
state before coming online.

> diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
> --- a/arch/riscv/kernel/cpufeature.c
> +++ b/arch/riscv/kernel/cpufeature.c

[ ... ]

> +int riscv_enable_hw_pte_ad_updating(void)
> +{
> +	unsigned int cpu;
> +	int ret;
> +
> +	if (!riscv_has_hw_pte_ad_updating() ||
> +	    !riscv_hw_pte_ad_updating_requires_fwft)
> +		return 0;

[Severity: High]
Does this code allow a hotplugged CPU to come online without the Svadu
extension when hardware A/D updating is globally enabled?

If the boot CPUs have Svadu but lack Svade, the flag
riscv_hw_pte_ad_updating_requires_fwft is set to false and the global
static key riscv_hw_pte_ad_updating is enabled.

When a new CPU is later hotplugged, it calls this function via
smp_callin(). Since riscv_hw_pte_ad_updating_requires_fwft is false, it
returns 0 and bypasses any capability checks for the local CPU.

If the hotplugged CPU does not actually support Svadu (e.g., in an
asymmetric SoC or due to firmware misconfiguration), it successfully
comes online. Because the kernel globally assumes hardware A/D updating
is active, it skips software A/D fault handling.

Could this lead to the hotplugged CPU silently failing to update
accessed/dirty bits, causing data corruption when dirty data is dropped
during reclaim?

> +
> +	cpu = smp_processor_id();
> +	ret = riscv_enable_local_hw_pte_ad_updating();
> +	if (ret)
> +		pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
> +		       cpu, ret);
> +
> +	return ret;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260618064406.14508-1-cuiyunhui@bytedance.com?part=2

^ permalink raw reply

* [PATCH] dt-bindings: spi: st,stm32-qspi: Add power-domains property
From: Patrice Chotard @ 2026-06-18  6:46 UTC (permalink / raw)
  To: Mark Brown, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Maxime Coquelin, Alexandre Torgue, Christophe Kerello
  Cc: linux-spi, devicetree, linux-stm32, linux-arm-kernel,
	linux-kernel, Patrice Chotard

STM32 QSPI may be in a power domain. Allow a single 'power-domains'
entry for STM32 QSPI.

Signed-off-by: Patrice Chotard <patrice.chotard@foss.st.com>
---
 Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml b/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml
index 3f1a27efff80..ee57739b73b8 100644
--- a/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml
+++ b/Documentation/devicetree/bindings/spi/st,stm32-qspi.yaml
@@ -50,6 +50,9 @@ properties:
     minItems: 1
     maxItems: 2
 
+  power-domains:
+    maxItems: 1
+
 required:
   - compatible
   - reg

---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260618-add_power_domain_for_qpsi-898d2ebf20fa

Best regards,
--  
Patrice Chotard <patrice.chotard@foss.st.com>


^ permalink raw reply related

* [PATCH v4 3/3] riscv: preserve A/D and soft-dirty state across PTE updates
From: Yunhui Cui @ 2026-06-18  6:44 UTC (permalink / raw)
  To: akpm, alex, andrew+kernel, aou, apatel, apopple, atishp,
	baolin.wang, cleger, conor+dt, cuiyunhui, debug, devicetree,
	guodong, hui.wang, krzk+dt, linux-kernel, linux-riscv,
	liu.xuemei1, namcao, nick.hu, palmer, pincheng.plct, pjw,
	qingwei.hu, ritesh.list, rmclure, robh, wangruikang, zhangchunyan,
	zong.li
In-Reply-To: <20260618064406.14508-1-cuiyunhui@bytedance.com>

Use cmpxchg-based PTE updates so software permission changes do not lose
concurrent A/D updates from hardware. Preserve soft-dirty state as well,
since RISC-V marks PTEs dirty and soft-dirty together.

Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
---
 arch/riscv/include/asm/pgtable.h | 27 +++++++++----
 arch/riscv/mm/pgtable.c          | 68 ++++++++++++++++++++++++++------
 2 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/arch/riscv/include/asm/pgtable.h b/arch/riscv/include/asm/pgtable.h
index 5d5756bda82e3..02286b48dc471 100644
--- a/arch/riscv/include/asm/pgtable.h
+++ b/arch/riscv/include/asm/pgtable.h
@@ -678,15 +678,21 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
 static inline void ptep_set_wrprotect(struct mm_struct *mm,
 				      unsigned long address, pte_t *ptep)
 {
-	pte_t read_pte = READ_ONCE(*ptep);
+	pte_t old_pte;
+	pte_t pte;
 	/*
 	 * ptep_set_wrprotect can be called for shadow stack ranges too.
 	 * shadow stack memory is XWR = 010 and thus clearing _PAGE_WRITE will lead to
 	 * encoding 000b which is wrong encoding with V = 1. This should lead to page fault
 	 * but we dont want this wrong configuration to be set in page tables.
 	 */
-	atomic_long_set((atomic_long_t *)ptep,
-			((pte_val(read_pte) & ~(unsigned long)_PAGE_WRITE) | _PAGE_READ));
+	pte = READ_ONCE(*ptep);
+	do {
+		old_pte = pte;
+		pte = pte_wrprotect(pte);
+		pte_val(pte) = cmpxchg_relaxed(&pte_val(*ptep), pte_val(old_pte),
+					       pte_val(pte));
+	} while (pte_val(pte) != pte_val(old_pte));
 }
 
 #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
@@ -742,14 +748,14 @@ static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
 #define pgprot_dmacoherent pgprot_writecombine
 
 /*
- * Both Svade and Svadu control the hardware behavior when the PTE A/D bits need to be set. By
- * default the M-mode firmware enables the hardware updating scheme when only Svadu is present in
- * DT.
+ * Both Svade and Svadu control the hardware behavior when the PTE A/D bits
+ * need to be set. The core MM code only cares whether hardware updating of
+ * the accessed/dirty state is currently active.
  */
 #define arch_has_hw_pte_young arch_has_hw_pte_young
 static inline bool arch_has_hw_pte_young(void)
 {
-	return riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
+	return riscv_has_hw_pte_ad_updating();
 }
 
 /*
@@ -1040,6 +1046,13 @@ static inline void pmdp_set_wrprotect(struct mm_struct *mm,
 	ptep_set_wrprotect(mm, address, (pte_t *)pmdp);
 }
 
+#define __HAVE_ARCH_PUDP_SET_WRPROTECT
+static inline void pudp_set_wrprotect(struct mm_struct *mm,
+				      unsigned long address, pud_t *pudp)
+{
+	ptep_set_wrprotect(mm, address, (pte_t *)pudp);
+}
+
 #define pmdp_establish pmdp_establish
 static inline pmd_t pmdp_establish(struct vm_area_struct *vma,
 				unsigned long address, pmd_t *pmdp, pmd_t pmd)
diff --git a/arch/riscv/mm/pgtable.c b/arch/riscv/mm/pgtable.c
index 9c4427d0b1874..98eed19ea70de 100644
--- a/arch/riscv/mm/pgtable.c
+++ b/arch/riscv/mm/pgtable.c
@@ -5,23 +5,55 @@
 #include <linux/kernel.h>
 #include <linux/pgtable.h>
 
+#define RISCV_PTE_ACCESS_FLAG_MASK	(_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC | \
+					 _PAGE_ACCESSED | _PAGE_DIRTY | \
+					 _PAGE_SOFT_DIRTY)
+
+static inline unsigned long riscv_pte_access_flags(unsigned long cur,
+						   unsigned long entry)
+{
+	unsigned long pteval;
+	unsigned long preserved_flags;
+
+	preserved_flags = _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_SOFT_DIRTY;
+	pteval = cur & ~RISCV_PTE_ACCESS_FLAG_MASK;
+	pteval |= entry & (RISCV_PTE_ACCESS_FLAG_MASK & ~preserved_flags);
+	pteval |= (cur | entry) & preserved_flags;
+
+	return pteval;
+}
+
 int ptep_set_access_flags(struct vm_area_struct *vma,
 			  unsigned long address, pte_t *ptep,
 			  pte_t entry, int dirty)
 {
+	unsigned long old_pteval;
+	unsigned long new_pteval;
+	unsigned long prev_pteval;
+	bool changed;
+
+	old_pteval = pte_val(ptep_get(ptep));
+	do {
+		new_pteval = riscv_pte_access_flags(old_pteval, pte_val(entry));
+		if (new_pteval == old_pteval)
+			break;
+
+		prev_pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval,
+					      new_pteval);
+		if (prev_pteval == old_pteval)
+			break;
+
+		old_pteval = prev_pteval;
+	} while (1);
+
+	changed = old_pteval != new_pteval;
 	if (riscv_has_extension_unlikely(RISCV_ISA_EXT_SVVPTC)) {
-		if (!pte_same(ptep_get(ptep), entry)) {
-			__set_pte_at(vma->vm_mm, ptep, entry);
-			/* Here only not svadu is impacted */
+		if (changed)
 			flush_tlb_page(vma, address);
-			return true;
-		}
 
-		return false;
+		return changed;
 	}
 
-	if (!pte_same(ptep_get(ptep), entry))
-		__set_pte_at(vma->vm_mm, ptep, entry);
 	/*
 	 * update_mmu_cache will unconditionally execute, handling both
 	 * the case that the PTE changed and the spurious fault case.
@@ -32,9 +64,23 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
 bool ptep_test_and_clear_young(struct vm_area_struct *vma,
 		unsigned long address, pte_t *ptep)
 {
-	if (!pte_young(ptep_get(ptep)))
-		return false;
-	return test_and_clear_bit(_PAGE_ACCESSED_OFFSET, &pte_val(*ptep));
+	unsigned long old_pteval;
+	unsigned long new_pteval;
+	unsigned long prev_pteval;
+
+	old_pteval = pte_val(ptep_get(ptep));
+	do {
+		if (!(old_pteval & _PAGE_ACCESSED))
+			return false;
+
+		new_pteval = pte_val(pte_mkold(__pte(old_pteval)));
+		prev_pteval = cmpxchg_relaxed(&pte_val(*ptep), old_pteval,
+					      new_pteval);
+		if (prev_pteval == old_pteval)
+			return true;
+
+		old_pteval = prev_pteval;
+	} while (1);
 }
 EXPORT_SYMBOL_GPL(ptep_test_and_clear_young);
 
-- 
2.39.5


^ permalink raw reply related

* [PATCH v4 2/3] riscv: track effective hardware PTE A/D updating
From: Yunhui Cui @ 2026-06-18  6:44 UTC (permalink / raw)
  To: akpm, alex, andrew+kernel, aou, apatel, apopple, atishp,
	baolin.wang, cleger, conor+dt, cuiyunhui, debug, devicetree,
	guodong, hui.wang, krzk+dt, linux-kernel, linux-riscv,
	liu.xuemei1, namcao, nick.hu, palmer, pincheng.plct, pjw,
	qingwei.hu, ritesh.list, rmclure, robh, wangruikang, zhangchunyan,
	zong.li
In-Reply-To: <20260618064406.14508-1-cuiyunhui@bytedance.com>

Svadu being present in the ISA does not always mean that hardware PTE A/D
updating is active. When Svade and Svadu are both advertised, Svadu starts
disabled and must be enabled through SBI FWFT before the kernel can rely on
hardware A/D updates.

Track that effective runtime state with a static key. During init, enable
FWFT for all online harts before setting the key; if that fails, leave the
system in software-managed A/D mode. Secondary harts must match the global
A/D update mode before they are marked online.

The full state flow is:

                boot init
                    |
                    v
          do all CPUs have Svadu?
                    |
             +------+------+
             |             |
            no            yes
             |             |
             v             v
      hw_ad key = false   does any CPU have Svade?
      requires_fwft = false       |
             |              +-----+-----+
             |              |           |
             |             no          yes
             |              |           |
             |              v           v
             |       hw_ad key = true   requires_fwft = true
             |       requires_fwft = false       |
             |                                v
             |                         FWFT on online CPUs
             |                                |
             |                         +------+------+
             |                         |             |
             |                      success       failure
             |                         |             |
             |                         v             v
             |                hw_ad key = true   requires_fwft = false
             |                requires_fwft = true hw_ad key = false
             |                                  fallback to software A/D
             |
             +--------------+-----------+
                            |
                            v
                       hotplug CPU
                            |
                            v
          if (!hw_ad_key || !requires_fwft)
                    |
             +------+------+
             |             |
           true          false
             |             |
             v             v
          return 0    enable local FWFT
             |             |
             |       +-----+-----+
             |       |           |
             |    success     failure
             |       |           |
             v       v           v
          continue  continue  return error
             |       |           |
             v       v           v
       set_cpu_online()     do not set CPU online
             |                   |
             v                   v
          CPU online        CPU bringup fails

Thus, an online CPU never runs with an A/D update mode different from the
global kernel state.

Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
---
 arch/riscv/include/asm/cpufeature.h |   8 +++
 arch/riscv/kernel/cpufeature.c      | 101 ++++++++++++++++++++++++++--
 arch/riscv/kernel/smpboot.c         |   4 ++
 3 files changed, 107 insertions(+), 6 deletions(-)

diff --git a/arch/riscv/include/asm/cpufeature.h b/arch/riscv/include/asm/cpufeature.h
index 739fcc84bf7b2..ba3d74f6006a6 100644
--- a/arch/riscv/include/asm/cpufeature.h
+++ b/arch/riscv/include/asm/cpufeature.h
@@ -128,6 +128,14 @@ struct riscv_isa_ext_data {
 extern const struct riscv_isa_ext_data riscv_isa_ext[];
 extern const size_t riscv_isa_ext_count;
 extern bool riscv_isa_fallback;
+DECLARE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
+
+static __always_inline bool riscv_has_hw_pte_ad_updating(void)
+{
+	return static_branch_unlikely(&riscv_hw_pte_ad_updating);
+}
+
+int riscv_enable_hw_pte_ad_updating(void);
 
 unsigned long riscv_isa_extension_base(const unsigned long *isa_bitmap);
 static __always_inline bool riscv_cpu_has_extension_likely(int cpu, const unsigned long ext)
diff --git a/arch/riscv/kernel/cpufeature.c b/arch/riscv/kernel/cpufeature.c
index f46aa5602d74d..c0ac7ab39d4e0 100644
--- a/arch/riscv/kernel/cpufeature.c
+++ b/arch/riscv/kernel/cpufeature.c
@@ -15,6 +15,7 @@
 #include <linux/memory.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/smp.h>
 #include <asm/acpi.h>
 #include <asm/alternative.h>
 #include <asm/bugs.h>
@@ -35,6 +36,9 @@
 static bool any_cpu_has_zicboz;
 static bool any_cpu_has_zicbop;
 static bool any_cpu_has_zicbom;
+DEFINE_STATIC_KEY_FALSE(riscv_hw_pte_ad_updating);
+EXPORT_SYMBOL_GPL(riscv_hw_pte_ad_updating);
+static bool riscv_hw_pte_ad_updating_requires_fwft __read_mostly;
 
 unsigned long elf_hwcap __read_mostly;
 
@@ -287,15 +291,100 @@ static int riscv_ext_zvfbfwma_validate(const struct riscv_isa_ext_data *data,
 	return -EPROBE_DEFER;
 }
 
-static int riscv_ext_svadu_validate(const struct riscv_isa_ext_data *data,
-				    const unsigned long *isa_bitmap)
+static void riscv_set_hw_pte_ad_updating(void)
+{
+	static_branch_enable(&riscv_hw_pte_ad_updating);
+}
+
+static int riscv_enable_local_hw_pte_ad_updating(void)
 {
-	/* SVADE has already been detected, use SVADE only */
-	if (__riscv_isa_extension_available(isa_bitmap, RISCV_ISA_EXT_SVADE))
-		return -EOPNOTSUPP;
+	return sbi_fwft_set(SBI_FWFT_PTE_AD_HW_UPDATING, 1, 0);
+}
+
+static int riscv_set_online_hw_pte_ad_updating(bool enable)
+{
+	return sbi_fwft_set_online_cpus(SBI_FWFT_PTE_AD_HW_UPDATING,
+					   enable, 0);
+}
+
+static bool __init riscv_any_cpu_has_svade(void)
+{
+	unsigned int cpu;
 
+	for_each_possible_cpu(cpu) {
+		if (riscv_cpu_has_extension_unlikely(cpu, RISCV_ISA_EXT_SVADE))
+			return true;
+	}
+
+	return false;
+}
+
+int riscv_enable_hw_pte_ad_updating(void)
+{
+	unsigned int cpu;
+	int ret;
+
+	if (!riscv_has_hw_pte_ad_updating() ||
+	    !riscv_hw_pte_ad_updating_requires_fwft)
+		return 0;
+
+	cpu = smp_processor_id();
+	ret = riscv_enable_local_hw_pte_ad_updating();
+	if (ret)
+		pr_err("CPU%u failed to enable hardware PTE A/D updating: %d\n",
+		       cpu, ret);
+
+	return ret;
+}
+
+static void __init riscv_disable_hw_pte_ad_updating(int error)
+{
+	int ret;
+
+	riscv_hw_pte_ad_updating_requires_fwft = false;
+	if (error != -EOPNOTSUPP)
+		pr_err("Failed to enable hardware PTE A/D updating: %d\n",
+		       error);
+
+	ret = riscv_set_online_hw_pte_ad_updating(false);
+	if (ret && ret != -EOPNOTSUPP)
+		pr_err("Failed to rollback hardware PTE A/D updating: %d\n",
+		       ret);
+
+	pr_info("riscv: leave PTE A/D updates software-managed (%d)\n",
+		error);
+}
+
+static int __init riscv_hw_pte_ad_updating_init(void)
+{
+	bool requires_fwft, has_svadu;
+	int ret;
+
+	has_svadu = riscv_has_extension_unlikely(RISCV_ISA_EXT_SVADU);
+	requires_fwft = riscv_any_cpu_has_svade();
+
+	if (!has_svadu)
+		return 0;
+
+	if (requires_fwft) {
+		riscv_hw_pte_ad_updating_requires_fwft = true;
+		ret = riscv_set_online_hw_pte_ad_updating(true);
+		if (ret) {
+			riscv_disable_hw_pte_ad_updating(ret);
+			return 0;
+		}
+	}
+
+	/*
+	 * At this point hardware PTE A/D updating is active for all online
+	 * harts, either from boot or from the FWFT setup above. Later harts
+	 * must do the same in secondary startup before they are marked online.
+	 */
+	riscv_set_hw_pte_ad_updating();
+	pr_debug("riscv: hardware PTE A/D updating enabled\n");
 	return 0;
 }
+arch_initcall(riscv_hw_pte_ad_updating_init);
 
 static int riscv_cfilp_validate(const struct riscv_isa_ext_data *data,
 				const unsigned long *isa_bitmap)
@@ -584,7 +673,7 @@ const struct riscv_isa_ext_data riscv_isa_ext[] = {
 	__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),
-	__RISCV_ISA_EXT_DATA_VALIDATE(svadu, RISCV_ISA_EXT_SVADU, riscv_ext_svadu_validate),
+	__RISCV_ISA_EXT_DATA(svadu, RISCV_ISA_EXT_SVADU),
 	__RISCV_ISA_EXT_DATA(svinval, RISCV_ISA_EXT_SVINVAL),
 	__RISCV_ISA_EXT_DATA(svnapot, RISCV_ISA_EXT_SVNAPOT),
 	__RISCV_ISA_EXT_DATA(svpbmt, RISCV_ISA_EXT_SVPBMT),
diff --git a/arch/riscv/kernel/smpboot.c b/arch/riscv/kernel/smpboot.c
index 8b628580fe118..4fe62f96bcca2 100644
--- a/arch/riscv/kernel/smpboot.c
+++ b/arch/riscv/kernel/smpboot.c
@@ -27,6 +27,7 @@
 #include <linux/sched/mm.h>
 
 #include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
 #include <asm/cpu_ops.h>
 #include <asm/irq.h>
 #include <asm/mmu_context.h>
@@ -221,6 +222,9 @@ asmlinkage __visible void smp_callin(void)
 	struct mm_struct *mm = &init_mm;
 	unsigned int curr_cpuid = smp_processor_id();
 
+	if (riscv_enable_hw_pte_ad_updating())
+		return;
+
 	if (has_vector()) {
 		/*
 		 * Return as early as possible so the hart with a mismatching
-- 
2.39.5


^ permalink raw reply related

* [PATCH v4 1/3] dt-bindings: riscv: describe Svadu as disabled at boot
From: Yunhui Cui @ 2026-06-18  6:44 UTC (permalink / raw)
  To: akpm, alex, andrew+kernel, aou, apatel, apopple, atishp,
	baolin.wang, cleger, conor+dt, cuiyunhui, debug, devicetree,
	guodong, hui.wang, krzk+dt, linux-kernel, linux-riscv,
	liu.xuemei1, namcao, nick.hu, palmer, pincheng.plct, pjw,
	qingwei.hu, ritesh.list, rmclure, robh, wangruikang, zhangchunyan,
	zong.li
In-Reply-To: <20260618064406.14508-1-cuiyunhui@bytedance.com>

When both Svade and Svadu are advertised, Svadu is not active at boot and
must be enabled through SBI FWFT before supervisor software can rely on
hardware PTE A/D updates.

Use "disabled" instead of "turned-off" to describe that boot-time state.
This matches the FWFT terminology more closely and avoids the informal
"turned-off" wording without changing the binding semantics.

Signed-off-by: Yunhui Cui <cuiyunhui@bytedance.com>
Reviewed-by: Qingwei Hu <qingwei.hu@bytedance.com>
---
 Documentation/devicetree/bindings/riscv/extensions.yaml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Documentation/devicetree/bindings/riscv/extensions.yaml b/Documentation/devicetree/bindings/riscv/extensions.yaml
index 2b0a8a93bb214..f1e6b0d79486b 100644
--- a/Documentation/devicetree/bindings/riscv/extensions.yaml
+++ b/Documentation/devicetree/bindings/riscv/extensions.yaml
@@ -297,7 +297,7 @@ properties:
             3) Only Svadu present in DT => Supervisor must assume Svadu to be
                always enabled.
             4) Both Svade and Svadu present in DT => Supervisor must assume
-               Svadu turned-off at boot time. To use Svadu, supervisor must
+               Svadu is disabled at boot time. To use Svadu, supervisor must
                explicitly enable it using the SBI FWFT extension.
 
         - const: svadu
-- 
2.39.5


^ permalink raw reply related

* [PATCH v4 0/3] riscv: support effective hardware PTE A/D updates
From: Yunhui Cui @ 2026-06-18  6:44 UTC (permalink / raw)
  To: akpm, alex, andrew+kernel, aou, apatel, apopple, atishp,
	baolin.wang, cleger, conor+dt, cuiyunhui, debug, devicetree,
	guodong, hui.wang, krzk+dt, linux-kernel, linux-riscv,
	liu.xuemei1, namcao, nick.hu, palmer, pincheng.plct, pjw,
	qingwei.hu, ritesh.list, rmclure, robh, wangruikang, zhangchunyan,
	zong.li

This series makes RISC-V track hardware PTE A/D updating by the
effective runtime state instead of treating Svadu discovery alone as
enough.

When both Svade and Svadu are present, Svadu is disabled at boot and
must be enabled through SBI FWFT.  The series enables FWFT PTE A/D
hardware updating on all currently online CPUs before enabling the
global static key, and enables it for later hotplug CPUs early in
smp_callin(), before they are marked online.  If FWFT setup fails, the
kernel falls back to software-managed A/D updates.

It also makes live PTE access/permission updates use cmpxchg-based
merges so that software updates do not lose concurrently
hardware-updated accessed, dirty, or soft-dirty state.

Changes since v3:
- Keep the only-Svadu binding contract as always enabled, and only
  describe the Svade+Svadu boot state as disabled instead of turned-off.
- Detect the need for FWFT from per-CPU Svade state instead of the global
  ISA intersection, so asymmetric Svade/Svadu systems do not skip FWFT.
- Document the secondary-hart FWFT bringup state flow in the commit log.

Yunhui Cui (3):
  dt-bindings: riscv: describe Svadu as disabled at boot
  riscv: track effective hardware PTE A/D updating
  riscv: preserve A/D and soft-dirty state across PTE updates

 .../devicetree/bindings/riscv/extensions.yaml |   2 +-
 arch/riscv/include/asm/cpufeature.h           |   8 ++
 arch/riscv/include/asm/pgtable.h              |  27 +++--
 arch/riscv/kernel/cpufeature.c                | 101 ++++++++++++++++--
 arch/riscv/kernel/smpboot.c                   |   4 +
 arch/riscv/mm/pgtable.c                       |  68 ++++++++++--
 6 files changed, 185 insertions(+), 25 deletions(-)

-- 
2.39.5

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox