]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 6.12
authorSasha Levin <sashal@kernel.org>
Thu, 3 Jul 2025 01:51:49 +0000 (21:51 -0400)
committerSasha Levin <sashal@kernel.org>
Thu, 3 Jul 2025 01:51:49 +0000 (21:51 -0400)
Signed-off-by: Sasha Levin <sashal@kernel.org>
27 files changed:
queue-6.12/alsa-hda-realtek-bass-speaker-fixup-for-asus-um5606k.patch [new file with mode: 0644]
queue-6.12/arm64-dts-rockchip-add-avdd-hdmi-supplies-to-rockpro.patch [new file with mode: 0644]
queue-6.12/btrfs-do-proper-folio-cleanup-when-cow_file_range-fa.patch [new file with mode: 0644]
queue-6.12/btrfs-do-regular-iput-instead-of-delayed-iput-during.patch [new file with mode: 0644]
queue-6.12/btrfs-fix-use-after-free-on-inode-when-scanning-root.patch [new file with mode: 0644]
queue-6.12/btrfs-make-the-extent-map-shrinker-run-asynchronousl.patch [new file with mode: 0644]
queue-6.12/btrfs-skip-inodes-without-loaded-extent-maps-when-sh.patch [new file with mode: 0644]
queue-6.12/btrfs-zoned-fix-extent-range-end-unlock-in-cow_file_.patch [new file with mode: 0644]
queue-6.12/drm-amdkfd-fix-instruction-hazard-in-gfx12-trap-hand.patch [new file with mode: 0644]
queue-6.12/drm-amdkfd-remove-gfx-12-trap-handler-page-size-cap.patch [new file with mode: 0644]
queue-6.12/drm-fbdev-dma-add-shadow-buffering-for-deferred-i-o.patch [new file with mode: 0644]
queue-6.12/drm-msm-dp-account-for-widebus-and-yuv420-during-mod.patch [new file with mode: 0644]
queue-6.12/drm-xe-carve-out-wopcm-portion-from-the-stolen-memor.patch [new file with mode: 0644]
queue-6.12/iio-dac-ad3552r-changes-to-use-field_prep.patch [new file with mode: 0644]
queue-6.12/iio-dac-ad3552r-common-fix-ad3541-2r-ranges.patch [new file with mode: 0644]
queue-6.12/iio-dac-ad3552r-extract-common-code-no-changes-in-be.patch [new file with mode: 0644]
queue-6.12/kvm-arm64-set-hcr_el2.tid1-unconditionally.patch [new file with mode: 0644]
queue-6.12/loongarch-set-hugetlb-mmap-base-address-aligned-with.patch [new file with mode: 0644]
queue-6.12/net-stmmac-fix-accessing-freed-irq-affinity_hint.patch [new file with mode: 0644]
queue-6.12/riscv-atomic-do-proper-sign-extension-also-for-unsig.patch [new file with mode: 0644]
queue-6.12/series
queue-6.12/spi-fsl-qspi-fix-double-cleanup-in-probe-error-path.patch [new file with mode: 0644]
queue-6.12/spi-fsl-qspi-support-per-spi-mem-operation-frequency.patch [new file with mode: 0644]
queue-6.12/spi-fsl-qspi-use-devm-function-instead-of-driver-rem.patch [new file with mode: 0644]
queue-6.12/spi-spi-mem-add-a-new-controller-capability.patch [new file with mode: 0644]
queue-6.12/spi-spi-mem-extend-spi-mem-operations-with-a-per-ope.patch [new file with mode: 0644]
queue-6.12/usb-typec-tcpm-pssourceofftimer-timeout-in-pr_swap-e.patch [new file with mode: 0644]

diff --git a/queue-6.12/alsa-hda-realtek-bass-speaker-fixup-for-asus-um5606k.patch b/queue-6.12/alsa-hda-realtek-bass-speaker-fixup-for-asus-um5606k.patch
new file mode 100644 (file)
index 0000000..5316241
--- /dev/null
@@ -0,0 +1,43 @@
+From 0c570a043c69df1cc34b0cf32839d7ed57d9940a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 25 Mar 2025 17:25:35 +0700
+Subject: ALSA: hda/realtek: Bass speaker fixup for ASUS UM5606KA
+
+From: Andres Traumann <andres.traumann.01@gmail.com>
+
+[ Upstream commit be8cd366beb80c709adbc7688ee72750f5aee3ff ]
+
+This patch applies the ALC294 bass speaker fixup (ALC294_FIXUP_BASS_SPEAKER_15),
+previously introduced in commit a7df7f909cec ("ALSA: hda: improve bass
+speaker support for ASUS Zenbook UM5606WA"), to the ASUS Zenbook UM5606KA.
+This hardware configuration matches ASUS Zenbook UM5606WA, where DAC NID
+0x06 was removed from the bass speaker (NID 0x15), routing both speaker
+pins to DAC NID 0x03.
+
+This resolves the bass speaker routing issue, ensuring correct audio
+output on ASUS UM5606KA.
+
+Signed-off-by: Andres Traumann <andres.traumann.01@gmail.com>
+Cc: <stable@vger.kernel.org>
+Link: https://patch.msgid.link/20250325102535.8172-1-andres.traumann.01@gmail.com
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/pci/hda/patch_realtek.c | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 94c5151c456d6..30e9e26c5b2a7 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -10859,6 +10859,7 @@ static const struct hda_quirk alc269_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x1043, 0x1204, "ASUS Strix G615JHR_JMR_JPR", ALC287_FIXUP_TAS2781_I2C),
+       SND_PCI_QUIRK(0x1043, 0x1214, "ASUS Strix G615LH_LM_LP", ALC287_FIXUP_TAS2781_I2C),
+       SND_PCI_QUIRK(0x1043, 0x125e, "ASUS Q524UQK", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
++      SND_PCI_QUIRK(0x1043, 0x1264, "ASUS UM5606KA", ALC294_FIXUP_BASS_SPEAKER_15),
+       SND_PCI_QUIRK(0x1043, 0x1271, "ASUS X430UN", ALC256_FIXUP_ASUS_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
+       SND_PCI_QUIRK(0x1043, 0x1294, "ASUS B3405CVA", ALC245_FIXUP_CS35L41_SPI_2),
+-- 
+2.39.5
+
diff --git a/queue-6.12/arm64-dts-rockchip-add-avdd-hdmi-supplies-to-rockpro.patch b/queue-6.12/arm64-dts-rockchip-add-avdd-hdmi-supplies-to-rockpro.patch
new file mode 100644 (file)
index 0000000..46d8526
--- /dev/null
@@ -0,0 +1,74 @@
+From 6a1b00a5ef9f98fd2bf1a882f2c1686fae759295 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 2 Mar 2025 19:48:03 +0100
+Subject: arm64: dts: rockchip: Add avdd HDMI supplies to RockPro64 board dtsi
+
+From: Dragan Simic <dsimic@manjaro.org>
+
+[ Upstream commit bd1c959f37f384b477f51572331b0dc828bd009a ]
+
+Add missing "avdd-0v9-supply" and "avdd-1v8-supply" properties to the "hdmi"
+node in the Pine64 RockPro64 board dtsi file.  To achieve this, also add the
+associated "vcca_0v9" regulator that produces the 0.9 V supply, [1][2] which
+hasn't been defined previously in the board dtsi file.
+
+This also eliminates the following warnings from the kernel log:
+
+  dwhdmi-rockchip ff940000.hdmi: supply avdd-0v9 not found, using dummy regulator
+  dwhdmi-rockchip ff940000.hdmi: supply avdd-1v8 not found, using dummy regulator
+
+There are no functional changes to the way board works with these additions,
+because the "vcc1v8_dvp" and "vcca_0v9" regulators are always enabled, [1][2]
+but these additions improve the accuracy of hardware description.
+
+These changes apply to the both supported hardware revisions of the Pine64
+RockPro64, i.e. to the production-run revisions 2.0 and 2.1. [1][2]
+
+[1] https://files.pine64.org/doc/rockpro64/rockpro64_v21-SCH.pdf
+[2] https://files.pine64.org/doc/rockpro64/rockpro64_v20-SCH.pdf
+
+Fixes: e4f3fb490967 ("arm64: dts: rockchip: add initial dts support for Rockpro64")
+Cc: stable@vger.kernel.org
+Suggested-by: Diederik de Haas <didi.debian@cknow.org>
+Signed-off-by: Dragan Simic <dsimic@manjaro.org>
+Tested-by: Diederik de Haas <didi.debian@cknow.org>
+Link: https://lore.kernel.org/r/df3d7e8fe74ed5e727e085b18c395260537bb5ac.1740941097.git.dsimic@manjaro.org
+Signed-off-by: Heiko Stuebner <heiko@sntech.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi
+index 11d99d8b34a2b..66d010a9e8c31 100644
+--- a/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi
++++ b/arch/arm64/boot/dts/rockchip/rk3399-rockpro64.dtsi
+@@ -227,6 +227,16 @@ vcc5v0_usb: vcc5v0-usb {
+               vin-supply = <&vcc12v_dcin>;
+       };
++      vcca_0v9: vcca-0v9 {
++              compatible = "regulator-fixed";
++              regulator-name = "vcca_0v9";
++              regulator-always-on;
++              regulator-boot-on;
++              regulator-min-microvolt = <900000>;
++              regulator-max-microvolt = <900000>;
++              vin-supply = <&vcc3v3_sys>;
++      };
++
+       vdd_log: vdd-log {
+               compatible = "pwm-regulator";
+               pwms = <&pwm2 0 25000 1>;
+@@ -312,6 +322,8 @@ &gmac {
+ };
+ &hdmi {
++      avdd-0v9-supply = <&vcca_0v9>;
++      avdd-1v8-supply = <&vcc1v8_dvp>;
+       ddc-i2c-bus = <&i2c3>;
+       pinctrl-names = "default";
+       pinctrl-0 = <&hdmi_cec>;
+-- 
+2.39.5
+
diff --git a/queue-6.12/btrfs-do-proper-folio-cleanup-when-cow_file_range-fa.patch b/queue-6.12/btrfs-do-proper-folio-cleanup-when-cow_file_range-fa.patch
new file mode 100644 (file)
index 0000000..7ad39a9
--- /dev/null
@@ -0,0 +1,286 @@
+From eeee66aacff2587599644989ed599a608d06cede Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 12 Dec 2024 16:43:58 +1030
+Subject: btrfs: do proper folio cleanup when cow_file_range() failed
+
+From: Qu Wenruo <wqu@suse.com>
+
+[ Upstream commit 06f364284794f149d2abc167c11d556cf20c954b ]
+
+[BUG]
+When testing with COW fixup marked as BUG_ON() (this is involved with the
+new pin_user_pages*() change, which should not result new out-of-band
+dirty pages), I hit a crash triggered by the BUG_ON() from hitting COW
+fixup path.
+
+This BUG_ON() happens just after a failed btrfs_run_delalloc_range():
+
+  BTRFS error (device dm-2): failed to run delalloc range, root 348 ino 405 folio 65536 submit_bitmap 6-15 start 90112 len 106496: -28
+  ------------[ cut here ]------------
+  kernel BUG at fs/btrfs/extent_io.c:1444!
+  Internal error: Oops - BUG: 00000000f2000800 [#1] SMP
+  CPU: 0 UID: 0 PID: 434621 Comm: kworker/u24:8 Tainted: G           OE      6.12.0-rc7-custom+ #86
+  Hardware name: QEMU KVM Virtual Machine, BIOS unknown 2/2/2022
+  Workqueue: events_unbound btrfs_async_reclaim_data_space [btrfs]
+  pc : extent_writepage_io+0x2d4/0x308 [btrfs]
+  lr : extent_writepage_io+0x2d4/0x308 [btrfs]
+  Call trace:
+   extent_writepage_io+0x2d4/0x308 [btrfs]
+   extent_writepage+0x218/0x330 [btrfs]
+   extent_write_cache_pages+0x1d4/0x4b0 [btrfs]
+   btrfs_writepages+0x94/0x150 [btrfs]
+   do_writepages+0x74/0x190
+   filemap_fdatawrite_wbc+0x88/0xc8
+   start_delalloc_inodes+0x180/0x3b0 [btrfs]
+   btrfs_start_delalloc_roots+0x174/0x280 [btrfs]
+   shrink_delalloc+0x114/0x280 [btrfs]
+   flush_space+0x250/0x2f8 [btrfs]
+   btrfs_async_reclaim_data_space+0x180/0x228 [btrfs]
+   process_one_work+0x164/0x408
+   worker_thread+0x25c/0x388
+   kthread+0x100/0x118
+   ret_from_fork+0x10/0x20
+  Code: aa1403e1 9402f3ef aa1403e0 9402f36f (d4210000)
+  ---[ end trace 0000000000000000 ]---
+
+[CAUSE]
+That failure is mostly from cow_file_range(), where we can hit -ENOSPC.
+
+Although the -ENOSPC is already a bug related to our space reservation
+code, let's just focus on the error handling.
+
+For example, we have the following dirty range [0, 64K) of an inode,
+with 4K sector size and 4K page size:
+
+   0        16K        32K       48K       64K
+   |///////////////////////////////////////|
+   |#######################################|
+
+Where |///| means page are still dirty, and |###| means the extent io
+tree has EXTENT_DELALLOC flag.
+
+- Enter extent_writepage() for page 0
+
+- Enter btrfs_run_delalloc_range() for range [0, 64K)
+
+- Enter cow_file_range() for range [0, 64K)
+
+- Function btrfs_reserve_extent() only reserved one 16K extent
+  So we created extent map and ordered extent for range [0, 16K)
+
+   0        16K        32K       48K       64K
+   |////////|//////////////////////////////|
+   |<- OE ->|##############################|
+
+   And range [0, 16K) has its delalloc flag cleared.
+   But since we haven't yet submit any bio, involved 4 pages are still
+   dirty.
+
+- Function btrfs_reserve_extent() returns with -ENOSPC
+  Now we have to run error cleanup, which will clear all
+  EXTENT_DELALLOC* flags and clear the dirty flags for the remaining
+  ranges:
+
+   0        16K        32K       48K       64K
+   |////////|                              |
+   |        |                              |
+
+  Note that range [0, 16K) still has its pages dirty.
+
+- Some time later, writeback is triggered again for the range [0, 16K)
+  since the page range still has dirty flags.
+
+- btrfs_run_delalloc_range() will do nothing because there is no
+  EXTENT_DELALLOC flag.
+
+- extent_writepage_io() finds page 0 has no ordered flag
+  Which falls into the COW fixup path, triggering the BUG_ON().
+
+Unfortunately this error handling bug dates back to the introduction of
+btrfs.  Thankfully with the abuse of COW fixup, at least it won't crash
+the kernel.
+
+[FIX]
+Instead of immediately unlocking the extent and folios, we keep the extent
+and folios locked until either erroring out or the whole delalloc range
+finished.
+
+When the whole delalloc range finished without error, we just unlock the
+whole range with PAGE_SET_ORDERED (and PAGE_UNLOCK for !keep_locked
+cases), with EXTENT_DELALLOC and EXTENT_LOCKED cleared.
+And the involved folios will be properly submitted, with their dirty
+flags cleared during submission.
+
+For the error path, it will be a little more complex:
+
+- The range with ordered extent allocated (range (1))
+  We only clear the EXTENT_DELALLOC and EXTENT_LOCKED, as the remaining
+  flags are cleaned up by
+  btrfs_mark_ordered_io_finished()->btrfs_finish_one_ordered().
+
+  For folios we finish the IO (clear dirty, start writeback and
+  immediately finish the writeback) and unlock the folios.
+
+- The range with reserved extent but no ordered extent (range(2))
+- The range we never touched (range(3))
+  For both range (2) and range(3) the behavior is not changed.
+
+Now even if cow_file_range() failed halfway with some successfully
+reserved extents/ordered extents, we will keep all folios clean, so
+there will be no future writeback triggered on them.
+
+CC: stable@vger.kernel.org
+Reviewed-by: Boris Burkov <boris@bur.io>
+Signed-off-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/inode.c | 77 +++++++++++++++++++++++-------------------------
+ 1 file changed, 37 insertions(+), 40 deletions(-)
+
+diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
+index cc6a350ae6ede..2f0b2cb2ae6e8 100644
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -1408,6 +1408,17 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
+       alloc_hint = btrfs_get_extent_allocation_hint(inode, start, num_bytes);
++      /*
++       * We're not doing compressed IO, don't unlock the first page (which
++       * the caller expects to stay locked), don't clear any dirty bits and
++       * don't set any writeback bits.
++       *
++       * Do set the Ordered (Private2) bit so we know this page was properly
++       * setup for writepage.
++       */
++      page_ops = (keep_locked ? 0 : PAGE_UNLOCK);
++      page_ops |= PAGE_SET_ORDERED;
++
+       /*
+        * Relocation relies on the relocated extents to have exactly the same
+        * size as the original extents. Normally writeback for relocation data
+@@ -1470,6 +1481,10 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
+               file_extent.offset = 0;
+               file_extent.compression = BTRFS_COMPRESS_NONE;
++              /*
++               * Locked range will be released either during error clean up or
++               * after the whole range is finished.
++               */
+               lock_extent(&inode->io_tree, start, start + ram_size - 1,
+                           &cached);
+@@ -1515,27 +1530,12 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
+               btrfs_dec_block_group_reservations(fs_info, ins.objectid);
+-              /*
+-               * We're not doing compressed IO, don't unlock the first page
+-               * (which the caller expects to stay locked), don't clear any
+-               * dirty bits and don't set any writeback bits
+-               *
+-               * Do set the Ordered (Private2) bit so we know this page was
+-               * properly setup for writepage.
+-               */
+-              page_ops = (keep_locked ? 0 : PAGE_UNLOCK);
+-              page_ops |= PAGE_SET_ORDERED;
+-
+-              extent_clear_unlock_delalloc(inode, start, start + ram_size - 1,
+-                                           locked_folio, &cached,
+-                                           EXTENT_LOCKED | EXTENT_DELALLOC,
+-                                           page_ops);
+-              if (num_bytes < cur_alloc_size)
++              if (num_bytes < ram_size)
+                       num_bytes = 0;
+               else
+-                      num_bytes -= cur_alloc_size;
++                      num_bytes -= ram_size;
+               alloc_hint = ins.objectid + ins.offset;
+-              start += cur_alloc_size;
++              start += ram_size;
+               extent_reserved = false;
+               /*
+@@ -1546,6 +1546,8 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
+               if (ret)
+                       goto out_unlock;
+       }
++      extent_clear_unlock_delalloc(inode, orig_start, end, locked_folio, &cached,
++                                   EXTENT_LOCKED | EXTENT_DELALLOC, page_ops);
+ done:
+       if (done_offset)
+               *done_offset = end;
+@@ -1561,40 +1563,35 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
+        * Now, we have three regions to clean up:
+        *
+        * |-------(1)----|---(2)---|-------------(3)----------|
+-       * `- orig_start  `- start  `- start + cur_alloc_size  `- end
++       * `- orig_start  `- start  `- start + ram_size  `- end
+        *
+        * We process each region below.
+        */
+-      clear_bits = EXTENT_LOCKED | EXTENT_DELALLOC | EXTENT_DELALLOC_NEW |
+-              EXTENT_DEFRAG | EXTENT_CLEAR_META_RESV;
+-      page_ops = PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK;
+-
+       /*
+        * For the range (1). We have already instantiated the ordered extents
+        * for this region. They are cleaned up by
+        * btrfs_cleanup_ordered_extents() in e.g,
+-       * btrfs_run_delalloc_range(). EXTENT_LOCKED | EXTENT_DELALLOC are
+-       * already cleared in the above loop. And, EXTENT_DELALLOC_NEW |
+-       * EXTENT_DEFRAG | EXTENT_CLEAR_META_RESV are handled by the cleanup
+-       * function.
++       * btrfs_run_delalloc_range().
++       * EXTENT_DELALLOC_NEW | EXTENT_DEFRAG | EXTENT_CLEAR_META_RESV
++       * are also handled by the cleanup function.
+        *
+-       * However, in case of @keep_locked, we still need to unlock the pages
+-       * (except @locked_folio) to ensure all the pages are unlocked.
++       * So here we only clear EXTENT_LOCKED and EXTENT_DELALLOC flag, and
++       * finish the writeback of the involved folios, which will be never submitted.
+        */
+-      if (keep_locked && orig_start < start) {
++      if (orig_start < start) {
++              clear_bits = EXTENT_LOCKED | EXTENT_DELALLOC;
++              page_ops = PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK;
++
+               if (!locked_folio)
+                       mapping_set_error(inode->vfs_inode.i_mapping, ret);
+               extent_clear_unlock_delalloc(inode, orig_start, start - 1,
+-                                           locked_folio, NULL, 0, page_ops);
++                                           locked_folio, NULL, clear_bits, page_ops);
+       }
+-      /*
+-       * At this point we're unlocked, we want to make sure we're only
+-       * clearing these flags under the extent lock, so lock the rest of the
+-       * range and clear everything up.
+-       */
+-      lock_extent(&inode->io_tree, start, end, NULL);
++      clear_bits = EXTENT_LOCKED | EXTENT_DELALLOC | EXTENT_DELALLOC_NEW |
++                   EXTENT_DEFRAG | EXTENT_CLEAR_META_RESV;
++      page_ops = PAGE_UNLOCK | PAGE_START_WRITEBACK | PAGE_END_WRITEBACK;
+       /*
+        * For the range (2). If we reserved an extent for our delalloc range
+@@ -1608,11 +1605,11 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
+        */
+       if (extent_reserved) {
+               extent_clear_unlock_delalloc(inode, start,
+-                                           start + cur_alloc_size - 1,
++                                           start + ram_size - 1,
+                                            locked_folio, &cached, clear_bits,
+                                            page_ops);
+-              btrfs_qgroup_free_data(inode, NULL, start, cur_alloc_size, NULL);
+-              start += cur_alloc_size;
++              btrfs_qgroup_free_data(inode, NULL, start, ram_size, NULL);
++              start += ram_size;
+       }
+       /*
+-- 
+2.39.5
+
diff --git a/queue-6.12/btrfs-do-regular-iput-instead-of-delayed-iput-during.patch b/queue-6.12/btrfs-do-regular-iput-instead-of-delayed-iput-during.patch
new file mode 100644 (file)
index 0000000..e154897
--- /dev/null
@@ -0,0 +1,47 @@
+From be5739ee406bd78b101a05930003c47cc18927bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 15 Feb 2025 11:11:29 +0000
+Subject: btrfs: do regular iput instead of delayed iput during extent map
+ shrinking
+
+From: Filipe Manana <fdmanana@suse.com>
+
+[ Upstream commit 15b3b3254d1453a8db038b7d44b311a2d6c71f98 ]
+
+The extent map shrinker now runs in the system unbound workqueue and no
+longer in kswapd context so it can directly do an iput() on inodes even
+if that blocks or needs to acquire any lock (we aren't holding any locks
+when requesting the delayed iput from the shrinker). So we don't need to
+add a delayed iput, wake up the cleaner and delegate the iput() to the
+cleaner, which also adds extra contention on the spinlock that protects
+the delayed iputs list.
+
+Reported-by: Ivan Shapovalov <intelfx@intelfx.name>
+Tested-by: Ivan Shapovalov <intelfx@intelfx.name>
+Link: https://lore.kernel.org/linux-btrfs/0414d690ac5680d0d77dfc930606cdc36e42e12f.camel@intelfx.name/
+CC: stable@vger.kernel.org # 6.12+
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/extent_map.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
+index 61477cb69a6fd..93043edc8ff93 100644
+--- a/fs/btrfs/extent_map.c
++++ b/fs/btrfs/extent_map.c
+@@ -1261,7 +1261,7 @@ static long btrfs_scan_root(struct btrfs_root *root, struct btrfs_em_shrink_ctx
+               min_ino = btrfs_ino(inode) + 1;
+               ctx->last_ino = btrfs_ino(inode);
+-              btrfs_add_delayed_iput(inode);
++              iput(&inode->vfs_inode);
+               if (ctx->scanned >= ctx->nr_to_scan ||
+                   btrfs_fs_closing(inode->root->fs_info))
+-- 
+2.39.5
+
diff --git a/queue-6.12/btrfs-fix-use-after-free-on-inode-when-scanning-root.patch b/queue-6.12/btrfs-fix-use-after-free-on-inode-when-scanning-root.patch
new file mode 100644 (file)
index 0000000..a27a16b
--- /dev/null
@@ -0,0 +1,54 @@
+From 5d075b4c9c1810fc58c42a4fa5292e74ba3f5c64 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Jul 2025 19:02:58 -0400
+Subject: btrfs: fix use-after-free on inode when scanning root during em
+ shrinking
+
+[ Upstream commit 59f37036bb7ab3d554c24abc856aabca01126414 ]
+
+At btrfs_scan_root() we are accessing the inode's root (and fs_info) in a
+call to btrfs_fs_closing() after we have scheduled the inode for a delayed
+iput, and that can result in a use-after-free on the inode in case the
+cleaner kthread does the iput before we dereference the inode in the call
+to btrfs_fs_closing().
+
+Fix this by using the fs_info stored already in a local variable instead
+of doing inode->root->fs_info.
+
+Fixes: 102044384056 ("btrfs: make the extent map shrinker run asynchronously as a work queue job")
+CC: stable@vger.kernel.org # 6.13+
+Tested-by: Ivan Shapovalov <intelfx@intelfx.name>
+Link: https://lore.kernel.org/linux-btrfs/0414d690ac5680d0d77dfc930606cdc36e42e12f.camel@intelfx.name/
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/extent_map.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
+index 93043edc8ff93..36af9aa9aab13 100644
+--- a/fs/btrfs/extent_map.c
++++ b/fs/btrfs/extent_map.c
+@@ -1250,6 +1250,7 @@ static struct btrfs_inode *find_first_inode_to_shrink(struct btrfs_root *root,
+ static long btrfs_scan_root(struct btrfs_root *root, struct btrfs_em_shrink_ctx *ctx)
+ {
++      struct btrfs_fs_info *fs_info = root->fs_info;
+       struct btrfs_inode *inode;
+       long nr_dropped = 0;
+       u64 min_ino = ctx->last_ino + 1;
+@@ -1264,7 +1265,7 @@ static long btrfs_scan_root(struct btrfs_root *root, struct btrfs_em_shrink_ctx
+               iput(&inode->vfs_inode);
+               if (ctx->scanned >= ctx->nr_to_scan ||
+-                  btrfs_fs_closing(inode->root->fs_info))
++                  btrfs_fs_closing(fs_info))
+                       break;
+               cond_resched();
+-- 
+2.39.5
+
diff --git a/queue-6.12/btrfs-make-the-extent-map-shrinker-run-asynchronousl.patch b/queue-6.12/btrfs-make-the-extent-map-shrinker-run-asynchronousl.patch
new file mode 100644 (file)
index 0000000..cff2e2e
--- /dev/null
@@ -0,0 +1,243 @@
+From ff155cff3944f92ed4eebed0e2d6f093415a2126 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 29 Aug 2024 15:23:32 +0100
+Subject: btrfs: make the extent map shrinker run asynchronously as a work
+ queue job
+
+From: Filipe Manana <fdmanana@suse.com>
+
+[ Upstream commit 1020443840569535f6025a855958f07ea3eebf71 ]
+
+Currently the extent map shrinker is run synchronously for kswapd tasks
+that end up calling the fs shrinker (fs/super.c:super_cache_scan()).
+This has some disadvantages and for some heavy workloads with memory
+pressure it can cause some delays and stalls that make a machine
+unresponsive for some periods. This happens because:
+
+1) We can have several kswapd tasks on machines with multiple NUMA zones,
+   and running the extent map shrinker concurrently can cause high
+   contention on some spin locks, namely the spin locks that protect
+   the radix tree that tracks roots, the per root xarray that tracks
+   open inodes and the list of delayed iputs. This not only delays the
+   shrinker but also causes high CPU consumption and makes the task
+   running the shrinker monopolize a core, resulting in the symptoms
+   of an unresponsive system. This was noted in previous commits such as
+   commit ae1e766f623f ("btrfs: only run the extent map shrinker from
+   kswapd tasks");
+
+2) The extent map shrinker's iteration over inodes can often be slow, even
+   after changing the data structure that tracks open inodes for a root
+   from a red black tree (up to kernel 6.10) to an xarray (kernel 6.10+).
+   The transition to the xarray while it made things a bit faster, it's
+   still somewhat slow - for example in a test scenario with 10000 inodes
+   that have no extent maps loaded, the extent map shrinker took between
+   5ms to 8ms, using a release, non-debug kernel. Iterating over the
+   extent maps of an inode can also be slow if have an inode with many
+   thousands of extent maps, since we use a red black tree to track and
+   search extent maps. So having the extent map shrinker run synchronously
+   adds extra delay for other things a kswapd task does.
+
+So make the extent map shrinker run asynchronously as a job for the
+system unbounded workqueue, just like what we do for data and metadata
+space reclaim jobs.
+
+Reviewed-by: Josef Bacik <josef@toxicpanda.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/disk-io.c    |  2 ++
+ fs/btrfs/extent_map.c | 51 ++++++++++++++++++++++++++++++++++++-------
+ fs/btrfs/extent_map.h |  3 ++-
+ fs/btrfs/fs.h         |  2 ++
+ fs/btrfs/super.c      | 13 +++--------
+ 5 files changed, 52 insertions(+), 19 deletions(-)
+
+diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
+index 96282bf28b19c..e655fa3bfd9be 100644
+--- a/fs/btrfs/disk-io.c
++++ b/fs/btrfs/disk-io.c
+@@ -2785,6 +2785,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
+       btrfs_init_scrub(fs_info);
+       btrfs_init_balance(fs_info);
+       btrfs_init_async_reclaim_work(fs_info);
++      btrfs_init_extent_map_shrinker_work(fs_info);
+       rwlock_init(&fs_info->block_group_cache_lock);
+       fs_info->block_group_cache_tree = RB_ROOT_CACHED;
+@@ -4334,6 +4335,7 @@ void __cold close_ctree(struct btrfs_fs_info *fs_info)
+       cancel_work_sync(&fs_info->async_reclaim_work);
+       cancel_work_sync(&fs_info->async_data_reclaim_work);
+       cancel_work_sync(&fs_info->preempt_reclaim_work);
++      cancel_work_sync(&fs_info->extent_map_shrinker_work);
+       /* Cancel or finish ongoing discard work */
+       btrfs_discard_cleanup(fs_info);
+diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
+index d67abf5f97a77..61477cb69a6fd 100644
+--- a/fs/btrfs/extent_map.c
++++ b/fs/btrfs/extent_map.c
+@@ -1128,7 +1128,8 @@ struct btrfs_em_shrink_ctx {
+ static long btrfs_scan_inode(struct btrfs_inode *inode, struct btrfs_em_shrink_ctx *ctx)
+ {
+-      const u64 cur_fs_gen = btrfs_get_fs_generation(inode->root->fs_info);
++      struct btrfs_fs_info *fs_info = inode->root->fs_info;
++      const u64 cur_fs_gen = btrfs_get_fs_generation(fs_info);
+       struct extent_map_tree *tree = &inode->extent_tree;
+       long nr_dropped = 0;
+       struct rb_node *node;
+@@ -1187,7 +1188,8 @@ static long btrfs_scan_inode(struct btrfs_inode *inode, struct btrfs_em_shrink_c
+                * lock. This is to avoid slowing other tasks trying to take the
+                * lock.
+                */
+-              if (need_resched() || rwlock_needbreak(&tree->lock))
++              if (need_resched() || rwlock_needbreak(&tree->lock) ||
++                  btrfs_fs_closing(fs_info))
+                       break;
+               node = next;
+       }
+@@ -1261,7 +1263,8 @@ static long btrfs_scan_root(struct btrfs_root *root, struct btrfs_em_shrink_ctx
+               ctx->last_ino = btrfs_ino(inode);
+               btrfs_add_delayed_iput(inode);
+-              if (ctx->scanned >= ctx->nr_to_scan)
++              if (ctx->scanned >= ctx->nr_to_scan ||
++                  btrfs_fs_closing(inode->root->fs_info))
+                       break;
+               cond_resched();
+@@ -1290,16 +1293,19 @@ static long btrfs_scan_root(struct btrfs_root *root, struct btrfs_em_shrink_ctx
+       return nr_dropped;
+ }
+-long btrfs_free_extent_maps(struct btrfs_fs_info *fs_info, long nr_to_scan)
++static void btrfs_extent_map_shrinker_worker(struct work_struct *work)
+ {
++      struct btrfs_fs_info *fs_info;
+       struct btrfs_em_shrink_ctx ctx;
+       u64 start_root_id;
+       u64 next_root_id;
+       bool cycled = false;
+       long nr_dropped = 0;
++      fs_info = container_of(work, struct btrfs_fs_info, extent_map_shrinker_work);
++
+       ctx.scanned = 0;
+-      ctx.nr_to_scan = nr_to_scan;
++      ctx.nr_to_scan = atomic64_read(&fs_info->extent_map_shrinker_nr_to_scan);
+       /*
+        * In case we have multiple tasks running this shrinker, make the next
+@@ -1317,12 +1323,12 @@ long btrfs_free_extent_maps(struct btrfs_fs_info *fs_info, long nr_to_scan)
+       if (trace_btrfs_extent_map_shrinker_scan_enter_enabled()) {
+               s64 nr = percpu_counter_sum_positive(&fs_info->evictable_extent_maps);
+-              trace_btrfs_extent_map_shrinker_scan_enter(fs_info, nr_to_scan,
++              trace_btrfs_extent_map_shrinker_scan_enter(fs_info, ctx.nr_to_scan,
+                                                          nr, ctx.last_root,
+                                                          ctx.last_ino);
+       }
+-      while (ctx.scanned < ctx.nr_to_scan) {
++      while (ctx.scanned < ctx.nr_to_scan && !btrfs_fs_closing(fs_info)) {
+               struct btrfs_root *root;
+               unsigned long count;
+@@ -1380,5 +1386,34 @@ long btrfs_free_extent_maps(struct btrfs_fs_info *fs_info, long nr_to_scan)
+                                                         ctx.last_ino);
+       }
+-      return nr_dropped;
++      atomic64_set(&fs_info->extent_map_shrinker_nr_to_scan, 0);
++}
++
++void btrfs_free_extent_maps(struct btrfs_fs_info *fs_info, long nr_to_scan)
++{
++      /*
++       * Do nothing if the shrinker is already running. In case of high memory
++       * pressure we can have a lot of tasks calling us and all passing the
++       * same nr_to_scan value, but in reality we may need only to free
++       * nr_to_scan extent maps (or less). In case we need to free more than
++       * that, we will be called again by the fs shrinker, so no worries about
++       * not doing enough work to reclaim memory from extent maps.
++       * We can also be repeatedly called with the same nr_to_scan value
++       * simply because the shrinker runs asynchronously and multiple calls
++       * to this function are made before the shrinker does enough progress.
++       *
++       * That's why we set the atomic counter to nr_to_scan only if its
++       * current value is zero, instead of incrementing the counter by
++       * nr_to_scan.
++       */
++      if (atomic64_cmpxchg(&fs_info->extent_map_shrinker_nr_to_scan, 0, nr_to_scan) != 0)
++              return;
++
++      queue_work(system_unbound_wq, &fs_info->extent_map_shrinker_work);
++}
++
++void btrfs_init_extent_map_shrinker_work(struct btrfs_fs_info *fs_info)
++{
++      atomic64_set(&fs_info->extent_map_shrinker_nr_to_scan, 0);
++      INIT_WORK(&fs_info->extent_map_shrinker_work, btrfs_extent_map_shrinker_worker);
+ }
+diff --git a/fs/btrfs/extent_map.h b/fs/btrfs/extent_map.h
+index 5154a8f1d26c9..cd123b266b641 100644
+--- a/fs/btrfs/extent_map.h
++++ b/fs/btrfs/extent_map.h
+@@ -189,6 +189,7 @@ void btrfs_drop_extent_map_range(struct btrfs_inode *inode,
+ int btrfs_replace_extent_map_range(struct btrfs_inode *inode,
+                                  struct extent_map *new_em,
+                                  bool modified);
+-long btrfs_free_extent_maps(struct btrfs_fs_info *fs_info, long nr_to_scan);
++void btrfs_free_extent_maps(struct btrfs_fs_info *fs_info, long nr_to_scan);
++void btrfs_init_extent_map_shrinker_work(struct btrfs_fs_info *fs_info);
+ #endif
+diff --git a/fs/btrfs/fs.h b/fs/btrfs/fs.h
+index bb822e425d7fa..374843aca60d8 100644
+--- a/fs/btrfs/fs.h
++++ b/fs/btrfs/fs.h
+@@ -639,6 +639,8 @@ struct btrfs_fs_info {
+       spinlock_t extent_map_shrinker_lock;
+       u64 extent_map_shrinker_last_root;
+       u64 extent_map_shrinker_last_ino;
++      atomic64_t extent_map_shrinker_nr_to_scan;
++      struct work_struct extent_map_shrinker_work;
+       /* Protected by 'trans_lock'. */
+       struct list_head dirty_cowonly_roots;
+diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
+index bcb8def4ade20..6119a06b05693 100644
+--- a/fs/btrfs/super.c
++++ b/fs/btrfs/super.c
+@@ -28,7 +28,6 @@
+ #include <linux/btrfs.h>
+ #include <linux/security.h>
+ #include <linux/fs_parser.h>
+-#include <linux/swap.h>
+ #include "messages.h"
+ #include "delayed-inode.h"
+ #include "ctree.h"
+@@ -2399,16 +2398,10 @@ static long btrfs_free_cached_objects(struct super_block *sb, struct shrink_cont
+       const long nr_to_scan = min_t(unsigned long, LONG_MAX, sc->nr_to_scan);
+       struct btrfs_fs_info *fs_info = btrfs_sb(sb);
+-      /*
+-       * We may be called from any task trying to allocate memory and we don't
+-       * want to slow it down with scanning and dropping extent maps. It would
+-       * also cause heavy lock contention if many tasks concurrently enter
+-       * here. Therefore only allow kswapd tasks to scan and drop extent maps.
+-       */
+-      if (!current_is_kswapd())
+-              return 0;
++      btrfs_free_extent_maps(fs_info, nr_to_scan);
+-      return btrfs_free_extent_maps(fs_info, nr_to_scan);
++      /* The extent map shrinker runs asynchronously, so always return 0. */
++      return 0;
+ }
+ static const struct super_operations btrfs_super_ops = {
+-- 
+2.39.5
+
diff --git a/queue-6.12/btrfs-skip-inodes-without-loaded-extent-maps-when-sh.patch b/queue-6.12/btrfs-skip-inodes-without-loaded-extent-maps-when-sh.patch
new file mode 100644 (file)
index 0000000..91401e4
--- /dev/null
@@ -0,0 +1,161 @@
+From 6f7096b21055313b16be91b99f0c4fe1a05d9449 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 15 Feb 2025 11:04:15 +0000
+Subject: btrfs: skip inodes without loaded extent maps when shrinking extent
+ maps
+
+From: Filipe Manana <fdmanana@suse.com>
+
+[ Upstream commit c6c9c4d56483d941f567eb921434c25fc6086dfa ]
+
+If there are inodes that don't have any loaded extent maps, we end up
+grabbing a reference on them and later adding a delayed iput, which wakes
+up the cleaner and makes it do unnecessary work. This is common when for
+example the inodes were open only to run stat(2) or all their extent maps
+were already released through the folio release callback
+(btrfs_release_folio()) or released by a previous run of the shrinker, or
+directories which never have extent maps.
+
+Reported-by: Ivan Shapovalov <intelfx@intelfx.name>
+Tested-by: Ivan Shapovalov <intelfx@intelfx.name>
+Link: https://lore.kernel.org/linux-btrfs/0414d690ac5680d0d77dfc930606cdc36e42e12f.camel@intelfx.name/
+CC: stable@vger.kernel.org # 6.13+
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Signed-off-by: Filipe Manana <fdmanana@suse.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/extent_map.c | 78 +++++++++++++++++++++++++++++++------------
+ 1 file changed, 57 insertions(+), 21 deletions(-)
+
+diff --git a/fs/btrfs/extent_map.c b/fs/btrfs/extent_map.c
+index 1d93e1202c339..d67abf5f97a77 100644
+--- a/fs/btrfs/extent_map.c
++++ b/fs/btrfs/extent_map.c
+@@ -1133,6 +1133,8 @@ static long btrfs_scan_inode(struct btrfs_inode *inode, struct btrfs_em_shrink_c
+       long nr_dropped = 0;
+       struct rb_node *node;
++      lockdep_assert_held_write(&tree->lock);
++
+       /*
+        * Take the mmap lock so that we serialize with the inode logging phase
+        * of fsync because we may need to set the full sync flag on the inode,
+@@ -1144,28 +1146,12 @@ static long btrfs_scan_inode(struct btrfs_inode *inode, struct btrfs_em_shrink_c
+        * to find new extents, which may not be there yet because ordered
+        * extents haven't completed yet.
+        *
+-       * We also do a try lock because otherwise we could deadlock. This is
+-       * because the shrinker for this filesystem may be invoked while we are
+-       * in a path that is holding the mmap lock in write mode. For example in
+-       * a reflink operation while COWing an extent buffer, when allocating
+-       * pages for a new extent buffer and under memory pressure, the shrinker
+-       * may be invoked, and therefore we would deadlock by attempting to read
+-       * lock the mmap lock while we are holding already a write lock on it.
++       * We also do a try lock because we don't want to block for too long and
++       * we are holding the extent map tree's lock in write mode.
+        */
+       if (!down_read_trylock(&inode->i_mmap_lock))
+               return 0;
+-      /*
+-       * We want to be fast so if the lock is busy we don't want to spend time
+-       * waiting for it - either some task is about to do IO for the inode or
+-       * we may have another task shrinking extent maps, here in this code, so
+-       * skip this inode.
+-       */
+-      if (!write_trylock(&tree->lock)) {
+-              up_read(&inode->i_mmap_lock);
+-              return 0;
+-      }
+-
+       node = rb_first(&tree->root);
+       while (node) {
+               struct rb_node *next = rb_next(node);
+@@ -1205,21 +1191,71 @@ static long btrfs_scan_inode(struct btrfs_inode *inode, struct btrfs_em_shrink_c
+                       break;
+               node = next;
+       }
+-      write_unlock(&tree->lock);
+       up_read(&inode->i_mmap_lock);
+       return nr_dropped;
+ }
++static struct btrfs_inode *find_first_inode_to_shrink(struct btrfs_root *root,
++                                                    u64 min_ino)
++{
++      struct btrfs_inode *inode;
++      unsigned long from = min_ino;
++
++      xa_lock(&root->inodes);
++      while (true) {
++              struct extent_map_tree *tree;
++
++              inode = xa_find(&root->inodes, &from, ULONG_MAX, XA_PRESENT);
++              if (!inode)
++                      break;
++
++              tree = &inode->extent_tree;
++
++              /*
++               * We want to be fast so if the lock is busy we don't want to
++               * spend time waiting for it (some task is about to do IO for
++               * the inode).
++               */
++              if (!write_trylock(&tree->lock))
++                      goto next;
++
++              /*
++               * Skip inode if it doesn't have loaded extent maps, so we avoid
++               * getting a reference and doing an iput later. This includes
++               * cases like files that were opened for things like stat(2), or
++               * files with all extent maps previously released through the
++               * release folio callback (btrfs_release_folio()) or released in
++               * a previous run, or directories which never have extent maps.
++               */
++              if (RB_EMPTY_ROOT(&tree->root)) {
++                      write_unlock(&tree->lock);
++                      goto next;
++              }
++
++              if (igrab(&inode->vfs_inode))
++                      break;
++
++              write_unlock(&tree->lock);
++next:
++              from = btrfs_ino(inode) + 1;
++              cond_resched_lock(&root->inodes.xa_lock);
++      }
++      xa_unlock(&root->inodes);
++
++      return inode;
++}
++
+ static long btrfs_scan_root(struct btrfs_root *root, struct btrfs_em_shrink_ctx *ctx)
+ {
+       struct btrfs_inode *inode;
+       long nr_dropped = 0;
+       u64 min_ino = ctx->last_ino + 1;
+-      inode = btrfs_find_first_inode(root, min_ino);
++      inode = find_first_inode_to_shrink(root, min_ino);
+       while (inode) {
+               nr_dropped += btrfs_scan_inode(inode, ctx);
++              write_unlock(&inode->extent_tree.lock);
+               min_ino = btrfs_ino(inode) + 1;
+               ctx->last_ino = btrfs_ino(inode);
+@@ -1230,7 +1266,7 @@ static long btrfs_scan_root(struct btrfs_root *root, struct btrfs_em_shrink_ctx
+               cond_resched();
+-              inode = btrfs_find_first_inode(root, min_ino);
++              inode = find_first_inode_to_shrink(root, min_ino);
+       }
+       if (inode) {
+-- 
+2.39.5
+
diff --git a/queue-6.12/btrfs-zoned-fix-extent-range-end-unlock-in-cow_file_.patch b/queue-6.12/btrfs-zoned-fix-extent-range-end-unlock-in-cow_file_.patch
new file mode 100644 (file)
index 0000000..a02c3dc
--- /dev/null
@@ -0,0 +1,135 @@
+From 6ed6b3b95a677a97677044d859cb750662af6bff Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 19 Feb 2025 16:02:11 +0900
+Subject: btrfs: zoned: fix extent range end unlock in cow_file_range()
+
+From: Naohiro Aota <naohiro.aota@wdc.com>
+
+[ Upstream commit 5a4041f2c47247575a6c2e53ce14f7b0ac946c33 ]
+
+Running generic/751 on the for-next branch often results in a hang like
+below. They are both stack by locking an extent. This suggests someone
+forget to unlock an extent.
+
+  INFO: task kworker/u128:1:12 blocked for more than 323 seconds.
+        Not tainted 6.13.0-BTRFS-ZNS+ #503
+  "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
+  task:kworker/u128:1  state:D stack:0     pid:12    tgid:12    ppid:2      flags:0x00004000
+  Workqueue: btrfs-fixup btrfs_work_helper [btrfs]
+  Call Trace:
+   <TASK>
+   __schedule+0x534/0xdd0
+   schedule+0x39/0x140
+   __lock_extent+0x31b/0x380 [btrfs]
+   ? __pfx_autoremove_wake_function+0x10/0x10
+   btrfs_writepage_fixup_worker+0xf1/0x3a0 [btrfs]
+   btrfs_work_helper+0xff/0x480 [btrfs]
+   ? lock_release+0x178/0x2c0
+   process_one_work+0x1ee/0x570
+   ? srso_return_thunk+0x5/0x5f
+   worker_thread+0x1d1/0x3b0
+   ? __pfx_worker_thread+0x10/0x10
+   kthread+0x10b/0x230
+   ? __pfx_kthread+0x10/0x10
+   ret_from_fork+0x30/0x50
+   ? __pfx_kthread+0x10/0x10
+   ret_from_fork_asm+0x1a/0x30
+   </TASK>
+  INFO: task kworker/u134:0:184 blocked for more than 323 seconds.
+        Not tainted 6.13.0-BTRFS-ZNS+ #503
+  "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
+  task:kworker/u134:0  state:D stack:0     pid:184   tgid:184   ppid:2      flags:0x00004000
+  Workqueue: writeback wb_workfn (flush-btrfs-4)
+  Call Trace:
+   <TASK>
+   __schedule+0x534/0xdd0
+   schedule+0x39/0x140
+   __lock_extent+0x31b/0x380 [btrfs]
+   ? __pfx_autoremove_wake_function+0x10/0x10
+   find_lock_delalloc_range+0xdb/0x260 [btrfs]
+   writepage_delalloc+0x12f/0x500 [btrfs]
+   ? srso_return_thunk+0x5/0x5f
+   extent_write_cache_pages+0x232/0x840 [btrfs]
+   btrfs_writepages+0x72/0x130 [btrfs]
+   do_writepages+0xe7/0x260
+   ? srso_return_thunk+0x5/0x5f
+   ? lock_acquire+0xd2/0x300
+   ? srso_return_thunk+0x5/0x5f
+   ? find_held_lock+0x2b/0x80
+   ? wbc_attach_and_unlock_inode.part.0+0x102/0x250
+   ? wbc_attach_and_unlock_inode.part.0+0x102/0x250
+   __writeback_single_inode+0x5c/0x4b0
+   writeback_sb_inodes+0x22d/0x550
+   __writeback_inodes_wb+0x4c/0xe0
+   wb_writeback+0x2f6/0x3f0
+   wb_workfn+0x32a/0x510
+   process_one_work+0x1ee/0x570
+   ? srso_return_thunk+0x5/0x5f
+   worker_thread+0x1d1/0x3b0
+   ? __pfx_worker_thread+0x10/0x10
+   kthread+0x10b/0x230
+   ? __pfx_kthread+0x10/0x10
+   ret_from_fork+0x30/0x50
+   ? __pfx_kthread+0x10/0x10
+   ret_from_fork_asm+0x1a/0x30
+   </TASK>
+
+This happens because we have another success path for the zoned mode. When
+there is no active zone available, btrfs_reserve_extent() returns
+-EAGAIN. In this case, we have two reactions.
+
+(1) If the given range is never allocated, we can only wait for someone
+    to finish a zone, so wait on BTRFS_FS_NEED_ZONE_FINISH bit and retry
+    afterward.
+
+(2) Or, if some allocations are already done, we must bail out and let
+    the caller to send IOs for the allocation. This is because these IOs
+    may be necessary to finish a zone.
+
+The commit 06f364284794 ("btrfs: do proper folio cleanup when
+cow_file_range() failed") moved the unlock code from the inside of the
+loop to the outside. So, previously, the allocated extents are unlocked
+just after the allocation and so before returning from the function.
+However, they are no longer unlocked on the case (2) above. That caused
+the hang issue.
+
+Fix the issue by modifying the 'end' to the end of the allocated
+range. Then, we can exit the loop and the same unlock code can properly
+handle the case.
+
+Reported-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com>
+Tested-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Fixes: 06f364284794 ("btrfs: do proper folio cleanup when cow_file_range() failed")
+CC: stable@vger.kernel.org
+Reviewed-by: Qu Wenruo <wqu@suse.com>
+Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
+Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
+Signed-off-by: David Sterba <dsterba@suse.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/btrfs/inode.c | 9 +++++++--
+ 1 file changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
+index 2f0b2cb2ae6e8..921ec3802648b 100644
+--- a/fs/btrfs/inode.c
++++ b/fs/btrfs/inode.c
+@@ -1463,8 +1463,13 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
+                               continue;
+                       }
+                       if (done_offset) {
+-                              *done_offset = start - 1;
+-                              return 0;
++                              /*
++                               * Move @end to the end of the processed range,
++                               * and exit the loop to unlock the processed extents.
++                               */
++                              end = start - 1;
++                              ret = 0;
++                              break;
+                       }
+                       ret = -ENOSPC;
+               }
+-- 
+2.39.5
+
diff --git a/queue-6.12/drm-amdkfd-fix-instruction-hazard-in-gfx12-trap-hand.patch b/queue-6.12/drm-amdkfd-fix-instruction-hazard-in-gfx12-trap-hand.patch
new file mode 100644 (file)
index 0000000..a35498d
--- /dev/null
@@ -0,0 +1,932 @@
+From bf4d4b17e212aba8bcebe5d7cfc657b09166f1e7 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 7 Feb 2025 16:40:34 -0500
+Subject: drm/amdkfd: Fix instruction hazard in gfx12 trap handler
+
+From: Jay Cornwall <jay.cornwall@amd.com>
+
+[ Upstream commit 424648c3838133f93a34fdfe4f9d5597551e7b3b ]
+
+VALU instructions with SGPR source need wait states to avoid hazard
+with SALU using different SGPR.
+
+v2: Eliminate some hazards to reduce code explosion
+
+Signed-off-by: Jay Cornwall <jay.cornwall@amd.com>
+Reviewed-by: Lancelot Six <lancelot.six@amd.com>
+Acked-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+(cherry picked from commit 7e0459d453b911435673edd7a86eadc600c63238)
+Cc: stable@vger.kernel.org # 6.12.x
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../gpu/drm/amd/amdkfd/cwsr_trap_handler.h    | 677 ++++++++++--------
+ .../amd/amdkfd/cwsr_trap_handler_gfx12.asm    |  82 ++-
+ 2 files changed, 404 insertions(+), 355 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h
+index 7062f12b5b751..6c8c9935a0f2e 100644
+--- a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h
++++ b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler.h
+@@ -3640,7 +3640,7 @@ static const uint32_t cwsr_trap_gfx9_4_3_hex[] = {
+ };
+ static const uint32_t cwsr_trap_gfx12_hex[] = {
+-      0xbfa00001, 0xbfa0024b,
++      0xbfa00001, 0xbfa002a2,
+       0xb0804009, 0xb8f8f804,
+       0x9178ff78, 0x00008c00,
+       0xb8fbf811, 0x8b6eff78,
+@@ -3714,7 +3714,15 @@ static const uint32_t cwsr_trap_gfx12_hex[] = {
+       0x00011677, 0xd7610000,
+       0x00011a79, 0xd7610000,
+       0x00011c7e, 0xd7610000,
+-      0x00011e7f, 0xbefe00ff,
++      0x00011e7f, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xbefe00ff,
+       0x00003fff, 0xbeff0080,
+       0xee0a407a, 0x000c0000,
+       0x00004000, 0xd760007a,
+@@ -3751,38 +3759,46 @@ static const uint32_t cwsr_trap_gfx12_hex[] = {
+       0x00000200, 0xbef600ff,
+       0x01000000, 0x7e000280,
+       0x7e020280, 0x7e040280,
+-      0xbefd0080, 0xbe804ec2,
+-      0xbf94fffe, 0xb8faf804,
+-      0x8b7a847a, 0x91788478,
+-      0x8c787a78, 0xd7610002,
+-      0x0000fa71, 0x807d817d,
+-      0xd7610002, 0x0000fa6c,
+-      0x807d817d, 0x917aff6d,
+-      0x80000000, 0xd7610002,
+-      0x0000fa7a, 0x807d817d,
+-      0xd7610002, 0x0000fa6e,
+-      0x807d817d, 0xd7610002,
+-      0x0000fa6f, 0x807d817d,
+-      0xd7610002, 0x0000fa78,
+-      0x807d817d, 0xb8faf811,
+-      0xd7610002, 0x0000fa7a,
+-      0x807d817d, 0xd7610002,
+-      0x0000fa7b, 0x807d817d,
+-      0xb8f1f801, 0xd7610002,
+-      0x0000fa71, 0x807d817d,
+-      0xb8f1f814, 0xd7610002,
+-      0x0000fa71, 0x807d817d,
+-      0xb8f1f815, 0xd7610002,
+-      0x0000fa71, 0x807d817d,
+-      0xb8f1f812, 0xd7610002,
+-      0x0000fa71, 0x807d817d,
+-      0xb8f1f813, 0xd7610002,
+-      0x0000fa71, 0x807d817d,
++      0xbe804ec2, 0xbf94fffe,
++      0xb8faf804, 0x8b7a847a,
++      0x91788478, 0x8c787a78,
++      0x917aff6d, 0x80000000,
++      0xd7610002, 0x00010071,
++      0xd7610002, 0x0001026c,
++      0xd7610002, 0x0001047a,
++      0xd7610002, 0x0001066e,
++      0xd7610002, 0x0001086f,
++      0xd7610002, 0x00010a78,
++      0xd7610002, 0x00010e7b,
++      0xd8500000, 0x00000000,
++      0xd8500000, 0x00000000,
++      0xd8500000, 0x00000000,
++      0xd8500000, 0x00000000,
++      0xd8500000, 0x00000000,
++      0xd8500000, 0x00000000,
++      0xd8500000, 0x00000000,
++      0xd8500000, 0x00000000,
++      0xb8faf811, 0xd7610002,
++      0x00010c7a, 0xb8faf801,
++      0xd7610002, 0x0001107a,
++      0xb8faf814, 0xd7610002,
++      0x0001127a, 0xb8faf815,
++      0xd7610002, 0x0001147a,
++      0xb8faf812, 0xd7610002,
++      0x0001167a, 0xb8faf813,
++      0xd7610002, 0x0001187a,
+       0xb8faf802, 0xd7610002,
+-      0x0000fa7a, 0x807d817d,
+-      0xbefa50c1, 0xbfc70000,
+-      0xd7610002, 0x0000fa7a,
+-      0x807d817d, 0xbefe00ff,
++      0x00011a7a, 0xbefa50c1,
++      0xbfc70000, 0xd7610002,
++      0x00011c7a, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xbefe00ff,
+       0x0000ffff, 0xbeff0080,
+       0xc4068070, 0x008ce802,
+       0x00000000, 0xbefe00c1,
+@@ -3797,329 +3813,356 @@ static const uint32_t cwsr_trap_gfx12_hex[] = {
+       0xbe824102, 0xbe844104,
+       0xbe864106, 0xbe884108,
+       0xbe8a410a, 0xbe8c410c,
+-      0xbe8e410e, 0xd7610002,
+-      0x0000f200, 0x80798179,
+-      0xd7610002, 0x0000f201,
+-      0x80798179, 0xd7610002,
+-      0x0000f202, 0x80798179,
+-      0xd7610002, 0x0000f203,
+-      0x80798179, 0xd7610002,
+-      0x0000f204, 0x80798179,
+-      0xd7610002, 0x0000f205,
+-      0x80798179, 0xd7610002,
+-      0x0000f206, 0x80798179,
+-      0xd7610002, 0x0000f207,
+-      0x80798179, 0xd7610002,
+-      0x0000f208, 0x80798179,
+-      0xd7610002, 0x0000f209,
+-      0x80798179, 0xd7610002,
+-      0x0000f20a, 0x80798179,
+-      0xd7610002, 0x0000f20b,
+-      0x80798179, 0xd7610002,
+-      0x0000f20c, 0x80798179,
+-      0xd7610002, 0x0000f20d,
+-      0x80798179, 0xd7610002,
+-      0x0000f20e, 0x80798179,
+-      0xd7610002, 0x0000f20f,
+-      0x80798179, 0xbf06a079,
+-      0xbfa10007, 0xc4068070,
++      0xbe8e410e, 0xbf068079,
++      0xbfa10032, 0xd7610002,
++      0x00010000, 0xd7610002,
++      0x00010201, 0xd7610002,
++      0x00010402, 0xd7610002,
++      0x00010603, 0xd7610002,
++      0x00010804, 0xd7610002,
++      0x00010a05, 0xd7610002,
++      0x00010c06, 0xd7610002,
++      0x00010e07, 0xd7610002,
++      0x00011008, 0xd7610002,
++      0x00011209, 0xd7610002,
++      0x0001140a, 0xd7610002,
++      0x0001160b, 0xd7610002,
++      0x0001180c, 0xd7610002,
++      0x00011a0d, 0xd7610002,
++      0x00011c0e, 0xd7610002,
++      0x00011e0f, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0x80799079,
++      0xbfa00038, 0xd7610002,
++      0x00012000, 0xd7610002,
++      0x00012201, 0xd7610002,
++      0x00012402, 0xd7610002,
++      0x00012603, 0xd7610002,
++      0x00012804, 0xd7610002,
++      0x00012a05, 0xd7610002,
++      0x00012c06, 0xd7610002,
++      0x00012e07, 0xd7610002,
++      0x00013008, 0xd7610002,
++      0x00013209, 0xd7610002,
++      0x0001340a, 0xd7610002,
++      0x0001360b, 0xd7610002,
++      0x0001380c, 0xd7610002,
++      0x00013a0d, 0xd7610002,
++      0x00013c0e, 0xd7610002,
++      0x00013e0f, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0x80799079,
++      0xc4068070, 0x008ce802,
++      0x00000000, 0x8070ff70,
++      0x00000080, 0xbef90080,
++      0x7e040280, 0x807d907d,
++      0xbf0aff7d, 0x00000060,
++      0xbfa2ff88, 0xbe804100,
++      0xbe824102, 0xbe844104,
++      0xbe864106, 0xbe884108,
++      0xbe8a410a, 0xd7610002,
++      0x00010000, 0xd7610002,
++      0x00010201, 0xd7610002,
++      0x00010402, 0xd7610002,
++      0x00010603, 0xd7610002,
++      0x00010804, 0xd7610002,
++      0x00010a05, 0xd7610002,
++      0x00010c06, 0xd7610002,
++      0x00010e07, 0xd7610002,
++      0x00011008, 0xd7610002,
++      0x00011209, 0xd7610002,
++      0x0001140a, 0xd7610002,
++      0x0001160b, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xd8500000,
++      0x00000000, 0xc4068070,
+       0x008ce802, 0x00000000,
++      0xbefe00c1, 0x857d9973,
++      0x8b7d817d, 0xbf06817d,
++      0xbfa20002, 0xbeff0080,
++      0xbfa00001, 0xbeff00c1,
++      0xb8fb4306, 0x8b7bc17b,
++      0xbfa10044, 0x8b7aff6d,
++      0x80000000, 0xbfa10041,
++      0x847b897b, 0xbef6007b,
++      0xb8f03b05, 0x80708170,
++      0xbf0d9973, 0xbfa20002,
++      0x84708970, 0xbfa00001,
++      0x84708a70, 0xb8fa1e06,
++      0x847a8a7a, 0x80707a70,
++      0x8070ff70, 0x00000200,
+       0x8070ff70, 0x00000080,
+-      0xbef90080, 0x7e040280,
+-      0x807d907d, 0xbf0aff7d,
+-      0x00000060, 0xbfa2ffbb,
+-      0xbe804100, 0xbe824102,
+-      0xbe844104, 0xbe864106,
+-      0xbe884108, 0xbe8a410a,
+-      0xd7610002, 0x0000f200,
+-      0x80798179, 0xd7610002,
+-      0x0000f201, 0x80798179,
+-      0xd7610002, 0x0000f202,
+-      0x80798179, 0xd7610002,
+-      0x0000f203, 0x80798179,
+-      0xd7610002, 0x0000f204,
+-      0x80798179, 0xd7610002,
+-      0x0000f205, 0x80798179,
+-      0xd7610002, 0x0000f206,
+-      0x80798179, 0xd7610002,
+-      0x0000f207, 0x80798179,
+-      0xd7610002, 0x0000f208,
+-      0x80798179, 0xd7610002,
+-      0x0000f209, 0x80798179,
+-      0xd7610002, 0x0000f20a,
+-      0x80798179, 0xd7610002,
+-      0x0000f20b, 0x80798179,
+-      0xc4068070, 0x008ce802,
+-      0x00000000, 0xbefe00c1,
+-      0x857d9973, 0x8b7d817d,
+-      0xbf06817d, 0xbfa20002,
+-      0xbeff0080, 0xbfa00001,
+-      0xbeff00c1, 0xb8fb4306,
+-      0x8b7bc17b, 0xbfa10044,
+-      0x8b7aff6d, 0x80000000,
+-      0xbfa10041, 0x847b897b,
+-      0xbef6007b, 0xb8f03b05,
+-      0x80708170, 0xbf0d9973,
+-      0xbfa20002, 0x84708970,
+-      0xbfa00001, 0x84708a70,
+-      0xb8fa1e06, 0x847a8a7a,
+-      0x80707a70, 0x8070ff70,
+-      0x00000200, 0x8070ff70,
+-      0x00000080, 0xbef600ff,
+-      0x01000000, 0xd71f0000,
+-      0x000100c1, 0xd7200000,
+-      0x000200c1, 0x16000084,
+-      0x857d9973, 0x8b7d817d,
+-      0xbf06817d, 0xbefd0080,
+-      0xbfa20013, 0xbe8300ff,
+-      0x00000080, 0xbf800000,
+-      0xbf800000, 0xbf800000,
+-      0xd8d80000, 0x01000000,
+-      0xbf8a0000, 0xc4068070,
+-      0x008ce801, 0x00000000,
+-      0x807d037d, 0x80700370,
+-      0xd5250000, 0x0001ff00,
+-      0x00000080, 0xbf0a7b7d,
+-      0xbfa2fff3, 0xbfa00012,
+-      0xbe8300ff, 0x00000100,
++      0xbef600ff, 0x01000000,
++      0xd71f0000, 0x000100c1,
++      0xd7200000, 0x000200c1,
++      0x16000084, 0x857d9973,
++      0x8b7d817d, 0xbf06817d,
++      0xbefd0080, 0xbfa20013,
++      0xbe8300ff, 0x00000080,
+       0xbf800000, 0xbf800000,
+       0xbf800000, 0xd8d80000,
+       0x01000000, 0xbf8a0000,
+       0xc4068070, 0x008ce801,
+       0x00000000, 0x807d037d,
+       0x80700370, 0xd5250000,
+-      0x0001ff00, 0x00000100,
++      0x0001ff00, 0x00000080,
+       0xbf0a7b7d, 0xbfa2fff3,
+-      0xbefe00c1, 0x857d9973,
+-      0x8b7d817d, 0xbf06817d,
+-      0xbfa20004, 0xbef000ff,
+-      0x00000200, 0xbeff0080,
+-      0xbfa00003, 0xbef000ff,
+-      0x00000400, 0xbeff00c1,
+-      0xb8fb3b05, 0x807b817b,
+-      0x847b827b, 0x857d9973,
+-      0x8b7d817d, 0xbf06817d,
+-      0xbfa2001b, 0xbef600ff,
+-      0x01000000, 0xbefd0084,
+-      0xbf0a7b7d, 0xbfa10040,
+-      0x7e008700, 0x7e028701,
+-      0x7e048702, 0x7e068703,
+-      0xc4068070, 0x008ce800,
+-      0x00000000, 0xc4068070,
+-      0x008ce801, 0x00008000,
+-      0xc4068070, 0x008ce802,
+-      0x00010000, 0xc4068070,
+-      0x008ce803, 0x00018000,
+-      0x807d847d, 0x8070ff70,
+-      0x00000200, 0xbf0a7b7d,
+-      0xbfa2ffeb, 0xbfa0002a,
++      0xbfa00012, 0xbe8300ff,
++      0x00000100, 0xbf800000,
++      0xbf800000, 0xbf800000,
++      0xd8d80000, 0x01000000,
++      0xbf8a0000, 0xc4068070,
++      0x008ce801, 0x00000000,
++      0x807d037d, 0x80700370,
++      0xd5250000, 0x0001ff00,
++      0x00000100, 0xbf0a7b7d,
++      0xbfa2fff3, 0xbefe00c1,
++      0x857d9973, 0x8b7d817d,
++      0xbf06817d, 0xbfa20004,
++      0xbef000ff, 0x00000200,
++      0xbeff0080, 0xbfa00003,
++      0xbef000ff, 0x00000400,
++      0xbeff00c1, 0xb8fb3b05,
++      0x807b817b, 0x847b827b,
++      0x857d9973, 0x8b7d817d,
++      0xbf06817d, 0xbfa2001b,
+       0xbef600ff, 0x01000000,
+       0xbefd0084, 0xbf0a7b7d,
+-      0xbfa10015, 0x7e008700,
++      0xbfa10040, 0x7e008700,
+       0x7e028701, 0x7e048702,
+       0x7e068703, 0xc4068070,
+       0x008ce800, 0x00000000,
+       0xc4068070, 0x008ce801,
+-      0x00010000, 0xc4068070,
+-      0x008ce802, 0x00020000,
++      0x00008000, 0xc4068070,
++      0x008ce802, 0x00010000,
+       0xc4068070, 0x008ce803,
+-      0x00030000, 0x807d847d,
+-      0x8070ff70, 0x00000400,
++      0x00018000, 0x807d847d,
++      0x8070ff70, 0x00000200,
+       0xbf0a7b7d, 0xbfa2ffeb,
+-      0xb8fb1e06, 0x8b7bc17b,
+-      0xbfa1000d, 0x847b837b,
+-      0x807b7d7b, 0xbefe00c1,
+-      0xbeff0080, 0x7e008700,
++      0xbfa0002a, 0xbef600ff,
++      0x01000000, 0xbefd0084,
++      0xbf0a7b7d, 0xbfa10015,
++      0x7e008700, 0x7e028701,
++      0x7e048702, 0x7e068703,
+       0xc4068070, 0x008ce800,
+-      0x00000000, 0x807d817d,
+-      0x8070ff70, 0x00000080,
+-      0xbf0a7b7d, 0xbfa2fff7,
+-      0xbfa0016e, 0xbef4007e,
+-      0x8b75ff7f, 0x0000ffff,
+-      0x8c75ff75, 0x00040000,
+-      0xbef60080, 0xbef700ff,
+-      0x10807fac, 0xbef1007f,
+-      0xb8f20742, 0x84729972,
+-      0x8b6eff7f, 0x04000000,
+-      0xbfa1003b, 0xbefe00c1,
+-      0x857d9972, 0x8b7d817d,
+-      0xbf06817d, 0xbfa20002,
+-      0xbeff0080, 0xbfa00001,
+-      0xbeff00c1, 0xb8ef4306,
+-      0x8b6fc16f, 0xbfa10030,
+-      0x846f896f, 0xbef6006f,
++      0x00000000, 0xc4068070,
++      0x008ce801, 0x00010000,
++      0xc4068070, 0x008ce802,
++      0x00020000, 0xc4068070,
++      0x008ce803, 0x00030000,
++      0x807d847d, 0x8070ff70,
++      0x00000400, 0xbf0a7b7d,
++      0xbfa2ffeb, 0xb8fb1e06,
++      0x8b7bc17b, 0xbfa1000d,
++      0x847b837b, 0x807b7d7b,
++      0xbefe00c1, 0xbeff0080,
++      0x7e008700, 0xc4068070,
++      0x008ce800, 0x00000000,
++      0x807d817d, 0x8070ff70,
++      0x00000080, 0xbf0a7b7d,
++      0xbfa2fff7, 0xbfa0016e,
++      0xbef4007e, 0x8b75ff7f,
++      0x0000ffff, 0x8c75ff75,
++      0x00040000, 0xbef60080,
++      0xbef700ff, 0x10807fac,
++      0xbef1007f, 0xb8f20742,
++      0x84729972, 0x8b6eff7f,
++      0x04000000, 0xbfa1003b,
++      0xbefe00c1, 0x857d9972,
++      0x8b7d817d, 0xbf06817d,
++      0xbfa20002, 0xbeff0080,
++      0xbfa00001, 0xbeff00c1,
++      0xb8ef4306, 0x8b6fc16f,
++      0xbfa10030, 0x846f896f,
++      0xbef6006f, 0xb8f83b05,
++      0x80788178, 0xbf0d9972,
++      0xbfa20002, 0x84788978,
++      0xbfa00001, 0x84788a78,
++      0xb8ee1e06, 0x846e8a6e,
++      0x80786e78, 0x8078ff78,
++      0x00000200, 0x8078ff78,
++      0x00000080, 0xbef600ff,
++      0x01000000, 0x857d9972,
++      0x8b7d817d, 0xbf06817d,
++      0xbefd0080, 0xbfa2000d,
++      0xc4050078, 0x0080e800,
++      0x00000000, 0xbf8a0000,
++      0xdac00000, 0x00000000,
++      0x807dff7d, 0x00000080,
++      0x8078ff78, 0x00000080,
++      0xbf0a6f7d, 0xbfa2fff4,
++      0xbfa0000c, 0xc4050078,
++      0x0080e800, 0x00000000,
++      0xbf8a0000, 0xdac00000,
++      0x00000000, 0x807dff7d,
++      0x00000100, 0x8078ff78,
++      0x00000100, 0xbf0a6f7d,
++      0xbfa2fff4, 0xbef80080,
++      0xbefe00c1, 0x857d9972,
++      0x8b7d817d, 0xbf06817d,
++      0xbfa20002, 0xbeff0080,
++      0xbfa00001, 0xbeff00c1,
++      0xb8ef3b05, 0x806f816f,
++      0x846f826f, 0x857d9972,
++      0x8b7d817d, 0xbf06817d,
++      0xbfa2002c, 0xbef600ff,
++      0x01000000, 0xbeee0078,
++      0x8078ff78, 0x00000200,
++      0xbefd0084, 0xbf0a6f7d,
++      0xbfa10061, 0xc4050078,
++      0x008ce800, 0x00000000,
++      0xc4050078, 0x008ce801,
++      0x00008000, 0xc4050078,
++      0x008ce802, 0x00010000,
++      0xc4050078, 0x008ce803,
++      0x00018000, 0xbf8a0000,
++      0x7e008500, 0x7e028501,
++      0x7e048502, 0x7e068503,
++      0x807d847d, 0x8078ff78,
++      0x00000200, 0xbf0a6f7d,
++      0xbfa2ffea, 0xc405006e,
++      0x008ce800, 0x00000000,
++      0xc405006e, 0x008ce801,
++      0x00008000, 0xc405006e,
++      0x008ce802, 0x00010000,
++      0xc405006e, 0x008ce803,
++      0x00018000, 0xbf8a0000,
++      0xbfa0003d, 0xbef600ff,
++      0x01000000, 0xbeee0078,
++      0x8078ff78, 0x00000400,
++      0xbefd0084, 0xbf0a6f7d,
++      0xbfa10016, 0xc4050078,
++      0x008ce800, 0x00000000,
++      0xc4050078, 0x008ce801,
++      0x00010000, 0xc4050078,
++      0x008ce802, 0x00020000,
++      0xc4050078, 0x008ce803,
++      0x00030000, 0xbf8a0000,
++      0x7e008500, 0x7e028501,
++      0x7e048502, 0x7e068503,
++      0x807d847d, 0x8078ff78,
++      0x00000400, 0xbf0a6f7d,
++      0xbfa2ffea, 0xb8ef1e06,
++      0x8b6fc16f, 0xbfa1000f,
++      0x846f836f, 0x806f7d6f,
++      0xbefe00c1, 0xbeff0080,
++      0xc4050078, 0x008ce800,
++      0x00000000, 0xbf8a0000,
++      0x7e008500, 0x807d817d,
++      0x8078ff78, 0x00000080,
++      0xbf0a6f7d, 0xbfa2fff6,
++      0xbeff00c1, 0xc405006e,
++      0x008ce800, 0x00000000,
++      0xc405006e, 0x008ce801,
++      0x00010000, 0xc405006e,
++      0x008ce802, 0x00020000,
++      0xc405006e, 0x008ce803,
++      0x00030000, 0xbf8a0000,
+       0xb8f83b05, 0x80788178,
+       0xbf0d9972, 0xbfa20002,
+       0x84788978, 0xbfa00001,
+       0x84788a78, 0xb8ee1e06,
+       0x846e8a6e, 0x80786e78,
+       0x8078ff78, 0x00000200,
+-      0x8078ff78, 0x00000080,
+-      0xbef600ff, 0x01000000,
+-      0x857d9972, 0x8b7d817d,
+-      0xbf06817d, 0xbefd0080,
+-      0xbfa2000d, 0xc4050078,
+-      0x0080e800, 0x00000000,
+-      0xbf8a0000, 0xdac00000,
+-      0x00000000, 0x807dff7d,
+-      0x00000080, 0x8078ff78,
+-      0x00000080, 0xbf0a6f7d,
+-      0xbfa2fff4, 0xbfa0000c,
+-      0xc4050078, 0x0080e800,
+-      0x00000000, 0xbf8a0000,
+-      0xdac00000, 0x00000000,
+-      0x807dff7d, 0x00000100,
+-      0x8078ff78, 0x00000100,
+-      0xbf0a6f7d, 0xbfa2fff4,
+-      0xbef80080, 0xbefe00c1,
+-      0x857d9972, 0x8b7d817d,
+-      0xbf06817d, 0xbfa20002,
+-      0xbeff0080, 0xbfa00001,
+-      0xbeff00c1, 0xb8ef3b05,
+-      0x806f816f, 0x846f826f,
+-      0x857d9972, 0x8b7d817d,
+-      0xbf06817d, 0xbfa2002c,
++      0x80f8ff78, 0x00000050,
+       0xbef600ff, 0x01000000,
+-      0xbeee0078, 0x8078ff78,
+-      0x00000200, 0xbefd0084,
+-      0xbf0a6f7d, 0xbfa10061,
+-      0xc4050078, 0x008ce800,
+-      0x00000000, 0xc4050078,
+-      0x008ce801, 0x00008000,
+-      0xc4050078, 0x008ce802,
+-      0x00010000, 0xc4050078,
+-      0x008ce803, 0x00018000,
+-      0xbf8a0000, 0x7e008500,
+-      0x7e028501, 0x7e048502,
+-      0x7e068503, 0x807d847d,
++      0xbefd00ff, 0x0000006c,
++      0x80f89078, 0xf462403a,
++      0xf0000000, 0xbf8a0000,
++      0x80fd847d, 0xbf800000,
++      0xbe804300, 0xbe824302,
++      0x80f8a078, 0xf462603a,
++      0xf0000000, 0xbf8a0000,
++      0x80fd887d, 0xbf800000,
++      0xbe804300, 0xbe824302,
++      0xbe844304, 0xbe864306,
++      0x80f8c078, 0xf462803a,
++      0xf0000000, 0xbf8a0000,
++      0x80fd907d, 0xbf800000,
++      0xbe804300, 0xbe824302,
++      0xbe844304, 0xbe864306,
++      0xbe884308, 0xbe8a430a,
++      0xbe8c430c, 0xbe8e430e,
++      0xbf06807d, 0xbfa1fff0,
++      0xb980f801, 0x00000000,
++      0xb8f83b05, 0x80788178,
++      0xbf0d9972, 0xbfa20002,
++      0x84788978, 0xbfa00001,
++      0x84788a78, 0xb8ee1e06,
++      0x846e8a6e, 0x80786e78,
+       0x8078ff78, 0x00000200,
+-      0xbf0a6f7d, 0xbfa2ffea,
+-      0xc405006e, 0x008ce800,
+-      0x00000000, 0xc405006e,
+-      0x008ce801, 0x00008000,
+-      0xc405006e, 0x008ce802,
+-      0x00010000, 0xc405006e,
+-      0x008ce803, 0x00018000,
+-      0xbf8a0000, 0xbfa0003d,
+       0xbef600ff, 0x01000000,
+-      0xbeee0078, 0x8078ff78,
+-      0x00000400, 0xbefd0084,
+-      0xbf0a6f7d, 0xbfa10016,
+-      0xc4050078, 0x008ce800,
+-      0x00000000, 0xc4050078,
+-      0x008ce801, 0x00010000,
+-      0xc4050078, 0x008ce802,
+-      0x00020000, 0xc4050078,
+-      0x008ce803, 0x00030000,
+-      0xbf8a0000, 0x7e008500,
+-      0x7e028501, 0x7e048502,
+-      0x7e068503, 0x807d847d,
+-      0x8078ff78, 0x00000400,
+-      0xbf0a6f7d, 0xbfa2ffea,
+-      0xb8ef1e06, 0x8b6fc16f,
+-      0xbfa1000f, 0x846f836f,
+-      0x806f7d6f, 0xbefe00c1,
+-      0xbeff0080, 0xc4050078,
+-      0x008ce800, 0x00000000,
+-      0xbf8a0000, 0x7e008500,
+-      0x807d817d, 0x8078ff78,
+-      0x00000080, 0xbf0a6f7d,
+-      0xbfa2fff6, 0xbeff00c1,
+-      0xc405006e, 0x008ce800,
+-      0x00000000, 0xc405006e,
+-      0x008ce801, 0x00010000,
+-      0xc405006e, 0x008ce802,
+-      0x00020000, 0xc405006e,
+-      0x008ce803, 0x00030000,
+-      0xbf8a0000, 0xb8f83b05,
+-      0x80788178, 0xbf0d9972,
+-      0xbfa20002, 0x84788978,
+-      0xbfa00001, 0x84788a78,
+-      0xb8ee1e06, 0x846e8a6e,
+-      0x80786e78, 0x8078ff78,
+-      0x00000200, 0x80f8ff78,
+-      0x00000050, 0xbef600ff,
+-      0x01000000, 0xbefd00ff,
+-      0x0000006c, 0x80f89078,
+-      0xf462403a, 0xf0000000,
+-      0xbf8a0000, 0x80fd847d,
+-      0xbf800000, 0xbe804300,
+-      0xbe824302, 0x80f8a078,
+-      0xf462603a, 0xf0000000,
+-      0xbf8a0000, 0x80fd887d,
+-      0xbf800000, 0xbe804300,
+-      0xbe824302, 0xbe844304,
+-      0xbe864306, 0x80f8c078,
+-      0xf462803a, 0xf0000000,
+-      0xbf8a0000, 0x80fd907d,
+-      0xbf800000, 0xbe804300,
+-      0xbe824302, 0xbe844304,
+-      0xbe864306, 0xbe884308,
+-      0xbe8a430a, 0xbe8c430c,
+-      0xbe8e430e, 0xbf06807d,
+-      0xbfa1fff0, 0xb980f801,
+-      0x00000000, 0xb8f83b05,
+-      0x80788178, 0xbf0d9972,
+-      0xbfa20002, 0x84788978,
+-      0xbfa00001, 0x84788a78,
+-      0xb8ee1e06, 0x846e8a6e,
+-      0x80786e78, 0x8078ff78,
+-      0x00000200, 0xbef600ff,
+-      0x01000000, 0xbeff0071,
+-      0xf4621bfa, 0xf0000000,
+-      0x80788478, 0xf4621b3a,
++      0xbeff0071, 0xf4621bfa,
+       0xf0000000, 0x80788478,
+-      0xf4621b7a, 0xf0000000,
+-      0x80788478, 0xf4621c3a,
++      0xf4621b3a, 0xf0000000,
++      0x80788478, 0xf4621b7a,
+       0xf0000000, 0x80788478,
+-      0xf4621c7a, 0xf0000000,
+-      0x80788478, 0xf4621eba,
++      0xf4621c3a, 0xf0000000,
++      0x80788478, 0xf4621c7a,
+       0xf0000000, 0x80788478,
+-      0xf4621efa, 0xf0000000,
+-      0x80788478, 0xf4621e7a,
++      0xf4621eba, 0xf0000000,
++      0x80788478, 0xf4621efa,
+       0xf0000000, 0x80788478,
+-      0xf4621cfa, 0xf0000000,
+-      0x80788478, 0xf4621bba,
++      0xf4621e7a, 0xf0000000,
++      0x80788478, 0xf4621cfa,
+       0xf0000000, 0x80788478,
+-      0xbf8a0000, 0xb96ef814,
+       0xf4621bba, 0xf0000000,
+       0x80788478, 0xbf8a0000,
+-      0xb96ef815, 0xf4621bba,
++      0xb96ef814, 0xf4621bba,
+       0xf0000000, 0x80788478,
+-      0xbf8a0000, 0xb96ef812,
++      0xbf8a0000, 0xb96ef815,
+       0xf4621bba, 0xf0000000,
+       0x80788478, 0xbf8a0000,
+-      0xb96ef813, 0x8b6eff7f,
+-      0x04000000, 0xbfa1000d,
+-      0x80788478, 0xf4621bba,
++      0xb96ef812, 0xf4621bba,
+       0xf0000000, 0x80788478,
+-      0xbf8a0000, 0xbf0d806e,
+-      0xbfa10006, 0x856e906e,
+-      0x8b6e6e6e, 0xbfa10003,
+-      0xbe804ec1, 0x816ec16e,
+-      0xbfa0fffb, 0xbefd006f,
+-      0xbefe0070, 0xbeff0071,
+-      0xb97b2011, 0x857b867b,
+-      0xb97b0191, 0x857b827b,
+-      0xb97bba11, 0xb973f801,
+-      0xb8ee3b05, 0x806e816e,
+-      0xbf0d9972, 0xbfa20002,
+-      0x846e896e, 0xbfa00001,
+-      0x846e8a6e, 0xb8ef1e06,
+-      0x846f8a6f, 0x806e6f6e,
+-      0x806eff6e, 0x00000200,
+-      0x806e746e, 0x826f8075,
+-      0x8b6fff6f, 0x0000ffff,
+-      0xf4605c37, 0xf8000050,
+-      0xf4605d37, 0xf8000060,
+-      0xf4601e77, 0xf8000074,
+-      0xbf8a0000, 0x8b6dff6d,
+-      0x0000ffff, 0x8bfe7e7e,
+-      0x8bea6a6a, 0xb97af804,
++      0xbf8a0000, 0xb96ef813,
++      0x8b6eff7f, 0x04000000,
++      0xbfa1000d, 0x80788478,
++      0xf4621bba, 0xf0000000,
++      0x80788478, 0xbf8a0000,
++      0xbf0d806e, 0xbfa10006,
++      0x856e906e, 0x8b6e6e6e,
++      0xbfa10003, 0xbe804ec1,
++      0x816ec16e, 0xbfa0fffb,
++      0xbefd006f, 0xbefe0070,
++      0xbeff0071, 0xb97b2011,
++      0x857b867b, 0xb97b0191,
++      0x857b827b, 0xb97bba11,
++      0xb973f801, 0xb8ee3b05,
++      0x806e816e, 0xbf0d9972,
++      0xbfa20002, 0x846e896e,
++      0xbfa00001, 0x846e8a6e,
++      0xb8ef1e06, 0x846f8a6f,
++      0x806e6f6e, 0x806eff6e,
++      0x00000200, 0x806e746e,
++      0x826f8075, 0x8b6fff6f,
++      0x0000ffff, 0xf4605c37,
++      0xf8000050, 0xf4605d37,
++      0xf8000060, 0xf4601e77,
++      0xf8000074, 0xbf8a0000,
++      0x8b6dff6d, 0x0000ffff,
++      0x8bfe7e7e, 0x8bea6a6a,
++      0xb97af804, 0xbe804ec2,
++      0xbf94fffe, 0xbe804a6c,
+       0xbe804ec2, 0xbf94fffe,
+-      0xbe804a6c, 0xbe804ec2,
+-      0xbf94fffe, 0xbfb10000,
++      0xbfb10000, 0xbf9f0000,
+       0xbf9f0000, 0xbf9f0000,
+       0xbf9f0000, 0xbf9f0000,
+-      0xbf9f0000, 0x00000000,
+ };
+diff --git a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx12.asm b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx12.asm
+index 7b9d36e5fa437..5a1a1b1f897fe 100644
+--- a/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx12.asm
++++ b/drivers/gpu/drm/amd/amdkfd/cwsr_trap_handler_gfx12.asm
+@@ -30,6 +30,7 @@
+ #define CHIP_GFX12 37
+ #define SINGLE_STEP_MISSED_WORKAROUND 1       //workaround for lost TRAP_AFTER_INST exception when SAVECTX raised
++#define HAVE_VALU_SGPR_HAZARD (ASIC_FAMILY == CHIP_GFX12)
+ var SQ_WAVE_STATE_PRIV_BARRIER_COMPLETE_MASK  = 0x4
+ var SQ_WAVE_STATE_PRIV_SCC_SHIFT              = 9
+@@ -351,6 +352,7 @@ L_HAVE_VGPRS:
+       v_writelane_b32 v0, ttmp13, 0xD
+       v_writelane_b32 v0, exec_lo, 0xE
+       v_writelane_b32 v0, exec_hi, 0xF
++      valu_sgpr_hazard()
+       s_mov_b32       exec_lo, 0x3FFF
+       s_mov_b32       exec_hi, 0x0
+@@ -417,7 +419,6 @@ L_SAVE_HWREG:
+       v_mov_b32       v0, 0x0                                                 //Offset[31:0] from buffer resource
+       v_mov_b32       v1, 0x0                                                 //Offset[63:32] from buffer resource
+       v_mov_b32       v2, 0x0                                                 //Set of SGPRs for TCP store
+-      s_mov_b32       m0, 0x0                                                 //Next lane of v2 to write to
+       // Ensure no further changes to barrier or LDS state.
+       // STATE_PRIV.BARRIER_COMPLETE may change up to this point.
+@@ -430,40 +431,41 @@ L_SAVE_HWREG:
+       s_andn2_b32     s_save_state_priv, s_save_state_priv, SQ_WAVE_STATE_PRIV_BARRIER_COMPLETE_MASK
+       s_or_b32        s_save_state_priv, s_save_state_priv, s_save_tmp
+-      write_hwreg_to_v2(s_save_m0)
+-      write_hwreg_to_v2(s_save_pc_lo)
+       s_andn2_b32     s_save_tmp, s_save_pc_hi, S_SAVE_PC_HI_FIRST_WAVE_MASK
+-      write_hwreg_to_v2(s_save_tmp)
+-      write_hwreg_to_v2(s_save_exec_lo)
+-      write_hwreg_to_v2(s_save_exec_hi)
+-      write_hwreg_to_v2(s_save_state_priv)
++      v_writelane_b32 v2, s_save_m0, 0x0
++      v_writelane_b32 v2, s_save_pc_lo, 0x1
++      v_writelane_b32 v2, s_save_tmp, 0x2
++      v_writelane_b32 v2, s_save_exec_lo, 0x3
++      v_writelane_b32 v2, s_save_exec_hi, 0x4
++      v_writelane_b32 v2, s_save_state_priv, 0x5
++      v_writelane_b32 v2, s_save_xnack_mask, 0x7
++      valu_sgpr_hazard()
+       s_getreg_b32    s_save_tmp, hwreg(HW_REG_WAVE_EXCP_FLAG_PRIV)
+-      write_hwreg_to_v2(s_save_tmp)
++      v_writelane_b32 v2, s_save_tmp, 0x6
+-      write_hwreg_to_v2(s_save_xnack_mask)
++      s_getreg_b32    s_save_tmp, hwreg(HW_REG_WAVE_MODE)
++      v_writelane_b32 v2, s_save_tmp, 0x8
+-      s_getreg_b32    s_save_m0, hwreg(HW_REG_WAVE_MODE)
+-      write_hwreg_to_v2(s_save_m0)
++      s_getreg_b32    s_save_tmp, hwreg(HW_REG_WAVE_SCRATCH_BASE_LO)
++      v_writelane_b32 v2, s_save_tmp, 0x9
+-      s_getreg_b32    s_save_m0, hwreg(HW_REG_WAVE_SCRATCH_BASE_LO)
+-      write_hwreg_to_v2(s_save_m0)
++      s_getreg_b32    s_save_tmp, hwreg(HW_REG_WAVE_SCRATCH_BASE_HI)
++      v_writelane_b32 v2, s_save_tmp, 0xA
+-      s_getreg_b32    s_save_m0, hwreg(HW_REG_WAVE_SCRATCH_BASE_HI)
+-      write_hwreg_to_v2(s_save_m0)
++      s_getreg_b32    s_save_tmp, hwreg(HW_REG_WAVE_EXCP_FLAG_USER)
++      v_writelane_b32 v2, s_save_tmp, 0xB
+-      s_getreg_b32    s_save_m0, hwreg(HW_REG_WAVE_EXCP_FLAG_USER)
+-      write_hwreg_to_v2(s_save_m0)
+-
+-      s_getreg_b32    s_save_m0, hwreg(HW_REG_WAVE_TRAP_CTRL)
+-      write_hwreg_to_v2(s_save_m0)
++      s_getreg_b32    s_save_tmp, hwreg(HW_REG_WAVE_TRAP_CTRL)
++      v_writelane_b32 v2, s_save_tmp, 0xC
+       s_getreg_b32    s_save_tmp, hwreg(HW_REG_WAVE_STATUS)
+-      write_hwreg_to_v2(s_save_tmp)
++      v_writelane_b32 v2, s_save_tmp, 0xD
+       s_get_barrier_state s_save_tmp, -1
+       s_wait_kmcnt (0)
+-      write_hwreg_to_v2(s_save_tmp)
++      v_writelane_b32 v2, s_save_tmp, 0xE
++      valu_sgpr_hazard()
+       // Write HWREGs with 16 VGPR lanes. TTMPs occupy space after this.
+       s_mov_b32       exec_lo, 0xFFFF
+@@ -497,10 +499,12 @@ L_SAVE_SGPR_LOOP:
+       s_movrels_b64   s12, s12                                                //s12 = s[12+m0], s13 = s[13+m0]
+       s_movrels_b64   s14, s14                                                //s14 = s[14+m0], s15 = s[15+m0]
+-      write_16sgpr_to_v2(s0)
+-
+-      s_cmp_eq_u32    ttmp13, 0x20                                            //have 32 VGPR lanes filled?
+-      s_cbranch_scc0  L_SAVE_SGPR_SKIP_TCP_STORE
++      s_cmp_eq_u32    ttmp13, 0x0
++      s_cbranch_scc0  L_WRITE_V2_SECOND_HALF
++      write_16sgpr_to_v2(s0, 0x0)
++      s_branch        L_SAVE_SGPR_SKIP_TCP_STORE
++L_WRITE_V2_SECOND_HALF:
++      write_16sgpr_to_v2(s0, 0x10)
+       buffer_store_dword      v2, v0, s_save_buf_rsrc0, s_save_mem_offset scope:SCOPE_SYS
+       s_add_u32       s_save_mem_offset, s_save_mem_offset, 0x80
+@@ -1056,27 +1060,21 @@ L_END_PGM:
+       s_endpgm_saved
+ end
+-function write_hwreg_to_v2(s)
+-      // Copy into VGPR for later TCP store.
+-      v_writelane_b32 v2, s, m0
+-      s_add_u32       m0, m0, 0x1
+-end
+-
+-
+-function write_16sgpr_to_v2(s)
++function write_16sgpr_to_v2(s, lane_offset)
+       // Copy into VGPR for later TCP store.
+       for var sgpr_idx = 0; sgpr_idx < 16; sgpr_idx ++
+-              v_writelane_b32 v2, s[sgpr_idx], ttmp13
+-              s_add_u32       ttmp13, ttmp13, 0x1
++              v_writelane_b32 v2, s[sgpr_idx], sgpr_idx + lane_offset
+       end
++      valu_sgpr_hazard()
++      s_add_u32       ttmp13, ttmp13, 0x10
+ end
+ function write_12sgpr_to_v2(s)
+       // Copy into VGPR for later TCP store.
+       for var sgpr_idx = 0; sgpr_idx < 12; sgpr_idx ++
+-              v_writelane_b32 v2, s[sgpr_idx], ttmp13
+-              s_add_u32       ttmp13, ttmp13, 0x1
++              v_writelane_b32 v2, s[sgpr_idx], sgpr_idx
+       end
++      valu_sgpr_hazard()
+ end
+ function read_hwreg_from_mem(s, s_rsrc, s_mem_offset)
+@@ -1128,3 +1126,11 @@ function get_wave_size2(s_reg)
+       s_getreg_b32    s_reg, hwreg(HW_REG_WAVE_STATUS,SQ_WAVE_STATUS_WAVE64_SHIFT,SQ_WAVE_STATUS_WAVE64_SIZE)
+       s_lshl_b32      s_reg, s_reg, S_WAVE_SIZE
+ end
++
++function valu_sgpr_hazard
++#if HAVE_VALU_SGPR_HAZARD
++      for var rep = 0; rep < 8; rep ++
++              ds_nop
++      end
++#endif
++end
+-- 
+2.39.5
+
diff --git a/queue-6.12/drm-amdkfd-remove-gfx-12-trap-handler-page-size-cap.patch b/queue-6.12/drm-amdkfd-remove-gfx-12-trap-handler-page-size-cap.patch
new file mode 100644 (file)
index 0000000..563c466
--- /dev/null
@@ -0,0 +1,37 @@
+From e8c3565abdfa7dc2b5161ce7fb4e1c1b2abd5844 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 5 Nov 2024 12:38:30 -0500
+Subject: drm/amdkfd: remove gfx 12 trap handler page size cap
+
+From: Jonathan Kim <jonathan.kim@amd.com>
+
+[ Upstream commit cd82f29ec51b2e616289db7b258a936127c16efa ]
+
+GFX 12 does not require a page size cap for the trap handler because
+it does not require a CWSR work around like GFX 11 did.
+
+Signed-off-by: Jonathan Kim <jonathan.kim@amd.com>
+Reviewed-by: David Belanger <david.belanger@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/amd/amdkfd/kfd_device.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
+index 9186ef0bd2a32..07eadab4c1c4d 100644
+--- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c
++++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c
+@@ -537,7 +537,8 @@ static void kfd_cwsr_init(struct kfd_dev *kfd)
+                       kfd->cwsr_isa = cwsr_trap_gfx11_hex;
+                       kfd->cwsr_isa_size = sizeof(cwsr_trap_gfx11_hex);
+               } else {
+-                      BUILD_BUG_ON(sizeof(cwsr_trap_gfx12_hex) > PAGE_SIZE);
++                      BUILD_BUG_ON(sizeof(cwsr_trap_gfx12_hex)
++                                           > KFD_CWSR_TMA_OFFSET);
+                       kfd->cwsr_isa = cwsr_trap_gfx12_hex;
+                       kfd->cwsr_isa_size = sizeof(cwsr_trap_gfx12_hex);
+               }
+-- 
+2.39.5
+
diff --git a/queue-6.12/drm-fbdev-dma-add-shadow-buffering-for-deferred-i-o.patch b/queue-6.12/drm-fbdev-dma-add-shadow-buffering-for-deferred-i-o.patch
new file mode 100644 (file)
index 0000000..ce3396e
--- /dev/null
@@ -0,0 +1,347 @@
+From 516707b69d16dc07cfa3f1aaf48e5da31b9949b4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 11 Dec 2024 10:06:28 +0100
+Subject: drm/fbdev-dma: Add shadow buffering for deferred I/O
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 3603996432997f7c88da37a97062a46cda01ac9d ]
+
+DMA areas are not necessarily backed by struct page, so we cannot
+rely on it for deferred I/O. Allocate a shadow buffer for drivers
+that require deferred I/O and use it as framebuffer memory.
+
+Fixes driver errors about being "Unable to handle kernel NULL pointer
+dereference at virtual address" or "Unable to handle kernel paging
+request at virtual address".
+
+The patch splits drm_fbdev_dma_driver_fbdev_probe() in an initial
+allocation, which creates the DMA-backed buffer object, and a tail
+that sets up the fbdev data structures. There is a tail function for
+direct memory mappings and a tail function for deferred I/O with
+the shadow buffer.
+
+It is no longer possible to use deferred I/O without shadow buffer.
+It can be re-added if there exists a reliably test for usable struct
+page in the allocated DMA-backed buffer object.
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reported-by: Nuno Gonçalves <nunojpg@gmail.com>
+CLoses: https://lore.kernel.org/dri-devel/CAEXMXLR55DziAMbv_+2hmLeH-jP96pmit6nhs6siB22cpQFr9w@mail.gmail.com/
+Tested-by: Nuno Gonçalves <nunojpg@gmail.com>
+Fixes: 5ab91447aa13 ("drm/tiny/ili9225: Use fbdev-dma")
+Cc: Thomas Zimmermann <tzimmermann@suse.de>
+Cc: <stable@vger.kernel.org> # v6.11+
+Reviewed-by: Simona Vetter <simona.vetter@ffwll.ch>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20241211090643.74250-1-tzimmermann@suse.de
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fbdev_dma.c | 219 +++++++++++++++++++++++---------
+ 1 file changed, 156 insertions(+), 63 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fbdev_dma.c b/drivers/gpu/drm/drm_fbdev_dma.c
+index 7c8287c18e381..6fcf2a8bf6762 100644
+--- a/drivers/gpu/drm/drm_fbdev_dma.c
++++ b/drivers/gpu/drm/drm_fbdev_dma.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: MIT
+ #include <linux/fb.h>
++#include <linux/vmalloc.h>
+ #include <drm/drm_crtc_helper.h>
+ #include <drm/drm_drv.h>
+@@ -72,43 +73,108 @@ static const struct fb_ops drm_fbdev_dma_fb_ops = {
+       .fb_destroy = drm_fbdev_dma_fb_destroy,
+ };
+-FB_GEN_DEFAULT_DEFERRED_DMAMEM_OPS(drm_fbdev_dma,
++FB_GEN_DEFAULT_DEFERRED_DMAMEM_OPS(drm_fbdev_dma_shadowed,
+                                  drm_fb_helper_damage_range,
+                                  drm_fb_helper_damage_area);
+-static int drm_fbdev_dma_deferred_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
++static void drm_fbdev_dma_shadowed_fb_destroy(struct fb_info *info)
+ {
+       struct drm_fb_helper *fb_helper = info->par;
+-      struct drm_framebuffer *fb = fb_helper->fb;
+-      struct drm_gem_dma_object *dma = drm_fb_dma_get_gem_obj(fb, 0);
++      void *shadow = info->screen_buffer;
++
++      if (!fb_helper->dev)
++              return;
+-      if (!dma->map_noncoherent)
+-              vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
++      if (info->fbdefio)
++              fb_deferred_io_cleanup(info);
++      drm_fb_helper_fini(fb_helper);
++      vfree(shadow);
+-      return fb_deferred_io_mmap(info, vma);
++      drm_client_buffer_vunmap(fb_helper->buffer);
++      drm_client_framebuffer_delete(fb_helper->buffer);
++      drm_client_release(&fb_helper->client);
++      drm_fb_helper_unprepare(fb_helper);
++      kfree(fb_helper);
+ }
+-static const struct fb_ops drm_fbdev_dma_deferred_fb_ops = {
++static const struct fb_ops drm_fbdev_dma_shadowed_fb_ops = {
+       .owner = THIS_MODULE,
+       .fb_open = drm_fbdev_dma_fb_open,
+       .fb_release = drm_fbdev_dma_fb_release,
+-      __FB_DEFAULT_DEFERRED_OPS_RDWR(drm_fbdev_dma),
++      FB_DEFAULT_DEFERRED_OPS(drm_fbdev_dma_shadowed),
+       DRM_FB_HELPER_DEFAULT_OPS,
+-      __FB_DEFAULT_DEFERRED_OPS_DRAW(drm_fbdev_dma),
+-      .fb_mmap = drm_fbdev_dma_deferred_fb_mmap,
+-      .fb_destroy = drm_fbdev_dma_fb_destroy,
++      .fb_destroy = drm_fbdev_dma_shadowed_fb_destroy,
+ };
+ /*
+  * struct drm_fb_helper
+  */
++static void drm_fbdev_dma_damage_blit_real(struct drm_fb_helper *fb_helper,
++                                         struct drm_clip_rect *clip,
++                                         struct iosys_map *dst)
++{
++      struct drm_framebuffer *fb = fb_helper->fb;
++      size_t offset = clip->y1 * fb->pitches[0];
++      size_t len = clip->x2 - clip->x1;
++      unsigned int y;
++      void *src;
++
++      switch (drm_format_info_bpp(fb->format, 0)) {
++      case 1:
++              offset += clip->x1 / 8;
++              len = DIV_ROUND_UP(len + clip->x1 % 8, 8);
++              break;
++      case 2:
++              offset += clip->x1 / 4;
++              len = DIV_ROUND_UP(len + clip->x1 % 4, 4);
++              break;
++      case 4:
++              offset += clip->x1 / 2;
++              len = DIV_ROUND_UP(len + clip->x1 % 2, 2);
++              break;
++      default:
++              offset += clip->x1 * fb->format->cpp[0];
++              len *= fb->format->cpp[0];
++              break;
++      }
++
++      src = fb_helper->info->screen_buffer + offset;
++      iosys_map_incr(dst, offset); /* go to first pixel within clip rect */
++
++      for (y = clip->y1; y < clip->y2; y++) {
++              iosys_map_memcpy_to(dst, 0, src, len);
++              iosys_map_incr(dst, fb->pitches[0]);
++              src += fb->pitches[0];
++      }
++}
++
++static int drm_fbdev_dma_damage_blit(struct drm_fb_helper *fb_helper,
++                                   struct drm_clip_rect *clip)
++{
++      struct drm_client_buffer *buffer = fb_helper->buffer;
++      struct iosys_map dst;
++
++      /*
++       * For fbdev emulation, we only have to protect against fbdev modeset
++       * operations. Nothing else will involve the client buffer's BO. So it
++       * is sufficient to acquire struct drm_fb_helper.lock here.
++       */
++      mutex_lock(&fb_helper->lock);
++
++      dst = buffer->map;
++      drm_fbdev_dma_damage_blit_real(fb_helper, clip, &dst);
++
++      mutex_unlock(&fb_helper->lock);
++
++      return 0;
++}
++
+ static int drm_fbdev_dma_helper_fb_probe(struct drm_fb_helper *fb_helper,
+                                        struct drm_fb_helper_surface_size *sizes)
+ {
+       return drm_fbdev_dma_driver_fbdev_probe(fb_helper, sizes);
+ }
+-
+ static int drm_fbdev_dma_helper_fb_dirty(struct drm_fb_helper *helper,
+                                        struct drm_clip_rect *clip)
+ {
+@@ -120,6 +186,10 @@ static int drm_fbdev_dma_helper_fb_dirty(struct drm_fb_helper *helper,
+               return 0;
+       if (helper->fb->funcs->dirty) {
++              ret = drm_fbdev_dma_damage_blit(helper, clip);
++              if (drm_WARN_ONCE(dev, ret, "Damage blitter failed: ret=%d\n", ret))
++                      return ret;
++
+               ret = helper->fb->funcs->dirty(helper->fb, NULL, 0, 0, clip, 1);
+               if (drm_WARN_ONCE(dev, ret, "Dirty helper failed: ret=%d\n", ret))
+                       return ret;
+@@ -137,14 +207,80 @@ static const struct drm_fb_helper_funcs drm_fbdev_dma_helper_funcs = {
+  * struct drm_fb_helper
+  */
++static int drm_fbdev_dma_driver_fbdev_probe_tail(struct drm_fb_helper *fb_helper,
++                                               struct drm_fb_helper_surface_size *sizes)
++{
++      struct drm_device *dev = fb_helper->dev;
++      struct drm_client_buffer *buffer = fb_helper->buffer;
++      struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(buffer->gem);
++      struct drm_framebuffer *fb = fb_helper->fb;
++      struct fb_info *info = fb_helper->info;
++      struct iosys_map map = buffer->map;
++
++      info->fbops = &drm_fbdev_dma_fb_ops;
++
++      /* screen */
++      info->flags |= FBINFO_VIRTFB; /* system memory */
++      if (dma_obj->map_noncoherent)
++              info->flags |= FBINFO_READS_FAST; /* signal caching */
++      info->screen_size = sizes->surface_height * fb->pitches[0];
++      info->screen_buffer = map.vaddr;
++      if (!(info->flags & FBINFO_HIDE_SMEM_START)) {
++              if (!drm_WARN_ON(dev, is_vmalloc_addr(info->screen_buffer)))
++                      info->fix.smem_start = page_to_phys(virt_to_page(info->screen_buffer));
++      }
++      info->fix.smem_len = info->screen_size;
++
++      return 0;
++}
++
++static int drm_fbdev_dma_driver_fbdev_probe_tail_shadowed(struct drm_fb_helper *fb_helper,
++                                                        struct drm_fb_helper_surface_size *sizes)
++{
++      struct drm_client_buffer *buffer = fb_helper->buffer;
++      struct fb_info *info = fb_helper->info;
++      size_t screen_size = buffer->gem->size;
++      void *screen_buffer;
++      int ret;
++
++      /*
++       * Deferred I/O requires struct page for framebuffer memory,
++       * which is not guaranteed for all DMA ranges. We thus create
++       * a shadow buffer in system memory.
++       */
++      screen_buffer = vzalloc(screen_size);
++      if (!screen_buffer)
++              return -ENOMEM;
++
++      info->fbops = &drm_fbdev_dma_shadowed_fb_ops;
++
++      /* screen */
++      info->flags |= FBINFO_VIRTFB; /* system memory */
++      info->flags |= FBINFO_READS_FAST; /* signal caching */
++      info->screen_buffer = screen_buffer;
++      info->fix.smem_len = screen_size;
++
++      fb_helper->fbdefio.delay = HZ / 20;
++      fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
++
++      info->fbdefio = &fb_helper->fbdefio;
++      ret = fb_deferred_io_init(info);
++      if (ret)
++              goto err_vfree;
++
++      return 0;
++
++err_vfree:
++      vfree(screen_buffer);
++      return ret;
++}
++
+ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
+                                    struct drm_fb_helper_surface_size *sizes)
+ {
+       struct drm_client_dev *client = &fb_helper->client;
+       struct drm_device *dev = fb_helper->dev;
+-      bool use_deferred_io = false;
+       struct drm_client_buffer *buffer;
+-      struct drm_gem_dma_object *dma_obj;
+       struct drm_framebuffer *fb;
+       struct fb_info *info;
+       u32 format;
+@@ -161,19 +297,9 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
+                                              sizes->surface_height, format);
+       if (IS_ERR(buffer))
+               return PTR_ERR(buffer);
+-      dma_obj = to_drm_gem_dma_obj(buffer->gem);
+       fb = buffer->fb;
+-      /*
+-       * Deferred I/O requires struct page for framebuffer memory,
+-       * which is not guaranteed for all DMA ranges. We thus only
+-       * install deferred I/O if we have a framebuffer that requires
+-       * it.
+-       */
+-      if (fb->funcs->dirty)
+-              use_deferred_io = true;
+-
+       ret = drm_client_buffer_vmap(buffer, &map);
+       if (ret) {
+               goto err_drm_client_buffer_delete;
+@@ -194,45 +320,12 @@ int drm_fbdev_dma_driver_fbdev_probe(struct drm_fb_helper *fb_helper,
+       drm_fb_helper_fill_info(info, fb_helper, sizes);
+-      if (use_deferred_io)
+-              info->fbops = &drm_fbdev_dma_deferred_fb_ops;
++      if (fb->funcs->dirty)
++              ret = drm_fbdev_dma_driver_fbdev_probe_tail_shadowed(fb_helper, sizes);
+       else
+-              info->fbops = &drm_fbdev_dma_fb_ops;
+-
+-      /* screen */
+-      info->flags |= FBINFO_VIRTFB; /* system memory */
+-      if (dma_obj->map_noncoherent)
+-              info->flags |= FBINFO_READS_FAST; /* signal caching */
+-      info->screen_size = sizes->surface_height * fb->pitches[0];
+-      info->screen_buffer = map.vaddr;
+-      if (!(info->flags & FBINFO_HIDE_SMEM_START)) {
+-              if (!drm_WARN_ON(dev, is_vmalloc_addr(info->screen_buffer)))
+-                      info->fix.smem_start = page_to_phys(virt_to_page(info->screen_buffer));
+-      }
+-      info->fix.smem_len = info->screen_size;
+-
+-      /*
+-       * Only set up deferred I/O if the screen buffer supports
+-       * it. If this disagrees with the previous test for ->dirty,
+-       * mmap on the /dev/fb file might not work correctly.
+-       */
+-      if (!is_vmalloc_addr(info->screen_buffer) && info->fix.smem_start) {
+-              unsigned long pfn = info->fix.smem_start >> PAGE_SHIFT;
+-
+-              if (drm_WARN_ON(dev, !pfn_to_page(pfn)))
+-                      use_deferred_io = false;
+-      }
+-
+-      /* deferred I/O */
+-      if (use_deferred_io) {
+-              fb_helper->fbdefio.delay = HZ / 20;
+-              fb_helper->fbdefio.deferred_io = drm_fb_helper_deferred_io;
+-
+-              info->fbdefio = &fb_helper->fbdefio;
+-              ret = fb_deferred_io_init(info);
+-              if (ret)
+-                      goto err_drm_fb_helper_release_info;
+-      }
++              ret = drm_fbdev_dma_driver_fbdev_probe_tail(fb_helper, sizes);
++      if (ret)
++              goto err_drm_fb_helper_release_info;
+       return 0;
+-- 
+2.39.5
+
diff --git a/queue-6.12/drm-msm-dp-account-for-widebus-and-yuv420-during-mod.patch b/queue-6.12/drm-msm-dp-account-for-widebus-and-yuv420-during-mod.patch
new file mode 100644 (file)
index 0000000..9537b57
--- /dev/null
@@ -0,0 +1,75 @@
+From d1829f86087e1f01617eb0b7d4f6e5fd72b62e41 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Jul 2025 17:47:38 -0400
+Subject: drm/msm/dp: account for widebus and yuv420 during mode validation
+
+[ Upstream commit df9cf852ca3099feb8fed781bdd5d3863af001c8 ]
+
+Widebus allows the DP controller to operate in 2 pixel per clock mode.
+The mode validation logic validates the mode->clock against the max
+DP pixel clock. However the max DP pixel clock limit assumes widebus
+is already enabled. Adjust the mode validation logic to only compare
+the adjusted pixel clock which accounts for widebus against the max DP
+pixel clock. Also fix the mode validation logic for YUV420 modes as in
+that case as well, only half the pixel clock is needed.
+
+Cc: stable@vger.kernel.org
+Fixes: 757a2f36ab09 ("drm/msm/dp: enable widebus feature for display port")
+Fixes: 6db6e5606576 ("drm/msm/dp: change clock related programming for YUV420 over DP")
+Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org>
+Tested-by: Dale Whinham <daleyo@gmail.com>
+Patchwork: https://patchwork.freedesktop.org/patch/635789/
+Link: https://lore.kernel.org/r/20250206-dp-widebus-fix-v2-1-cb89a0313286@quicinc.com
+Signed-off-by: Abhinav Kumar <quic_abhinavk@quicinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/msm/dp/dp_display.c | 11 ++++++-----
+ drivers/gpu/drm/msm/dp/dp_drm.c     |  5 ++++-
+ 2 files changed, 10 insertions(+), 6 deletions(-)
+
+diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
+index e1228fb093ee0..a5c1534eafdb1 100644
+--- a/drivers/gpu/drm/msm/dp/dp_display.c
++++ b/drivers/gpu/drm/msm/dp/dp_display.c
+@@ -928,16 +928,17 @@ enum drm_mode_status dp_bridge_mode_valid(struct drm_bridge *bridge,
+               return -EINVAL;
+       }
+-      if (mode->clock > DP_MAX_PIXEL_CLK_KHZ)
+-              return MODE_CLOCK_HIGH;
+-
+       dp_display = container_of(dp, struct dp_display_private, dp_display);
+       link_info = &dp_display->panel->link_info;
+-      if (drm_mode_is_420_only(&dp->connector->display_info, mode) &&
+-          dp_display->panel->vsc_sdp_supported)
++      if ((drm_mode_is_420_only(&dp->connector->display_info, mode) &&
++           dp_display->panel->vsc_sdp_supported) ||
++           msm_dp_wide_bus_available(dp))
+               mode_pclk_khz /= 2;
++      if (mode_pclk_khz > DP_MAX_PIXEL_CLK_KHZ)
++              return MODE_CLOCK_HIGH;
++
+       mode_bpp = dp->connector->display_info.bpc * num_components;
+       if (!mode_bpp)
+               mode_bpp = default_bpp;
+diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
+index 1b9be5bd97f12..da0176eae3fe3 100644
+--- a/drivers/gpu/drm/msm/dp/dp_drm.c
++++ b/drivers/gpu/drm/msm/dp/dp_drm.c
+@@ -257,7 +257,10 @@ static enum drm_mode_status edp_bridge_mode_valid(struct drm_bridge *bridge,
+               return -EINVAL;
+       }
+-      if (mode->clock > DP_MAX_PIXEL_CLK_KHZ)
++      if (msm_dp_wide_bus_available(dp))
++              mode_pclk_khz /= 2;
++
++      if (mode_pclk_khz > DP_MAX_PIXEL_CLK_KHZ)
+               return MODE_CLOCK_HIGH;
+       /*
+-- 
+2.39.5
+
diff --git a/queue-6.12/drm-xe-carve-out-wopcm-portion-from-the-stolen-memor.patch b/queue-6.12/drm-xe-carve-out-wopcm-portion-from-the-stolen-memor.patch
new file mode 100644 (file)
index 0000000..f96c40e
--- /dev/null
@@ -0,0 +1,120 @@
+From 1560183dd074b967cddf6c175dbdc81cd9d08031 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Jul 2025 17:33:25 -0400
+Subject: drm/xe: Carve out wopcm portion from the stolen memory
+
+[ Upstream commit e977499820782ab1c69f354d9f41b6d9ad1f43d9 ]
+
+The top of stolen memory is WOPCM, which shouldn't be accessed. Remove
+this portion from the stolen memory region for discrete platforms.
+This was already done for integrated, but was missing for discrete
+platforms.
+
+This also moves get_wopcm_size() so detect_bar2_dgfx() and
+detect_bar2_integrated can use the same function.
+
+v2: Improve commit message and suitable stable version tag(Lucas)
+
+Fixes: d8b52a02cb40 ("drm/xe: Implement stolen memory.")
+Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
+Cc: Matthew Auld <matthew.auld@intel.com>
+Cc: Lucas De Marchi <lucas.demarchi@intel.com>
+Cc: stable@vger.kernel.org # v6.11+
+Reviewed-by: Lucas De Marchi <lucas.demarchi@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20250210143654.2076747-1-nirmoy.das@intel.com
+Signed-off-by: Nirmoy Das <nirmoy.das@intel.com>
+(cherry picked from commit 2c7f45cc7e197a792ce5c693e56ea48f60b312da)
+Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c | 54 ++++++++++++++------------
+ 1 file changed, 30 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+index ef84fa757b26f..34e38bb167bac 100644
+--- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
++++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+@@ -57,12 +57,35 @@ bool xe_ttm_stolen_cpu_access_needs_ggtt(struct xe_device *xe)
+       return GRAPHICS_VERx100(xe) < 1270 && !IS_DGFX(xe);
+ }
++static u32 get_wopcm_size(struct xe_device *xe)
++{
++      u32 wopcm_size;
++      u64 val;
++
++      val = xe_mmio_read64_2x32(xe_root_mmio_gt(xe), STOLEN_RESERVED);
++      val = REG_FIELD_GET64(WOPCM_SIZE_MASK, val);
++
++      switch (val) {
++      case 0x5 ... 0x6:
++              val--;
++              fallthrough;
++      case 0x0 ... 0x3:
++              wopcm_size = (1U << val) * SZ_1M;
++              break;
++      default:
++              WARN(1, "Missing case wopcm_size=%llx\n", val);
++              wopcm_size = 0;
++      }
++
++      return wopcm_size;
++}
++
+ static s64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
+ {
+       struct xe_tile *tile = xe_device_get_root_tile(xe);
+       struct xe_gt *mmio = xe_root_mmio_gt(xe);
+       struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+-      u64 stolen_size;
++      u64 stolen_size, wopcm_size;
+       u64 tile_offset;
+       u64 tile_size;
+@@ -74,7 +97,13 @@ static s64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
+       if (drm_WARN_ON(&xe->drm, tile_size < mgr->stolen_base))
+               return 0;
++      /* Carve out the top of DSM as it contains the reserved WOPCM region */
++      wopcm_size = get_wopcm_size(xe);
++      if (drm_WARN_ON(&xe->drm, !wopcm_size))
++              return 0;
++
+       stolen_size = tile_size - mgr->stolen_base;
++      stolen_size -= wopcm_size;
+       /* Verify usage fits in the actual resource available */
+       if (mgr->stolen_base + stolen_size <= pci_resource_len(pdev, LMEM_BAR))
+@@ -89,29 +118,6 @@ static s64 detect_bar2_dgfx(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
+       return ALIGN_DOWN(stolen_size, SZ_1M);
+ }
+-static u32 get_wopcm_size(struct xe_device *xe)
+-{
+-      u32 wopcm_size;
+-      u64 val;
+-
+-      val = xe_mmio_read64_2x32(xe_root_mmio_gt(xe), STOLEN_RESERVED);
+-      val = REG_FIELD_GET64(WOPCM_SIZE_MASK, val);
+-
+-      switch (val) {
+-      case 0x5 ... 0x6:
+-              val--;
+-              fallthrough;
+-      case 0x0 ... 0x3:
+-              wopcm_size = (1U << val) * SZ_1M;
+-              break;
+-      default:
+-              WARN(1, "Missing case wopcm_size=%llx\n", val);
+-              wopcm_size = 0;
+-      }
+-
+-      return wopcm_size;
+-}
+-
+ static u32 detect_bar2_integrated(struct xe_device *xe, struct xe_ttm_stolen_mgr *mgr)
+ {
+       struct pci_dev *pdev = to_pci_dev(xe->drm.dev);
+-- 
+2.39.5
+
diff --git a/queue-6.12/iio-dac-ad3552r-changes-to-use-field_prep.patch b/queue-6.12/iio-dac-ad3552r-changes-to-use-field_prep.patch
new file mode 100644 (file)
index 0000000..5381763
--- /dev/null
@@ -0,0 +1,310 @@
+From 5ac841dad37a42948a41cbddd6ee789fb30adf70 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 28 Oct 2024 22:45:32 +0100
+Subject: iio: dac: ad3552r: changes to use FIELD_PREP
+
+From: Angelo Dureghello <adureghello@baylibre.com>
+
+[ Upstream commit d5ac6cb1c8f3e14d93e2a50d9357a8acdbc5c166 ]
+
+Changes to use FIELD_PREP, so that driver-specific ad3552r_field_prep
+is removed. Variables (arrays) that was used to call ad3552r_field_prep
+are removed too.
+
+Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
+Reviewed-by: David Lechner <dlechner@baylibre.com>
+Link: https://patch.msgid.link/20241028-wip-bl-ad3552r-axi-v0-iio-testing-v9-5-f6960b4f9719@kernel-space.org
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/dac/ad3552r.c | 167 ++++++++++++--------------------------
+ 1 file changed, 50 insertions(+), 117 deletions(-)
+
+diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c
+index 390d3fab21478..aa453d3de5e1c 100644
+--- a/drivers/iio/dac/ad3552r.c
++++ b/drivers/iio/dac/ad3552r.c
+@@ -6,6 +6,7 @@
+  * Copyright 2021 Analog Devices Inc.
+  */
+ #include <linux/unaligned.h>
++#include <linux/bitfield.h>
+ #include <linux/device.h>
+ #include <linux/iio/triggered_buffer.h>
+ #include <linux/iio/trigger_consumer.h>
+@@ -210,46 +211,6 @@ static const s32 gains_scaling_table[] = {
+       [AD3552R_CH_GAIN_SCALING_0_125]         = 125
+ };
+-enum ad3552r_dev_attributes {
+-      /* - Direct register values */
+-      /* From 0-3 */
+-      AD3552R_SDO_DRIVE_STRENGTH,
+-      /*
+-       * 0 -> Internal Vref, vref_io pin floating (default)
+-       * 1 -> Internal Vref, vref_io driven by internal vref
+-       * 2 or 3 -> External Vref
+-       */
+-      AD3552R_VREF_SELECT,
+-      /* Read registers in ascending order if set. Else descending */
+-      AD3552R_ADDR_ASCENSION,
+-};
+-
+-enum ad3552r_ch_attributes {
+-      /* DAC powerdown */
+-      AD3552R_CH_DAC_POWERDOWN,
+-      /* DAC amplifier powerdown */
+-      AD3552R_CH_AMPLIFIER_POWERDOWN,
+-      /* Select the output range. Select from enum ad3552r_ch_output_range */
+-      AD3552R_CH_OUTPUT_RANGE_SEL,
+-      /*
+-       * Over-rider the range selector in order to manually set the output
+-       * voltage range
+-       */
+-      AD3552R_CH_RANGE_OVERRIDE,
+-      /* Manually set the offset voltage */
+-      AD3552R_CH_GAIN_OFFSET,
+-      /* Sets the polarity of the offset. */
+-      AD3552R_CH_GAIN_OFFSET_POLARITY,
+-      /* PDAC gain scaling */
+-      AD3552R_CH_GAIN_SCALING_P,
+-      /* NDAC gain scaling */
+-      AD3552R_CH_GAIN_SCALING_N,
+-      /* Rfb value */
+-      AD3552R_CH_RFB,
+-      /* Channel select. When set allow Input -> DAC and Mask -> DAC */
+-      AD3552R_CH_SELECT,
+-};
+-
+ struct ad3552r_ch_data {
+       s32     scale_int;
+       s32     scale_dec;
+@@ -285,45 +246,6 @@ struct ad3552r_desc {
+       unsigned int            num_ch;
+ };
+-static const u16 addr_mask_map[][2] = {
+-      [AD3552R_ADDR_ASCENSION] = {
+-                      AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
+-                      AD3552R_MASK_ADDR_ASCENSION
+-      },
+-      [AD3552R_SDO_DRIVE_STRENGTH] = {
+-                      AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
+-                      AD3552R_MASK_SDO_DRIVE_STRENGTH
+-      },
+-      [AD3552R_VREF_SELECT] = {
+-                      AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
+-                      AD3552R_MASK_REFERENCE_VOLTAGE_SEL
+-      },
+-};
+-
+-/* 0 -> reg addr, 1->ch0 mask, 2->ch1 mask */
+-static const u16 addr_mask_map_ch[][3] = {
+-      [AD3552R_CH_DAC_POWERDOWN] = {
+-                      AD3552R_REG_ADDR_POWERDOWN_CONFIG,
+-                      AD3552R_MASK_CH_DAC_POWERDOWN(0),
+-                      AD3552R_MASK_CH_DAC_POWERDOWN(1)
+-      },
+-      [AD3552R_CH_AMPLIFIER_POWERDOWN] = {
+-                      AD3552R_REG_ADDR_POWERDOWN_CONFIG,
+-                      AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0),
+-                      AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1)
+-      },
+-      [AD3552R_CH_OUTPUT_RANGE_SEL] = {
+-                      AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE,
+-                      AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0),
+-                      AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1)
+-      },
+-      [AD3552R_CH_SELECT] = {
+-                      AD3552R_REG_ADDR_CH_SELECT_16B,
+-                      AD3552R_MASK_CH(0),
+-                      AD3552R_MASK_CH(1)
+-      }
+-};
+-
+ static u8 _ad3552r_reg_len(u8 addr)
+ {
+       switch (addr) {
+@@ -399,11 +321,6 @@ static int ad3552r_read_reg(struct ad3552r_desc *dac, u8 addr, u16 *val)
+       return 0;
+ }
+-static u16 ad3552r_field_prep(u16 val, u16 mask)
+-{
+-      return (val << __ffs(mask)) & mask;
+-}
+-
+ /* Update field of a register, shift val if needed */
+ static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask,
+                                   u16 val)
+@@ -416,21 +333,11 @@ static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask,
+               return ret;
+       reg &= ~mask;
+-      reg |= ad3552r_field_prep(val, mask);
++      reg |= val;
+       return ad3552r_write_reg(dac, addr, reg);
+ }
+-static int ad3552r_set_ch_value(struct ad3552r_desc *dac,
+-                              enum ad3552r_ch_attributes attr,
+-                              u8 ch,
+-                              u16 val)
+-{
+-      /* Update register related to attributes in chip */
+-      return ad3552r_update_reg_field(dac, addr_mask_map_ch[attr][0],
+-                                     addr_mask_map_ch[attr][ch + 1], val);
+-}
+-
+ #define AD3552R_CH_DAC(_idx) ((struct iio_chan_spec) {                \
+       .type = IIO_VOLTAGE,                                    \
+       .output = true,                                         \
+@@ -510,8 +417,14 @@ static int ad3552r_write_raw(struct iio_dev *indio_dev,
+                                       val);
+               break;
+       case IIO_CHAN_INFO_ENABLE:
+-              err = ad3552r_set_ch_value(dac, AD3552R_CH_DAC_POWERDOWN,
+-                                         chan->channel, !val);
++              if (chan->channel == 0)
++                      val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(0), !val);
++              else
++                      val = FIELD_PREP(AD3552R_MASK_CH_DAC_POWERDOWN(1), !val);
++
++              err = ad3552r_update_reg_field(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG,
++                                             AD3552R_MASK_CH_DAC_POWERDOWN(chan->channel),
++                                             val);
+               break;
+       default:
+               err = -EINVAL;
+@@ -721,9 +634,9 @@ static int ad3552r_reset(struct ad3552r_desc *dac)
+               return ret;
+       return ad3552r_update_reg_field(dac,
+-                                      addr_mask_map[AD3552R_ADDR_ASCENSION][0],
+-                                      addr_mask_map[AD3552R_ADDR_ASCENSION][1],
+-                                      val);
++                                      AD3552R_REG_ADDR_INTERFACE_CONFIG_A,
++                                      AD3552R_MASK_ADDR_ASCENSION,
++                                      FIELD_PREP(AD3552R_MASK_ADDR_ASCENSION, val));
+ }
+ static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min,
+@@ -818,20 +731,20 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
+                                    "mandatory custom-output-range-config property missing\n");
+       dac->ch_data[ch].range_override = 1;
+-      reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE);
++      reg |= FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1);
+       err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
+       if (err)
+               return dev_err_probe(dev, err,
+                                    "mandatory adi,gain-scaling-p property missing\n");
+-      reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P);
++      reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P, val);
+       dac->ch_data[ch].p = val;
+       err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
+       if (err)
+               return dev_err_probe(dev, err,
+                                    "mandatory adi,gain-scaling-n property missing\n");
+-      reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N);
++      reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N, val);
+       dac->ch_data[ch].n = val;
+       err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
+@@ -847,9 +760,9 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
+       dac->ch_data[ch].gain_offset = val;
+       offset = abs((s32)val);
+-      reg |= ad3552r_field_prep((offset >> 8), AD3552R_MASK_CH_OFFSET_BIT_8);
++      reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8, (offset >> 8));
+-      reg |= ad3552r_field_prep((s32)val < 0, AD3552R_MASK_CH_OFFSET_POLARITY);
++      reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY, (s32)val < 0);
+       addr = AD3552R_REG_ADDR_CH_GAIN(ch);
+       err = ad3552r_write_reg(dac, addr,
+                               offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
+@@ -892,9 +805,9 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+       }
+       err = ad3552r_update_reg_field(dac,
+-                                     addr_mask_map[AD3552R_VREF_SELECT][0],
+-                                     addr_mask_map[AD3552R_VREF_SELECT][1],
+-                                     val);
++                                     AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
++                                     AD3552R_MASK_REFERENCE_VOLTAGE_SEL,
++                                     FIELD_PREP(AD3552R_MASK_REFERENCE_VOLTAGE_SEL, val));
+       if (err)
+               return err;
+@@ -906,9 +819,9 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+               }
+               err = ad3552r_update_reg_field(dac,
+-                                             addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][0],
+-                                             addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][1],
+-                                             val);
++                                             AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
++                                             AD3552R_MASK_SDO_DRIVE_STRENGTH,
++                                             FIELD_PREP(AD3552R_MASK_SDO_DRIVE_STRENGTH, val));
+               if (err)
+                       return err;
+       }
+@@ -944,9 +857,15 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+                                                    "Invalid adi,output-range-microvolt value\n");
+                       val = err;
+-                      err = ad3552r_set_ch_value(dac,
+-                                                 AD3552R_CH_OUTPUT_RANGE_SEL,
+-                                                 ch, val);
++                      if (ch == 0)
++                              val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), val);
++                      else
++                              val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1), val);
++
++                      err = ad3552r_update_reg_field(dac,
++                                                     AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE,
++                                                     AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch),
++                                                     val);
+                       if (err)
+                               return err;
+@@ -964,7 +883,14 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+               ad3552r_calc_gain_and_offset(dac, ch);
+               dac->enabled_ch |= BIT(ch);
+-              err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1);
++              if (ch == 0)
++                      val = FIELD_PREP(AD3552R_MASK_CH(0), 1);
++              else
++                      val = FIELD_PREP(AD3552R_MASK_CH(1), 1);
++
++              err = ad3552r_update_reg_field(dac,
++                                             AD3552R_REG_ADDR_CH_SELECT_16B,
++                                             AD3552R_MASK_CH(ch), val);
+               if (err < 0)
+                       return err;
+@@ -976,8 +902,15 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+       /* Disable unused channels */
+       for_each_clear_bit(ch, &dac->enabled_ch,
+                          dac->model_data->num_hw_channels) {
+-              err = ad3552r_set_ch_value(dac, AD3552R_CH_AMPLIFIER_POWERDOWN,
+-                                         ch, 1);
++              if (ch == 0)
++                      val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), 1);
++              else
++                      val = FIELD_PREP(AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1), 1);
++
++              err = ad3552r_update_reg_field(dac,
++                                             AD3552R_REG_ADDR_POWERDOWN_CONFIG,
++                                             AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch),
++                                             val);
+               if (err)
+                       return err;
+       }
+-- 
+2.39.5
+
diff --git a/queue-6.12/iio-dac-ad3552r-common-fix-ad3541-2r-ranges.patch b/queue-6.12/iio-dac-ad3552r-common-fix-ad3541-2r-ranges.patch
new file mode 100644 (file)
index 0000000..16b6268
--- /dev/null
@@ -0,0 +1,95 @@
+From 4523a5cc80cc03d3ab8c0621c385dacf8aa02de8 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Jan 2025 18:29:15 +0100
+Subject: iio: dac: ad3552r-common: fix ad3541/2r ranges
+
+From: Angelo Dureghello <adureghello@baylibre.com>
+
+[ Upstream commit 1e758b613212b6964518a67939535910b5aee831 ]
+
+Fix ad3541/2r voltage ranges to be as per ad3542r datasheet,
+rev. C, table 38 (page 57).
+
+The wrong ad354xr ranges was generating erroneous Vpp output.
+
+In more details:
+- fix wrong number of ranges, they are 5 ranges, not 6,
+- remove non-existent 0-3V range,
+- adjust order, since ad3552r_find_range() get a wrong index,
+  producing a wrong Vpp as output.
+
+Retested all the ranges on real hardware, EVALAD3542RFMCZ:
+
+adi,output-range-microvolt (fdt):
+<(000000) (2500000)>;   ok (Rfbx1, switch 10)
+<(000000) (5000000)>;   ok (Rfbx1, switch 10)
+<(000000) (10000000)>;  ok (Rfbx1, switch 10)
+<(-5000000) (5000000)>; ok (Rfbx2, switch +/- 5)
+<(-2500000) (7500000)>; ok (Rfbx2, switch -2.5/7.5)
+
+Fixes: 8f2b54824b28 ("drivers:iio:dac: Add AD3552R driver support")
+Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
+Reviewed-by: David Lechner <dlechner@baylibre.com>
+Link: https://patch.msgid.link/20250108-wip-bl-ad3552r-axi-v0-iio-testing-carlos-v2-1-2dac02f04638@baylibre.com
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/dac/ad3552r-common.c | 5 ++---
+ drivers/iio/dac/ad3552r.h        | 9 ++++-----
+ 2 files changed, 6 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/iio/dac/ad3552r-common.c b/drivers/iio/dac/ad3552r-common.c
+index 2dfeca3656d21..94869ad15c27e 100644
+--- a/drivers/iio/dac/ad3552r-common.c
++++ b/drivers/iio/dac/ad3552r-common.c
+@@ -22,11 +22,10 @@ EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges, IIO_AD3552R);
+ const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2] = {
+       [AD3542R_CH_OUTPUT_RANGE_0__2P5V]       = { 0, 2500 },
+-      [AD3542R_CH_OUTPUT_RANGE_0__3V]         = { 0, 3000 },
+       [AD3542R_CH_OUTPUT_RANGE_0__5V]         = { 0, 5000 },
+       [AD3542R_CH_OUTPUT_RANGE_0__10V]        = { 0, 10000 },
+-      [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 },
+-      [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V]     = { -5000, 5000 }
++      [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V]     = { -5000, 5000 },
++      [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 }
+ };
+ EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges, IIO_AD3552R);
+diff --git a/drivers/iio/dac/ad3552r.h b/drivers/iio/dac/ad3552r.h
+index 7511e3f1882cb..c20f64f80d5db 100644
+--- a/drivers/iio/dac/ad3552r.h
++++ b/drivers/iio/dac/ad3552r.h
+@@ -128,7 +128,8 @@
+ #define AD3552R_LDAC_PULSE_US                         100
+ #define AD3552R_MAX_RANGES    5
+-#define AD3542R_MAX_RANGES    6
++#define AD3542R_MAX_RANGES    5
++#define AD3552R_QUAD_SPI      2
+ extern const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2];
+ extern const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2];
+@@ -185,16 +186,14 @@ enum ad3552r_ch_vref_select {
+ enum ad3542r_ch_output_range {
+       /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
+       AD3542R_CH_OUTPUT_RANGE_0__2P5V,
+-      /* Range from 0 V to 3 V. Requires Rfb1x connection  */
+-      AD3542R_CH_OUTPUT_RANGE_0__3V,
+       /* Range from 0 V to 5 V. Requires Rfb1x connection  */
+       AD3542R_CH_OUTPUT_RANGE_0__5V,
+       /* Range from 0 V to 10 V. Requires Rfb2x connection  */
+       AD3542R_CH_OUTPUT_RANGE_0__10V,
+-      /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection  */
+-      AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V,
+       /* Range from -5 V to 5 V. Requires Rfb2x connection  */
+       AD3542R_CH_OUTPUT_RANGE_NEG_5__5V,
++      /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection  */
++      AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V,
+ };
+ enum ad3552r_ch_output_range {
+-- 
+2.39.5
+
diff --git a/queue-6.12/iio-dac-ad3552r-extract-common-code-no-changes-in-be.patch b/queue-6.12/iio-dac-ad3552r-extract-common-code-no-changes-in-be.patch
new file mode 100644 (file)
index 0000000..4c83641
--- /dev/null
@@ -0,0 +1,1002 @@
+From 96255fea746ff28e655fa04a9003bba5f29a9dbc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 28 Oct 2024 22:45:33 +0100
+Subject: iio: dac: ad3552r: extract common code (no changes in behavior
+ intended)
+
+From: Angelo Dureghello <adureghello@baylibre.com>
+
+[ Upstream commit f665d7d33d7909cf51e2db0f0767ecab0295c0bd ]
+
+Extracting common code, to share common code to be used later
+by the AXI driver version (ad3552r-axi.c).
+
+Signed-off-by: Angelo Dureghello <adureghello@baylibre.com>
+Reviewed-by: David Lechner <dlechner@baylibre.com>
+Link: https://patch.msgid.link/20241028-wip-bl-ad3552r-axi-v0-iio-testing-v9-6-f6960b4f9719@kernel-space.org
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/dac/Makefile         |   2 +-
+ drivers/iio/dac/ad3552r-common.c | 249 +++++++++++++++++++
+ drivers/iio/dac/ad3552r.c        | 398 +++----------------------------
+ drivers/iio/dac/ad3552r.h        | 224 +++++++++++++++++
+ 4 files changed, 501 insertions(+), 372 deletions(-)
+ create mode 100644 drivers/iio/dac/ad3552r-common.c
+ create mode 100644 drivers/iio/dac/ad3552r.h
+
+diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
+index 2cf148f16306d..56a125f56284f 100644
+--- a/drivers/iio/dac/Makefile
++++ b/drivers/iio/dac/Makefile
+@@ -4,7 +4,7 @@
+ #
+ # When adding new entries keep the list in alphabetical order
+-obj-$(CONFIG_AD3552R) += ad3552r.o
++obj-$(CONFIG_AD3552R) += ad3552r.o ad3552r-common.o
+ obj-$(CONFIG_AD5360) += ad5360.o
+ obj-$(CONFIG_AD5380) += ad5380.o
+ obj-$(CONFIG_AD5421) += ad5421.o
+diff --git a/drivers/iio/dac/ad3552r-common.c b/drivers/iio/dac/ad3552r-common.c
+new file mode 100644
+index 0000000000000..2dfeca3656d21
+--- /dev/null
++++ b/drivers/iio/dac/ad3552r-common.c
+@@ -0,0 +1,249 @@
++// SPDX-License-Identifier: GPL-2.0+
++//
++// Copyright (c) 2010-2024 Analog Devices Inc.
++// Copyright (c) 2024 Baylibre, SAS
++
++#include <linux/bitfield.h>
++#include <linux/device.h>
++#include <linux/module.h>
++#include <linux/property.h>
++#include <linux/regulator/consumer.h>
++
++#include "ad3552r.h"
++
++const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2] = {
++      [AD3552R_CH_OUTPUT_RANGE_0__2P5V]       = { 0, 2500 },
++      [AD3552R_CH_OUTPUT_RANGE_0__5V]         = { 0, 5000 },
++      [AD3552R_CH_OUTPUT_RANGE_0__10V]        = { 0, 10000 },
++      [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V]     = { -5000, 5000 },
++      [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V]   = { -10000, 10000 }
++};
++EXPORT_SYMBOL_NS_GPL(ad3552r_ch_ranges, IIO_AD3552R);
++
++const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2] = {
++      [AD3542R_CH_OUTPUT_RANGE_0__2P5V]       = { 0, 2500 },
++      [AD3542R_CH_OUTPUT_RANGE_0__3V]         = { 0, 3000 },
++      [AD3542R_CH_OUTPUT_RANGE_0__5V]         = { 0, 5000 },
++      [AD3542R_CH_OUTPUT_RANGE_0__10V]        = { 0, 10000 },
++      [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = { -2500, 7500 },
++      [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V]     = { -5000, 5000 }
++};
++EXPORT_SYMBOL_NS_GPL(ad3542r_ch_ranges, IIO_AD3552R);
++
++/* Gain * AD3552R_GAIN_SCALE */
++static const s32 gains_scaling_table[] = {
++      [AD3552R_CH_GAIN_SCALING_1]             = 1000,
++      [AD3552R_CH_GAIN_SCALING_0_5]           = 500,
++      [AD3552R_CH_GAIN_SCALING_0_25]          = 250,
++      [AD3552R_CH_GAIN_SCALING_0_125]         = 125
++};
++
++u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs)
++{
++      return FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1) |
++             FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P, p) |
++             FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N, n) |
++             FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8, abs(goffs)) |
++             FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY, goffs < 0);
++}
++EXPORT_SYMBOL_NS_GPL(ad3552r_calc_custom_gain, IIO_AD3552R);
++
++static void ad3552r_get_custom_range(struct ad3552r_ch_data *ch_data,
++                                   s32 *v_min, s32 *v_max)
++{
++      s64 vref, tmp, common, offset, gn, gp;
++      /*
++       * From datasheet formula (In Volts):
++       *      Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03]
++       *      Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03]
++       * Calculus are converted to milivolts
++       */
++      vref = 2500;
++      /* 2.5 * 1.03 * 1000 (To mV) */
++      common = 2575 * ch_data->rfb;
++      offset = ch_data->gain_offset;
++
++      gn = gains_scaling_table[ch_data->n];
++      tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common;
++      tmp = div_s64(tmp, 1024  * AD3552R_GAIN_SCALE);
++      *v_max = vref + tmp;
++
++      gp = gains_scaling_table[ch_data->p];
++      tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common;
++      tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
++      *v_min = vref - tmp;
++}
++
++void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data *ch_data,
++                                const struct ad3552r_model_data *model_data)
++{
++      s32 idx, v_max, v_min, span, rem;
++      s64 tmp;
++
++      if (ch_data->range_override) {
++              ad3552r_get_custom_range(ch_data, &v_min, &v_max);
++      } else {
++              /* Normal range */
++              idx = ch_data->range;
++              v_min = model_data->ranges_table[idx][0];
++              v_max = model_data->ranges_table[idx][1];
++      }
++
++      /*
++       * From datasheet formula:
++       *      Vout = Span * (D / 65536) + Vmin
++       * Converted to scale and offset:
++       *      Scale = Span / 65536
++       *      Offset = 65536 * Vmin / Span
++       *
++       * Reminders are in micros in order to be printed as
++       * IIO_VAL_INT_PLUS_MICRO
++       */
++      span = v_max - v_min;
++      ch_data->scale_int = div_s64_rem(span, 65536, &rem);
++      /* Do operations in microvolts */
++      ch_data->scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000, 65536);
++
++      ch_data->offset_int = div_s64_rem(v_min * 65536, span, &rem);
++      tmp = (s64)rem * 1000000;
++      ch_data->offset_dec = div_s64(tmp, span);
++}
++EXPORT_SYMBOL_NS_GPL(ad3552r_calc_gain_and_offset, IIO_AD3552R);
++
++int ad3552r_get_ref_voltage(struct device *dev, u32 *val)
++{
++      int voltage;
++      int delta = 100000;
++
++      voltage = devm_regulator_get_enable_read_voltage(dev, "vref");
++      if (voltage < 0 && voltage != -ENODEV)
++              return dev_err_probe(dev, voltage,
++                                   "Error getting vref voltage\n");
++
++      if (voltage == -ENODEV) {
++              if (device_property_read_bool(dev, "adi,vref-out-en"))
++                      *val = AD3552R_INTERNAL_VREF_PIN_2P5V;
++              else
++                      *val = AD3552R_INTERNAL_VREF_PIN_FLOATING;
++
++              return 0;
++      }
++
++      if (voltage > 2500000 + delta || voltage < 2500000 - delta) {
++              dev_warn(dev, "vref-supply must be 2.5V");
++              return -EINVAL;
++      }
++
++      *val = AD3552R_EXTERNAL_VREF_PIN_INPUT;
++
++      return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ad3552r_get_ref_voltage, IIO_AD3552R);
++
++int ad3552r_get_drive_strength(struct device *dev, u32 *val)
++{
++      int err;
++      u32 drive_strength;
++
++      err = device_property_read_u32(dev, "adi,sdo-drive-strength",
++                                     &drive_strength);
++      if (err)
++              return err;
++
++      if (drive_strength > 3) {
++              dev_err_probe(dev, -EINVAL,
++                            "adi,sdo-drive-strength must be less than 4\n");
++              return -EINVAL;
++      }
++
++      *val = drive_strength;
++
++      return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ad3552r_get_drive_strength, IIO_AD3552R);
++
++int ad3552r_get_custom_gain(struct device *dev, struct fwnode_handle *child,
++                          u8 *gs_p, u8 *gs_n, u16 *rfb, s16 *goffs)
++{
++      int err;
++      u32 val;
++      struct fwnode_handle *gain_child __free(fwnode_handle) =
++              fwnode_get_named_child_node(child,
++                                          "custom-output-range-config");
++
++      if (!gain_child)
++              return dev_err_probe(dev, -EINVAL,
++                                   "custom-output-range-config mandatory\n");
++
++      err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
++      if (err)
++              return dev_err_probe(dev, err,
++                                   "adi,gain-scaling-p mandatory\n");
++      *gs_p = val;
++
++      err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
++      if (err)
++              return dev_err_probe(dev, err,
++                                   "adi,gain-scaling-n property mandatory\n");
++      *gs_n = val;
++
++      err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
++      if (err)
++              return dev_err_probe(dev, err,
++                                   "adi,rfb-ohms mandatory\n");
++      *rfb = val;
++
++      err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
++      if (err)
++              return dev_err_probe(dev, err,
++                                   "adi,gain-offset mandatory\n");
++      *goffs = val;
++
++      return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ad3552r_get_custom_gain, IIO_AD3552R);
++
++static int ad3552r_find_range(const struct ad3552r_model_data *model_info,
++                            s32 *vals)
++{
++      int i;
++
++      for (i = 0; i < model_info->num_ranges; i++)
++              if (vals[0] == model_info->ranges_table[i][0] * 1000 &&
++                  vals[1] == model_info->ranges_table[i][1] * 1000)
++                      return i;
++
++      return -EINVAL;
++}
++
++int ad3552r_get_output_range(struct device *dev,
++                           const struct ad3552r_model_data *model_info,
++                           struct fwnode_handle *child, u32 *val)
++{
++      int ret;
++      s32 vals[2];
++
++      /* This property is optional, so returning -ENOENT if missing */
++      if (!fwnode_property_present(child, "adi,output-range-microvolt"))
++              return -ENOENT;
++
++      ret = fwnode_property_read_u32_array(child,
++                                           "adi,output-range-microvolt",
++                                           vals, 2);
++      if (ret)
++              return dev_err_probe(dev, ret,
++                              "invalid adi,output-range-microvolt\n");
++
++      ret = ad3552r_find_range(model_info, vals);
++      if (ret < 0)
++              return dev_err_probe(dev, ret,
++                      "invalid adi,output-range-microvolt value\n");
++
++      *val = ret;
++
++      return 0;
++}
++EXPORT_SYMBOL_NS_GPL(ad3552r_get_output_range, IIO_AD3552R);
++
++MODULE_DESCRIPTION("ad3552r common functions");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c
+index aa453d3de5e1c..5b2ce2aa67a47 100644
+--- a/drivers/iio/dac/ad3552r.c
++++ b/drivers/iio/dac/ad3552r.c
+@@ -12,226 +12,9 @@
+ #include <linux/iio/trigger_consumer.h>
+ #include <linux/iopoll.h>
+ #include <linux/kernel.h>
+-#include <linux/regulator/consumer.h>
+ #include <linux/spi/spi.h>
+-/* Register addresses */
+-/* Primary address space */
+-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A           0x00
+-#define   AD3552R_MASK_SOFTWARE_RESET                 (BIT(7) | BIT(0))
+-#define   AD3552R_MASK_ADDR_ASCENSION                 BIT(5)
+-#define   AD3552R_MASK_SDO_ACTIVE                     BIT(4)
+-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B           0x01
+-#define   AD3552R_MASK_SINGLE_INST                    BIT(7)
+-#define   AD3552R_MASK_SHORT_INSTRUCTION              BIT(3)
+-#define AD3552R_REG_ADDR_DEVICE_CONFIG                        0x02
+-#define   AD3552R_MASK_DEVICE_STATUS(n)                       BIT(4 + (n))
+-#define   AD3552R_MASK_CUSTOM_MODES                   GENMASK(3, 2)
+-#define   AD3552R_MASK_OPERATING_MODES                        GENMASK(1, 0)
+-#define AD3552R_REG_ADDR_CHIP_TYPE                    0x03
+-#define   AD3552R_MASK_CLASS                          GENMASK(7, 0)
+-#define AD3552R_REG_ADDR_PRODUCT_ID_L                 0x04
+-#define AD3552R_REG_ADDR_PRODUCT_ID_H                 0x05
+-#define AD3552R_REG_ADDR_CHIP_GRADE                   0x06
+-#define   AD3552R_MASK_GRADE                          GENMASK(7, 4)
+-#define   AD3552R_MASK_DEVICE_REVISION                        GENMASK(3, 0)
+-#define AD3552R_REG_ADDR_SCRATCH_PAD                  0x0A
+-#define AD3552R_REG_ADDR_SPI_REVISION                 0x0B
+-#define AD3552R_REG_ADDR_VENDOR_L                     0x0C
+-#define AD3552R_REG_ADDR_VENDOR_H                     0x0D
+-#define AD3552R_REG_ADDR_STREAM_MODE                  0x0E
+-#define   AD3552R_MASK_LENGTH                         GENMASK(7, 0)
+-#define AD3552R_REG_ADDR_TRANSFER_REGISTER            0x0F
+-#define   AD3552R_MASK_MULTI_IO_MODE                  GENMASK(7, 6)
+-#define   AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE               BIT(2)
+-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C           0x10
+-#define   AD3552R_MASK_CRC_ENABLE                     (GENMASK(7, 6) |\
+-                                                       GENMASK(1, 0))
+-#define   AD3552R_MASK_STRICT_REGISTER_ACCESS         BIT(5)
+-#define AD3552R_REG_ADDR_INTERFACE_STATUS_A           0x11
+-#define   AD3552R_MASK_INTERFACE_NOT_READY            BIT(7)
+-#define   AD3552R_MASK_CLOCK_COUNTING_ERROR           BIT(5)
+-#define   AD3552R_MASK_INVALID_OR_NO_CRC              BIT(3)
+-#define   AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER    BIT(2)
+-#define   AD3552R_MASK_PARTIAL_REGISTER_ACCESS                BIT(1)
+-#define   AD3552R_MASK_REGISTER_ADDRESS_INVALID               BIT(0)
+-#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D           0x14
+-#define   AD3552R_MASK_ALERT_ENABLE_PULLUP            BIT(6)
+-#define   AD3552R_MASK_MEM_CRC_EN                     BIT(4)
+-#define   AD3552R_MASK_SDO_DRIVE_STRENGTH             GENMASK(3, 2)
+-#define   AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN               BIT(1)
+-#define   AD3552R_MASK_SPI_CONFIG_DDR                 BIT(0)
+-#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG          0x15
+-#define   AD3552R_MASK_IDUMP_FAST_MODE                        BIT(6)
+-#define   AD3552R_MASK_SAMPLE_HOLD_DIFFERENTIAL_USER_EN       BIT(5)
+-#define   AD3552R_MASK_SAMPLE_HOLD_USER_TRIM          GENMASK(4, 3)
+-#define   AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE                BIT(2)
+-#define   AD3552R_MASK_REFERENCE_VOLTAGE_SEL          GENMASK(1, 0)
+-#define AD3552R_REG_ADDR_ERR_ALARM_MASK                       0x16
+-#define   AD3552R_MASK_REF_RANGE_ALARM                        BIT(6)
+-#define   AD3552R_MASK_CLOCK_COUNT_ERR_ALARM          BIT(5)
+-#define   AD3552R_MASK_MEM_CRC_ERR_ALARM              BIT(4)
+-#define   AD3552R_MASK_SPI_CRC_ERR_ALARM              BIT(3)
+-#define   AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM               BIT(2)
+-#define   AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM  BIT(1)
+-#define   AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0)
+-#define AD3552R_REG_ADDR_ERR_STATUS                   0x17
+-#define   AD3552R_MASK_REF_RANGE_ERR_STATUS                   BIT(6)
+-#define   AD3552R_MASK_DUAL_SPI_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5)
+-#define   AD3552R_MASK_MEM_CRC_ERR_STATUS                     BIT(4)
+-#define   AD3552R_MASK_RESET_STATUS                           BIT(0)
+-#define AD3552R_REG_ADDR_POWERDOWN_CONFIG             0x18
+-#define   AD3552R_MASK_CH_DAC_POWERDOWN(ch)           BIT(4 + (ch))
+-#define   AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch)     BIT(ch)
+-#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE         0x19
+-#define   AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch)                ((ch) ? GENMASK(7, 4) :\
+-                                                       GENMASK(3, 0))
+-#define AD3552R_REG_ADDR_CH_OFFSET(ch)                        (0x1B + (ch) * 2)
+-#define   AD3552R_MASK_CH_OFFSET_BITS_0_7             GENMASK(7, 0)
+-#define AD3552R_REG_ADDR_CH_GAIN(ch)                  (0x1C + (ch) * 2)
+-#define   AD3552R_MASK_CH_RANGE_OVERRIDE              BIT(7)
+-#define   AD3552R_MASK_CH_GAIN_SCALING_N              GENMASK(6, 5)
+-#define   AD3552R_MASK_CH_GAIN_SCALING_P              GENMASK(4, 3)
+-#define   AD3552R_MASK_CH_OFFSET_POLARITY             BIT(2)
+-#define   AD3552R_MASK_CH_OFFSET_BIT_8                        BIT(0)
+-/*
+- * Secondary region
+- * For multibyte registers specify the highest address because the access is
+- * done in descending order
+- */
+-#define AD3552R_SECONDARY_REGION_START                        0x28
+-#define AD3552R_REG_ADDR_HW_LDAC_16B                  0x28
+-#define AD3552R_REG_ADDR_CH_DAC_16B(ch)                       (0x2C - (1 - ch) * 2)
+-#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B            0x2E
+-#define AD3552R_REG_ADDR_CH_SELECT_16B                        0x2F
+-#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B          0x31
+-#define AD3552R_REG_ADDR_SW_LDAC_16B                  0x32
+-#define AD3552R_REG_ADDR_CH_INPUT_16B(ch)             (0x36 - (1 - ch) * 2)
+-/* 3 bytes registers */
+-#define AD3552R_REG_START_24B                         0x37
+-#define AD3552R_REG_ADDR_HW_LDAC_24B                  0x37
+-#define AD3552R_REG_ADDR_CH_DAC_24B(ch)                       (0x3D - (1 - ch) * 3)
+-#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B            0x40
+-#define AD3552R_REG_ADDR_CH_SELECT_24B                        0x41
+-#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B          0x44
+-#define AD3552R_REG_ADDR_SW_LDAC_24B                  0x45
+-#define AD3552R_REG_ADDR_CH_INPUT_24B(ch)             (0x4B - (1 - ch) * 3)
+-
+-/* Useful defines */
+-#define AD3552R_MAX_CH                                        2
+-#define AD3552R_MASK_CH(ch)                           BIT(ch)
+-#define AD3552R_MASK_ALL_CH                           GENMASK(1, 0)
+-#define AD3552R_MAX_REG_SIZE                          3
+-#define AD3552R_READ_BIT                              BIT(7)
+-#define AD3552R_ADDR_MASK                             GENMASK(6, 0)
+-#define AD3552R_MASK_DAC_12B                          0xFFF0
+-#define AD3552R_DEFAULT_CONFIG_B_VALUE                        0x8
+-#define AD3552R_SCRATCH_PAD_TEST_VAL1                 0x34
+-#define AD3552R_SCRATCH_PAD_TEST_VAL2                 0xB2
+-#define AD3552R_GAIN_SCALE                            1000
+-#define AD3552R_LDAC_PULSE_US                         100
+-
+-enum ad3552r_ch_vref_select {
+-      /* Internal source with Vref I/O floating */
+-      AD3552R_INTERNAL_VREF_PIN_FLOATING,
+-      /* Internal source with Vref I/O at 2.5V */
+-      AD3552R_INTERNAL_VREF_PIN_2P5V,
+-      /* External source with Vref I/O as input */
+-      AD3552R_EXTERNAL_VREF_PIN_INPUT
+-};
+-
+-enum ad3552r_id {
+-      AD3541R_ID = 0x400b,
+-      AD3542R_ID = 0x4009,
+-      AD3551R_ID = 0x400a,
+-      AD3552R_ID = 0x4008,
+-};
+-
+-enum ad3552r_ch_output_range {
+-      /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
+-      AD3552R_CH_OUTPUT_RANGE_0__2P5V,
+-      /* Range from 0 V to 5 V. Requires Rfb1x connection  */
+-      AD3552R_CH_OUTPUT_RANGE_0__5V,
+-      /* Range from 0 V to 10 V. Requires Rfb2x connection  */
+-      AD3552R_CH_OUTPUT_RANGE_0__10V,
+-      /* Range from -5 V to 5 V. Requires Rfb2x connection  */
+-      AD3552R_CH_OUTPUT_RANGE_NEG_5__5V,
+-      /* Range from -10 V to 10 V. Requires Rfb4x connection  */
+-      AD3552R_CH_OUTPUT_RANGE_NEG_10__10V,
+-};
+-
+-static const s32 ad3552r_ch_ranges[][2] = {
+-      [AD3552R_CH_OUTPUT_RANGE_0__2P5V]       = {0, 2500},
+-      [AD3552R_CH_OUTPUT_RANGE_0__5V]         = {0, 5000},
+-      [AD3552R_CH_OUTPUT_RANGE_0__10V]        = {0, 10000},
+-      [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V]     = {-5000, 5000},
+-      [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V]   = {-10000, 10000}
+-};
+-
+-enum ad3542r_ch_output_range {
+-      /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
+-      AD3542R_CH_OUTPUT_RANGE_0__2P5V,
+-      /* Range from 0 V to 3 V. Requires Rfb1x connection  */
+-      AD3542R_CH_OUTPUT_RANGE_0__3V,
+-      /* Range from 0 V to 5 V. Requires Rfb1x connection  */
+-      AD3542R_CH_OUTPUT_RANGE_0__5V,
+-      /* Range from 0 V to 10 V. Requires Rfb2x connection  */
+-      AD3542R_CH_OUTPUT_RANGE_0__10V,
+-      /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection  */
+-      AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V,
+-      /* Range from -5 V to 5 V. Requires Rfb2x connection  */
+-      AD3542R_CH_OUTPUT_RANGE_NEG_5__5V,
+-};
+-
+-static const s32 ad3542r_ch_ranges[][2] = {
+-      [AD3542R_CH_OUTPUT_RANGE_0__2P5V]       = {0, 2500},
+-      [AD3542R_CH_OUTPUT_RANGE_0__3V]         = {0, 3000},
+-      [AD3542R_CH_OUTPUT_RANGE_0__5V]         = {0, 5000},
+-      [AD3542R_CH_OUTPUT_RANGE_0__10V]        = {0, 10000},
+-      [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = {-2500, 7500},
+-      [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V]     = {-5000, 5000}
+-};
+-
+-enum ad3552r_ch_gain_scaling {
+-      /* Gain scaling of 1 */
+-      AD3552R_CH_GAIN_SCALING_1,
+-      /* Gain scaling of 0.5 */
+-      AD3552R_CH_GAIN_SCALING_0_5,
+-      /* Gain scaling of 0.25 */
+-      AD3552R_CH_GAIN_SCALING_0_25,
+-      /* Gain scaling of 0.125 */
+-      AD3552R_CH_GAIN_SCALING_0_125,
+-};
+-
+-/* Gain * AD3552R_GAIN_SCALE */
+-static const s32 gains_scaling_table[] = {
+-      [AD3552R_CH_GAIN_SCALING_1]             = 1000,
+-      [AD3552R_CH_GAIN_SCALING_0_5]           = 500,
+-      [AD3552R_CH_GAIN_SCALING_0_25]          = 250,
+-      [AD3552R_CH_GAIN_SCALING_0_125]         = 125
+-};
+-
+-struct ad3552r_ch_data {
+-      s32     scale_int;
+-      s32     scale_dec;
+-      s32     offset_int;
+-      s32     offset_dec;
+-      s16     gain_offset;
+-      u16     rfb;
+-      u8      n;
+-      u8      p;
+-      u8      range;
+-      bool    range_override;
+-};
+-
+-struct ad3552r_model_data {
+-      const char *model_name;
+-      enum ad3552r_id chip_id;
+-      unsigned int num_hw_channels;
+-      const s32 (*ranges_table)[2];
+-      int num_ranges;
+-      bool requires_output_range;
+-};
++#include "ad3552r.h"
+ struct ad3552r_desc {
+       const struct ad3552r_model_data *model_data;
+@@ -639,136 +422,35 @@ static int ad3552r_reset(struct ad3552r_desc *dac)
+                                       FIELD_PREP(AD3552R_MASK_ADDR_ASCENSION, val));
+ }
+-static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min,
+-                                   s32 *v_max)
+-{
+-      s64 vref, tmp, common, offset, gn, gp;
+-      /*
+-       * From datasheet formula (In Volts):
+-       *      Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03]
+-       *      Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03]
+-       * Calculus are converted to milivolts
+-       */
+-      vref = 2500;
+-      /* 2.5 * 1.03 * 1000 (To mV) */
+-      common = 2575 * dac->ch_data[i].rfb;
+-      offset = dac->ch_data[i].gain_offset;
+-
+-      gn = gains_scaling_table[dac->ch_data[i].n];
+-      tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common;
+-      tmp = div_s64(tmp, 1024  * AD3552R_GAIN_SCALE);
+-      *v_max = vref + tmp;
+-
+-      gp = gains_scaling_table[dac->ch_data[i].p];
+-      tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common;
+-      tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE);
+-      *v_min = vref - tmp;
+-}
+-
+-static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch)
+-{
+-      s32 idx, v_max, v_min, span, rem;
+-      s64 tmp;
+-
+-      if (dac->ch_data[ch].range_override) {
+-              ad3552r_get_custom_range(dac, ch, &v_min, &v_max);
+-      } else {
+-              /* Normal range */
+-              idx = dac->ch_data[ch].range;
+-              v_min = dac->model_data->ranges_table[idx][0];
+-              v_max = dac->model_data->ranges_table[idx][1];
+-      }
+-
+-      /*
+-       * From datasheet formula:
+-       *      Vout = Span * (D / 65536) + Vmin
+-       * Converted to scale and offset:
+-       *      Scale = Span / 65536
+-       *      Offset = 65536 * Vmin / Span
+-       *
+-       * Reminders are in micros in order to be printed as
+-       * IIO_VAL_INT_PLUS_MICRO
+-       */
+-      span = v_max - v_min;
+-      dac->ch_data[ch].scale_int = div_s64_rem(span, 65536, &rem);
+-      /* Do operations in microvolts */
+-      dac->ch_data[ch].scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000,
+-                                                      65536);
+-
+-      dac->ch_data[ch].offset_int = div_s64_rem(v_min * 65536, span, &rem);
+-      tmp = (s64)rem * 1000000;
+-      dac->ch_data[ch].offset_dec = div_s64(tmp, span);
+-}
+-
+-static int ad3552r_find_range(const struct ad3552r_model_data *model_data,
+-                            s32 *vals)
+-{
+-      int i;
+-
+-      for (i = 0; i < model_data->num_ranges; i++)
+-              if (vals[0] == model_data->ranges_table[i][0] * 1000 &&
+-                  vals[1] == model_data->ranges_table[i][1] * 1000)
+-                      return i;
+-
+-      return -EINVAL;
+-}
+-
+ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
+                                        struct fwnode_handle *child,
+                                        u32 ch)
+ {
+       struct device *dev = &dac->spi->dev;
+-      u32 val;
+       int err;
+       u8 addr;
+-      u16 reg = 0, offset;
+-
+-      struct fwnode_handle *gain_child __free(fwnode_handle)
+-              = fwnode_get_named_child_node(child,
+-                                            "custom-output-range-config");
+-      if (!gain_child)
+-              return dev_err_probe(dev, -EINVAL,
+-                                   "mandatory custom-output-range-config property missing\n");
+-
+-      dac->ch_data[ch].range_override = 1;
+-      reg |= FIELD_PREP(AD3552R_MASK_CH_RANGE_OVERRIDE, 1);
+-
+-      err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val);
+-      if (err)
+-              return dev_err_probe(dev, err,
+-                                   "mandatory adi,gain-scaling-p property missing\n");
+-      reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_P, val);
+-      dac->ch_data[ch].p = val;
+-
+-      err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val);
+-      if (err)
+-              return dev_err_probe(dev, err,
+-                                   "mandatory adi,gain-scaling-n property missing\n");
+-      reg |= FIELD_PREP(AD3552R_MASK_CH_GAIN_SCALING_N, val);
+-      dac->ch_data[ch].n = val;
+-
+-      err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val);
+-      if (err)
+-              return dev_err_probe(dev, err,
+-                                   "mandatory adi,rfb-ohms property missing\n");
+-      dac->ch_data[ch].rfb = val;
++      u16 reg;
+-      err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val);
++      err = ad3552r_get_custom_gain(dev, child,
++                                    &dac->ch_data[ch].p,
++                                    &dac->ch_data[ch].n,
++                                    &dac->ch_data[ch].rfb,
++                                    &dac->ch_data[ch].gain_offset);
+       if (err)
+-              return dev_err_probe(dev, err,
+-                                   "mandatory adi,gain-offset property missing\n");
+-      dac->ch_data[ch].gain_offset = val;
++              return err;
+-      offset = abs((s32)val);
+-      reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_BIT_8, (offset >> 8));
++      dac->ch_data[ch].range_override = 1;
+-      reg |= FIELD_PREP(AD3552R_MASK_CH_OFFSET_POLARITY, (s32)val < 0);
+       addr = AD3552R_REG_ADDR_CH_GAIN(ch);
+       err = ad3552r_write_reg(dac, addr,
+-                              offset & AD3552R_MASK_CH_OFFSET_BITS_0_7);
++                              abs((s32)dac->ch_data[ch].gain_offset) &
++                              AD3552R_MASK_CH_OFFSET_BITS_0_7);
+       if (err)
+               return dev_err_probe(dev, err, "Error writing register\n");
++      reg = ad3552r_calc_custom_gain(dac->ch_data[ch].p, dac->ch_data[ch].n,
++                                     dac->ch_data[ch].gain_offset);
++
+       err = ad3552r_write_reg(dac, addr, reg);
+       if (err)
+               return dev_err_probe(dev, err, "Error writing register\n");
+@@ -779,30 +461,17 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac,
+ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+ {
+       struct device *dev = &dac->spi->dev;
+-      int err, cnt = 0, voltage, delta = 100000;
+-      u32 vals[2], val, ch;
++      int err, cnt = 0;
++      u32 val, ch;
+       dac->gpio_ldac = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH);
+       if (IS_ERR(dac->gpio_ldac))
+               return dev_err_probe(dev, PTR_ERR(dac->gpio_ldac),
+                                    "Error getting gpio ldac");
+-      voltage = devm_regulator_get_enable_read_voltage(dev, "vref");
+-      if (voltage < 0 && voltage != -ENODEV)
+-              return dev_err_probe(dev, voltage, "Error getting vref voltage\n");
+-
+-      if (voltage == -ENODEV) {
+-              if (device_property_read_bool(dev, "adi,vref-out-en"))
+-                      val = AD3552R_INTERNAL_VREF_PIN_2P5V;
+-              else
+-                      val = AD3552R_INTERNAL_VREF_PIN_FLOATING;
+-      } else {
+-              if (voltage > 2500000 + delta || voltage < 2500000 - delta) {
+-                      dev_warn(dev, "vref-supply must be 2.5V");
+-                      return -EINVAL;
+-              }
+-              val = AD3552R_EXTERNAL_VREF_PIN_INPUT;
+-      }
++      err = ad3552r_get_ref_voltage(dev, &val);
++      if (err < 0)
++              return err;
+       err = ad3552r_update_reg_field(dac,
+                                      AD3552R_REG_ADDR_SH_REFERENCE_CONFIG,
+@@ -811,13 +480,8 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+       if (err)
+               return err;
+-      err = device_property_read_u32(dev, "adi,sdo-drive-strength", &val);
++      err = ad3552r_get_drive_strength(dev, &val);
+       if (!err) {
+-              if (val > 3) {
+-                      dev_err(dev, "adi,sdo-drive-strength must be less than 4\n");
+-                      return -EINVAL;
+-              }
+-
+               err = ad3552r_update_reg_field(dac,
+                                              AD3552R_REG_ADDR_INTERFACE_CONFIG_D,
+                                              AD3552R_MASK_SDO_DRIVE_STRENGTH,
+@@ -842,21 +506,12 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+                                            "reg must be less than %d\n",
+                                            dac->model_data->num_hw_channels);
+-              if (fwnode_property_present(child, "adi,output-range-microvolt")) {
+-                      err = fwnode_property_read_u32_array(child,
+-                                                           "adi,output-range-microvolt",
+-                                                           vals,
+-                                                           2);
+-                      if (err)
+-                              return dev_err_probe(dev, err,
+-                                      "adi,output-range-microvolt property could not be parsed\n");
+-
+-                      err = ad3552r_find_range(dac->model_data, vals);
+-                      if (err < 0)
+-                              return dev_err_probe(dev, err,
+-                                                   "Invalid adi,output-range-microvolt value\n");
++              err = ad3552r_get_output_range(dev, dac->model_data,
++                                             child, &val);
++              if (err && err != -ENOENT)
++                      return err;
+-                      val = err;
++              if (!err) {
+                       if (ch == 0)
+                               val = FIELD_PREP(AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), val);
+                       else
+@@ -880,7 +535,7 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac)
+                               return err;
+               }
+-              ad3552r_calc_gain_and_offset(dac, ch);
++              ad3552r_calc_gain_and_offset(&dac->ch_data[ch], dac->model_data);
+               dac->enabled_ch |= BIT(ch);
+               if (ch == 0)
+@@ -1079,3 +734,4 @@ module_spi_driver(ad3552r_driver);
+ MODULE_AUTHOR("Mihail Chindris <mihail.chindris@analog.com>");
+ MODULE_DESCRIPTION("Analog Device AD3552R DAC");
+ MODULE_LICENSE("GPL v2");
++MODULE_IMPORT_NS(IIO_AD3552R);
+diff --git a/drivers/iio/dac/ad3552r.h b/drivers/iio/dac/ad3552r.h
+new file mode 100644
+index 0000000000000..7511e3f1882cb
+--- /dev/null
++++ b/drivers/iio/dac/ad3552r.h
+@@ -0,0 +1,224 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++/*
++ * AD3552R Digital <-> Analog converters common header
++ *
++ * Copyright 2021-2024 Analog Devices Inc.
++ * Author: Angelo Dureghello <adureghello@baylibre.com>
++ */
++
++#ifndef __DRIVERS_IIO_DAC_AD3552R_H__
++#define __DRIVERS_IIO_DAC_AD3552R_H__
++
++/* Register addresses */
++/* Primary address space */
++#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A           0x00
++#define   AD3552R_MASK_SOFTWARE_RESET                 (BIT(7) | BIT(0))
++#define   AD3552R_MASK_ADDR_ASCENSION                 BIT(5)
++#define   AD3552R_MASK_SDO_ACTIVE                     BIT(4)
++#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B           0x01
++#define   AD3552R_MASK_SINGLE_INST                    BIT(7)
++#define   AD3552R_MASK_SHORT_INSTRUCTION              BIT(3)
++#define AD3552R_REG_ADDR_DEVICE_CONFIG                        0x02
++#define   AD3552R_MASK_DEVICE_STATUS(n)                       BIT(4 + (n))
++#define   AD3552R_MASK_CUSTOM_MODES                   GENMASK(3, 2)
++#define   AD3552R_MASK_OPERATING_MODES                        GENMASK(1, 0)
++#define AD3552R_REG_ADDR_CHIP_TYPE                    0x03
++#define   AD3552R_MASK_CLASS                          GENMASK(7, 0)
++#define AD3552R_REG_ADDR_PRODUCT_ID_L                 0x04
++#define AD3552R_REG_ADDR_PRODUCT_ID_H                 0x05
++#define AD3552R_REG_ADDR_CHIP_GRADE                   0x06
++#define   AD3552R_MASK_GRADE                          GENMASK(7, 4)
++#define   AD3552R_MASK_DEVICE_REVISION                        GENMASK(3, 0)
++#define AD3552R_REG_ADDR_SCRATCH_PAD                  0x0A
++#define AD3552R_REG_ADDR_SPI_REVISION                 0x0B
++#define AD3552R_REG_ADDR_VENDOR_L                     0x0C
++#define AD3552R_REG_ADDR_VENDOR_H                     0x0D
++#define AD3552R_REG_ADDR_STREAM_MODE                  0x0E
++#define   AD3552R_MASK_LENGTH                         GENMASK(7, 0)
++#define AD3552R_REG_ADDR_TRANSFER_REGISTER            0x0F
++#define   AD3552R_MASK_MULTI_IO_MODE                  GENMASK(7, 6)
++#define   AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE               BIT(2)
++#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C           0x10
++#define   AD3552R_MASK_CRC_ENABLE \
++              (GENMASK(7, 6) | GENMASK(1, 0))
++#define   AD3552R_MASK_STRICT_REGISTER_ACCESS         BIT(5)
++#define AD3552R_REG_ADDR_INTERFACE_STATUS_A           0x11
++#define   AD3552R_MASK_INTERFACE_NOT_READY            BIT(7)
++#define   AD3552R_MASK_CLOCK_COUNTING_ERROR           BIT(5)
++#define   AD3552R_MASK_INVALID_OR_NO_CRC              BIT(3)
++#define   AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER    BIT(2)
++#define   AD3552R_MASK_PARTIAL_REGISTER_ACCESS                BIT(1)
++#define   AD3552R_MASK_REGISTER_ADDRESS_INVALID               BIT(0)
++#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D           0x14
++#define   AD3552R_MASK_ALERT_ENABLE_PULLUP            BIT(6)
++#define   AD3552R_MASK_MEM_CRC_EN                     BIT(4)
++#define   AD3552R_MASK_SDO_DRIVE_STRENGTH             GENMASK(3, 2)
++#define   AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN               BIT(1)
++#define   AD3552R_MASK_SPI_CONFIG_DDR                 BIT(0)
++#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG          0x15
++#define   AD3552R_MASK_IDUMP_FAST_MODE                        BIT(6)
++#define   AD3552R_MASK_SAMPLE_HOLD_DIFF_USER_EN               BIT(5)
++#define   AD3552R_MASK_SAMPLE_HOLD_USER_TRIM          GENMASK(4, 3)
++#define   AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE                BIT(2)
++#define   AD3552R_MASK_REFERENCE_VOLTAGE_SEL          GENMASK(1, 0)
++#define AD3552R_REG_ADDR_ERR_ALARM_MASK                       0x16
++#define   AD3552R_MASK_REF_RANGE_ALARM                        BIT(6)
++#define   AD3552R_MASK_CLOCK_COUNT_ERR_ALARM          BIT(5)
++#define   AD3552R_MASK_MEM_CRC_ERR_ALARM              BIT(4)
++#define   AD3552R_MASK_SPI_CRC_ERR_ALARM              BIT(3)
++#define   AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM               BIT(2)
++#define   AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM  BIT(1)
++#define   AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0)
++#define AD3552R_REG_ADDR_ERR_STATUS                   0x17
++#define   AD3552R_MASK_REF_RANGE_ERR_STATUS           BIT(6)
++#define   AD3552R_MASK_STREAM_EXCEEDS_DAC_ERR_STATUS  BIT(5)
++#define   AD3552R_MASK_MEM_CRC_ERR_STATUS             BIT(4)
++#define   AD3552R_MASK_RESET_STATUS                   BIT(0)
++#define AD3552R_REG_ADDR_POWERDOWN_CONFIG             0x18
++#define   AD3552R_MASK_CH_DAC_POWERDOWN(ch)           BIT(4 + (ch))
++#define   AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch)     BIT(ch)
++#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE         0x19
++#define   AD3552R_MASK_CH0_RANGE                      GENMASK(2, 0)
++#define   AD3552R_MASK_CH1_RANGE                      GENMASK(6, 4)
++#define   AD3552R_MASK_CH_OUTPUT_RANGE                        GENMASK(7, 0)
++#define   AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) \
++              ((ch) ? GENMASK(7, 4) : GENMASK(3, 0))
++#define AD3552R_REG_ADDR_CH_OFFSET(ch)                        (0x1B + (ch) * 2)
++#define   AD3552R_MASK_CH_OFFSET_BITS_0_7             GENMASK(7, 0)
++#define AD3552R_REG_ADDR_CH_GAIN(ch)                  (0x1C + (ch) * 2)
++#define   AD3552R_MASK_CH_RANGE_OVERRIDE              BIT(7)
++#define   AD3552R_MASK_CH_GAIN_SCALING_N              GENMASK(6, 5)
++#define   AD3552R_MASK_CH_GAIN_SCALING_P              GENMASK(4, 3)
++#define   AD3552R_MASK_CH_OFFSET_POLARITY             BIT(2)
++#define   AD3552R_MASK_CH_OFFSET_BIT_8                        BIT(8)
++/*
++ * Secondary region
++ * For multibyte registers specify the highest address because the access is
++ * done in descending order
++ */
++#define AD3552R_SECONDARY_REGION_START                        0x28
++#define AD3552R_REG_ADDR_HW_LDAC_16B                  0x28
++#define AD3552R_REG_ADDR_CH_DAC_16B(ch)                       (0x2C - (1 - (ch)) * 2)
++#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B            0x2E
++#define AD3552R_REG_ADDR_CH_SELECT_16B                        0x2F
++#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B          0x31
++#define AD3552R_REG_ADDR_SW_LDAC_16B                  0x32
++#define AD3552R_REG_ADDR_CH_INPUT_16B(ch)             (0x36 - (1 - (ch)) * 2)
++/* 3 bytes registers */
++#define AD3552R_REG_START_24B                         0x37
++#define AD3552R_REG_ADDR_HW_LDAC_24B                  0x37
++#define AD3552R_REG_ADDR_CH_DAC_24B(ch)                       (0x3D - (1 - (ch)) * 3)
++#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B            0x40
++#define AD3552R_REG_ADDR_CH_SELECT_24B                        0x41
++#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B          0x44
++#define AD3552R_REG_ADDR_SW_LDAC_24B                  0x45
++#define AD3552R_REG_ADDR_CH_INPUT_24B(ch)             (0x4B - (1 - (ch)) * 3)
++
++#define AD3552R_MAX_CH                                        2
++#define AD3552R_MASK_CH(ch)                           BIT(ch)
++#define AD3552R_MASK_ALL_CH                           GENMASK(1, 0)
++#define AD3552R_MAX_REG_SIZE                          3
++#define AD3552R_READ_BIT                              BIT(7)
++#define AD3552R_ADDR_MASK                             GENMASK(6, 0)
++#define AD3552R_MASK_DAC_12B                          GENMASK(15, 4)
++#define AD3552R_DEFAULT_CONFIG_B_VALUE                        0x8
++#define AD3552R_SCRATCH_PAD_TEST_VAL1                 0x34
++#define AD3552R_SCRATCH_PAD_TEST_VAL2                 0xB2
++#define AD3552R_GAIN_SCALE                            1000
++#define AD3552R_LDAC_PULSE_US                         100
++
++#define AD3552R_MAX_RANGES    5
++#define AD3542R_MAX_RANGES    6
++
++extern const s32 ad3552r_ch_ranges[AD3552R_MAX_RANGES][2];
++extern const s32 ad3542r_ch_ranges[AD3542R_MAX_RANGES][2];
++
++enum ad3552r_id {
++      AD3541R_ID = 0x400b,
++      AD3542R_ID = 0x4009,
++      AD3551R_ID = 0x400a,
++      AD3552R_ID = 0x4008,
++};
++
++struct ad3552r_model_data {
++      const char *model_name;
++      enum ad3552r_id chip_id;
++      unsigned int num_hw_channels;
++      const s32 (*ranges_table)[2];
++      int num_ranges;
++      bool requires_output_range;
++};
++
++struct ad3552r_ch_data {
++      s32     scale_int;
++      s32     scale_dec;
++      s32     offset_int;
++      s32     offset_dec;
++      s16     gain_offset;
++      u16     rfb;
++      u8      n;
++      u8      p;
++      u8      range;
++      bool    range_override;
++};
++
++enum ad3552r_ch_gain_scaling {
++      /* Gain scaling of 1 */
++      AD3552R_CH_GAIN_SCALING_1,
++      /* Gain scaling of 0.5 */
++      AD3552R_CH_GAIN_SCALING_0_5,
++      /* Gain scaling of 0.25 */
++      AD3552R_CH_GAIN_SCALING_0_25,
++      /* Gain scaling of 0.125 */
++      AD3552R_CH_GAIN_SCALING_0_125,
++};
++
++enum ad3552r_ch_vref_select {
++      /* Internal source with Vref I/O floating */
++      AD3552R_INTERNAL_VREF_PIN_FLOATING,
++      /* Internal source with Vref I/O at 2.5V */
++      AD3552R_INTERNAL_VREF_PIN_2P5V,
++      /* External source with Vref I/O as input */
++      AD3552R_EXTERNAL_VREF_PIN_INPUT
++};
++
++enum ad3542r_ch_output_range {
++      /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
++      AD3542R_CH_OUTPUT_RANGE_0__2P5V,
++      /* Range from 0 V to 3 V. Requires Rfb1x connection  */
++      AD3542R_CH_OUTPUT_RANGE_0__3V,
++      /* Range from 0 V to 5 V. Requires Rfb1x connection  */
++      AD3542R_CH_OUTPUT_RANGE_0__5V,
++      /* Range from 0 V to 10 V. Requires Rfb2x connection  */
++      AD3542R_CH_OUTPUT_RANGE_0__10V,
++      /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection  */
++      AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V,
++      /* Range from -5 V to 5 V. Requires Rfb2x connection  */
++      AD3542R_CH_OUTPUT_RANGE_NEG_5__5V,
++};
++
++enum ad3552r_ch_output_range {
++      /* Range from 0 V to 2.5 V. Requires Rfb1x connection */
++      AD3552R_CH_OUTPUT_RANGE_0__2P5V,
++      /* Range from 0 V to 5 V. Requires Rfb1x connection  */
++      AD3552R_CH_OUTPUT_RANGE_0__5V,
++      /* Range from 0 V to 10 V. Requires Rfb2x connection  */
++      AD3552R_CH_OUTPUT_RANGE_0__10V,
++      /* Range from -5 V to 5 V. Requires Rfb2x connection  */
++      AD3552R_CH_OUTPUT_RANGE_NEG_5__5V,
++      /* Range from -10 V to 10 V. Requires Rfb4x connection  */
++      AD3552R_CH_OUTPUT_RANGE_NEG_10__10V,
++};
++
++int ad3552r_get_output_range(struct device *dev,
++                           const struct ad3552r_model_data *model_info,
++                           struct fwnode_handle *child, u32 *val);
++int ad3552r_get_custom_gain(struct device *dev, struct fwnode_handle *child,
++                          u8 *gs_p, u8 *gs_n, u16 *rfb, s16 *goffs);
++u16 ad3552r_calc_custom_gain(u8 p, u8 n, s16 goffs);
++int ad3552r_get_ref_voltage(struct device *dev, u32 *val);
++int ad3552r_get_drive_strength(struct device *dev, u32 *val);
++void ad3552r_calc_gain_and_offset(struct ad3552r_ch_data *ch_data,
++                                const struct ad3552r_model_data *model_data);
++
++#endif /* __DRIVERS_IIO_DAC_AD3552R_H__ */
+-- 
+2.39.5
+
diff --git a/queue-6.12/kvm-arm64-set-hcr_el2.tid1-unconditionally.patch b/queue-6.12/kvm-arm64-set-hcr_el2.tid1-unconditionally.patch
new file mode 100644 (file)
index 0000000..0a730fe
--- /dev/null
@@ -0,0 +1,336 @@
+From 0510d3297a23920caf74a63e3f38c0ede70d6555 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Jul 2025 18:37:48 -0400
+Subject: KVM: arm64: Set HCR_EL2.TID1 unconditionally
+
+[ Upstream commit 4cd48565b0e5df398e7253c0d2d8c0403d69e7bf ]
+
+commit 90807748ca3a ("KVM: arm64: Hide SME system registers from
+guests") added trap handling for SMIDR_EL1, treating it as UNDEFINED as
+KVM does not support SME. This is right for the most part, however KVM
+needs to set HCR_EL2.TID1 to _actually_ trap the register.
+
+Unfortunately, this comes with some collateral damage as TID1 forces
+REVIDR_EL1 and AIDR_EL1 to trap as well. KVM has long treated these
+registers as "invariant" which is an awful term for the following:
+
+ - Userspace sees the boot CPU values on all vCPUs
+
+ - The guest sees the hardware values of the CPU on which a vCPU is
+   scheduled
+
+Keep the plates spinning by adding trap handling for the affected
+registers and repaint all of the "invariant" crud into terms of
+identifying an implementation. Yes, at this point we only need to
+set TID1 on SME hardware, but REVIDR_EL1 and AIDR_EL1 are about to
+become mutable anyway.
+
+Cc: Mark Brown <broonie@kernel.org>
+Cc: stable@vger.kernel.org
+Fixes: 90807748ca3a ("KVM: arm64: Hide SME system registers from guests")
+[maz: handle traps from 32bit]
+Co-developed-by: Marc Zyngier <maz@kernel.org>
+Signed-off-by: Marc Zyngier <maz@kernel.org>
+Link: https://lore.kernel.org/r/20250225005401.679536-2-oliver.upton@linux.dev
+Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/include/asm/kvm_arm.h |   4 +-
+ arch/arm64/kvm/sys_regs.c        | 184 +++++++++++++++++--------------
+ 2 files changed, 101 insertions(+), 87 deletions(-)
+
+diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h
+index 109a85ee69100..dd34794cec997 100644
+--- a/arch/arm64/include/asm/kvm_arm.h
++++ b/arch/arm64/include/asm/kvm_arm.h
+@@ -92,12 +92,12 @@
+  * SWIO:      Turn set/way invalidates into set/way clean+invalidate
+  * PTW:               Take a stage2 fault if a stage1 walk steps in device memory
+  * TID3:      Trap EL1 reads of group 3 ID registers
+- * TID2:      Trap CTR_EL0, CCSIDR2_EL1, CLIDR_EL1, and CSSELR_EL1
++ * TID1:      Trap REVIDR_EL1, AIDR_EL1, and SMIDR_EL1
+  */
+ #define HCR_GUEST_FLAGS (HCR_TSC | HCR_TSW | HCR_TWE | HCR_TWI | HCR_VM | \
+                        HCR_BSU_IS | HCR_FB | HCR_TACR | \
+                        HCR_AMO | HCR_SWIO | HCR_TIDCP | HCR_RW | HCR_TLOR | \
+-                       HCR_FMO | HCR_IMO | HCR_PTW | HCR_TID3)
++                       HCR_FMO | HCR_IMO | HCR_PTW | HCR_TID3 | HCR_TID1)
+ #define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK | HCR_ATA)
+ #define HCR_HOST_NVHE_PROTECTED_FLAGS (HCR_HOST_NVHE_FLAGS | HCR_TSC)
+ #define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
+diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c
+index 42791971f7588..05c6f3c9bce25 100644
+--- a/arch/arm64/kvm/sys_regs.c
++++ b/arch/arm64/kvm/sys_regs.c
+@@ -2323,6 +2323,94 @@ static unsigned int s1poe_visibility(const struct kvm_vcpu *vcpu,
+       return REG_HIDDEN;
+ }
++/*
++ * For historical (ahem ABI) reasons, KVM treated MIDR_EL1, REVIDR_EL1, and
++ * AIDR_EL1 as "invariant" registers, meaning userspace cannot change them.
++ * The values made visible to userspace were the register values of the boot
++ * CPU.
++ *
++ * At the same time, reads from these registers at EL1 previously were not
++ * trapped, allowing the guest to read the actual hardware value. On big-little
++ * machines, this means the VM can see different values depending on where a
++ * given vCPU got scheduled.
++ *
++ * These registers are now trapped as collateral damage from SME, and what
++ * follows attempts to give a user / guest view consistent with the existing
++ * ABI.
++ */
++static bool access_imp_id_reg(struct kvm_vcpu *vcpu,
++                            struct sys_reg_params *p,
++                            const struct sys_reg_desc *r)
++{
++      if (p->is_write)
++              return write_to_read_only(vcpu, p, r);
++
++      switch (reg_to_encoding(r)) {
++      case SYS_REVIDR_EL1:
++              p->regval = read_sysreg(revidr_el1);
++              break;
++      case SYS_AIDR_EL1:
++              p->regval = read_sysreg(aidr_el1);
++              break;
++      default:
++              WARN_ON_ONCE(1);
++      }
++
++      return true;
++}
++
++static u64 __ro_after_init boot_cpu_midr_val;
++static u64 __ro_after_init boot_cpu_revidr_val;
++static u64 __ro_after_init boot_cpu_aidr_val;
++
++static void init_imp_id_regs(void)
++{
++      boot_cpu_midr_val = read_sysreg(midr_el1);
++      boot_cpu_revidr_val = read_sysreg(revidr_el1);
++      boot_cpu_aidr_val = read_sysreg(aidr_el1);
++}
++
++static int get_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
++                        u64 *val)
++{
++      switch (reg_to_encoding(r)) {
++      case SYS_MIDR_EL1:
++              *val = boot_cpu_midr_val;
++              break;
++      case SYS_REVIDR_EL1:
++              *val = boot_cpu_revidr_val;
++              break;
++      case SYS_AIDR_EL1:
++              *val = boot_cpu_aidr_val;
++              break;
++      default:
++              WARN_ON_ONCE(1);
++              return -EINVAL;
++      }
++
++      return 0;
++}
++
++static int set_imp_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r,
++                        u64 val)
++{
++      u64 expected;
++      int ret;
++
++      ret = get_imp_id_reg(vcpu, r, &expected);
++      if (ret)
++              return ret;
++
++      return (expected == val) ? 0 : -EINVAL;
++}
++
++#define IMPLEMENTATION_ID(reg) {                      \
++      SYS_DESC(SYS_##reg),                            \
++      .access = access_imp_id_reg,                    \
++      .get_user = get_imp_id_reg,                     \
++      .set_user = set_imp_id_reg,                     \
++}
++
+ /*
+  * Architected system registers.
+  * Important: Must be sorted ascending by Op0, Op1, CRn, CRm, Op2
+@@ -2371,7 +2459,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
+       { SYS_DESC(SYS_DBGVCR32_EL2), undef_access, reset_val, DBGVCR32_EL2, 0 },
++      IMPLEMENTATION_ID(MIDR_EL1),
+       { SYS_DESC(SYS_MPIDR_EL1), NULL, reset_mpidr, MPIDR_EL1 },
++      IMPLEMENTATION_ID(REVIDR_EL1),
+       /*
+        * ID regs: all ID_SANITISED() entries here must have corresponding
+@@ -2648,6 +2738,7 @@ static const struct sys_reg_desc sys_reg_descs[] = {
+         .set_user = set_clidr, .val = ~CLIDR_EL1_RES0 },
+       { SYS_DESC(SYS_CCSIDR2_EL1), undef_access },
+       { SYS_DESC(SYS_SMIDR_EL1), undef_access },
++      IMPLEMENTATION_ID(AIDR_EL1),
+       { SYS_DESC(SYS_CSSELR_EL1), access_csselr, reset_unknown, CSSELR_EL1 },
+       ID_WRITABLE(CTR_EL0, CTR_EL0_DIC_MASK |
+                            CTR_EL0_IDC_MASK |
+@@ -4060,9 +4151,13 @@ int kvm_handle_cp15_32(struct kvm_vcpu *vcpu)
+        * Certain AArch32 ID registers are handled by rerouting to the AArch64
+        * system register table. Registers in the ID range where CRm=0 are
+        * excluded from this scheme as they do not trivially map into AArch64
+-       * system register encodings.
++       * system register encodings, except for AIDR/REVIDR.
+        */
+-      if (params.Op1 == 0 && params.CRn == 0 && params.CRm)
++      if (params.Op1 == 0 && params.CRn == 0 &&
++          (params.CRm || params.Op2 == 6 /* REVIDR */))
++              return kvm_emulate_cp15_id_reg(vcpu, &params);
++      if (params.Op1 == 1 && params.CRn == 0 &&
++          params.CRm == 0 && params.Op2 == 7 /* AIDR */)
+               return kvm_emulate_cp15_id_reg(vcpu, &params);
+       return kvm_handle_cp_32(vcpu, &params, cp15_regs, ARRAY_SIZE(cp15_regs));
+@@ -4363,65 +4458,6 @@ id_to_sys_reg_desc(struct kvm_vcpu *vcpu, u64 id,
+       return r;
+ }
+-/*
+- * These are the invariant sys_reg registers: we let the guest see the
+- * host versions of these, so they're part of the guest state.
+- *
+- * A future CPU may provide a mechanism to present different values to
+- * the guest, or a future kvm may trap them.
+- */
+-
+-#define FUNCTION_INVARIANT(reg)                                               \
+-      static u64 reset_##reg(struct kvm_vcpu *v,                      \
+-                             const struct sys_reg_desc *r)            \
+-      {                                                               \
+-              ((struct sys_reg_desc *)r)->val = read_sysreg(reg);     \
+-              return ((struct sys_reg_desc *)r)->val;                 \
+-      }
+-
+-FUNCTION_INVARIANT(midr_el1)
+-FUNCTION_INVARIANT(revidr_el1)
+-FUNCTION_INVARIANT(aidr_el1)
+-
+-/* ->val is filled in by kvm_sys_reg_table_init() */
+-static struct sys_reg_desc invariant_sys_regs[] __ro_after_init = {
+-      { SYS_DESC(SYS_MIDR_EL1), NULL, reset_midr_el1 },
+-      { SYS_DESC(SYS_REVIDR_EL1), NULL, reset_revidr_el1 },
+-      { SYS_DESC(SYS_AIDR_EL1), NULL, reset_aidr_el1 },
+-};
+-
+-static int get_invariant_sys_reg(u64 id, u64 __user *uaddr)
+-{
+-      const struct sys_reg_desc *r;
+-
+-      r = get_reg_by_id(id, invariant_sys_regs,
+-                        ARRAY_SIZE(invariant_sys_regs));
+-      if (!r)
+-              return -ENOENT;
+-
+-      return put_user(r->val, uaddr);
+-}
+-
+-static int set_invariant_sys_reg(u64 id, u64 __user *uaddr)
+-{
+-      const struct sys_reg_desc *r;
+-      u64 val;
+-
+-      r = get_reg_by_id(id, invariant_sys_regs,
+-                        ARRAY_SIZE(invariant_sys_regs));
+-      if (!r)
+-              return -ENOENT;
+-
+-      if (get_user(val, uaddr))
+-              return -EFAULT;
+-
+-      /* This is what we mean by invariant: you can't change it. */
+-      if (r->val != val)
+-              return -EINVAL;
+-
+-      return 0;
+-}
+-
+ static int demux_c15_get(struct kvm_vcpu *vcpu, u64 id, void __user *uaddr)
+ {
+       u32 val;
+@@ -4503,15 +4539,10 @@ int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
+ int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+ {
+       void __user *uaddr = (void __user *)(unsigned long)reg->addr;
+-      int err;
+       if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
+               return demux_c15_get(vcpu, reg->id, uaddr);
+-      err = get_invariant_sys_reg(reg->id, uaddr);
+-      if (err != -ENOENT)
+-              return err;
+-
+       return kvm_sys_reg_get_user(vcpu, reg,
+                                   sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
+ }
+@@ -4547,15 +4578,10 @@ int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg,
+ int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg)
+ {
+       void __user *uaddr = (void __user *)(unsigned long)reg->addr;
+-      int err;
+       if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX)
+               return demux_c15_set(vcpu, reg->id, uaddr);
+-      err = set_invariant_sys_reg(reg->id, uaddr);
+-      if (err != -ENOENT)
+-              return err;
+-
+       return kvm_sys_reg_set_user(vcpu, reg,
+                                   sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
+ }
+@@ -4644,23 +4670,14 @@ static int walk_sys_regs(struct kvm_vcpu *vcpu, u64 __user *uind)
+ unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu)
+ {
+-      return ARRAY_SIZE(invariant_sys_regs)
+-              + num_demux_regs()
++      return num_demux_regs()
+               + walk_sys_regs(vcpu, (u64 __user *)NULL);
+ }
+ int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices)
+ {
+-      unsigned int i;
+       int err;
+-      /* Then give them all the invariant registers' indices. */
+-      for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++) {
+-              if (put_user(sys_reg_to_index(&invariant_sys_regs[i]), uindices))
+-                      return -EFAULT;
+-              uindices++;
+-      }
+-
+       err = walk_sys_regs(vcpu, uindices);
+       if (err < 0)
+               return err;
+@@ -4878,15 +4895,12 @@ int __init kvm_sys_reg_table_init(void)
+       valid &= check_sysreg_table(cp14_64_regs, ARRAY_SIZE(cp14_64_regs), true);
+       valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true);
+       valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true);
+-      valid &= check_sysreg_table(invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs), false);
+       valid &= check_sysreg_table(sys_insn_descs, ARRAY_SIZE(sys_insn_descs), false);
+       if (!valid)
+               return -EINVAL;
+-      /* We abuse the reset function to overwrite the table itself. */
+-      for (i = 0; i < ARRAY_SIZE(invariant_sys_regs); i++)
+-              invariant_sys_regs[i].reset(NULL, &invariant_sys_regs[i]);
++      init_imp_id_regs();
+       ret = populate_nv_trap_config();
+-- 
+2.39.5
+
diff --git a/queue-6.12/loongarch-set-hugetlb-mmap-base-address-aligned-with.patch b/queue-6.12/loongarch-set-hugetlb-mmap-base-address-aligned-with.patch
new file mode 100644 (file)
index 0000000..d96a803
--- /dev/null
@@ -0,0 +1,88 @@
+From ca3c89c940d1d5db28dc90f598ba763ce04399a1 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 8 Mar 2025 13:51:32 +0800
+Subject: LoongArch: Set hugetlb mmap base address aligned with pmd size
+
+From: Bibo Mao <maobibo@loongson.cn>
+
+[ Upstream commit 3109d5ff484b7bc7b955f166974c6776d91f247b ]
+
+With ltp test case "testcases/bin/hugefork02", there is a dmesg error
+report message such as:
+
+ kernel BUG at mm/hugetlb.c:5550!
+ Oops - BUG[#1]:
+ CPU: 0 UID: 0 PID: 1517 Comm: hugefork02 Not tainted 6.14.0-rc2+ #241
+ Hardware name: QEMU QEMU Virtual Machine, BIOS unknown 2/2/2022
+ pc 90000000004eaf1c ra 9000000000485538 tp 900000010edbc000 sp 900000010edbf940
+ a0 900000010edbfb00 a1 9000000108d20280 a2 00007fffe9474000 a3 00007ffff3474000
+ a4 0000000000000000 a5 0000000000000003 a6 00000000003cadd3 a7 0000000000000000
+ t0 0000000001ffffff t1 0000000001474000 t2 900000010ecd7900 t3 00007fffe9474000
+ t4 00007fffe9474000 t5 0000000000000040 t6 900000010edbfb00 t7 0000000000000001
+ t8 0000000000000005 u0 90000000004849d0 s9 900000010edbfa00 s0 9000000108d20280
+ s1 00007fffe9474000 s2 0000000002000000 s3 9000000108d20280 s4 9000000002b38b10
+ s5 900000010edbfb00 s6 00007ffff3474000 s7 0000000000000406 s8 900000010edbfa08
+    ra: 9000000000485538 unmap_vmas+0x130/0x218
+   ERA: 90000000004eaf1c __unmap_hugepage_range+0x6f4/0x7d0
+  PRMD: 00000004 (PPLV0 +PIE -PWE)
+  EUEN: 00000007 (+FPE +SXE +ASXE -BTE)
+  ECFG: 00071c1d (LIE=0,2-4,10-12 VS=7)
+ ESTAT: 000c0000 [BRK] (IS= ECode=12 EsubCode=0)
+ PRID: 0014c010 (Loongson-64bit, Loongson-3A5000)
+ Process hugefork02 (pid: 1517, threadinfo=00000000a670eaf4, task=000000007a95fc64)
+ Call Trace:
+ [<90000000004eaf1c>] __unmap_hugepage_range+0x6f4/0x7d0
+ [<9000000000485534>] unmap_vmas+0x12c/0x218
+ [<9000000000494068>] exit_mmap+0xe0/0x308
+ [<900000000025fdc4>] mmput+0x74/0x180
+ [<900000000026a284>] do_exit+0x294/0x898
+ [<900000000026aa30>] do_group_exit+0x30/0x98
+ [<900000000027bed4>] get_signal+0x83c/0x868
+ [<90000000002457b4>] arch_do_signal_or_restart+0x54/0xfa0
+ [<90000000015795e8>] irqentry_exit_to_user_mode+0xb8/0x138
+ [<90000000002572d0>] tlb_do_page_fault_1+0x114/0x1b4
+
+The problem is that base address allocated from hugetlbfs is not aligned
+with pmd size. Here add a checking for hugetlbfs and align base address
+with pmd size. After this patch the test case "testcases/bin/hugefork02"
+passes to run.
+
+This is similar to the commit 7f24cbc9c4d42db8a3c8484d1 ("mm/mmap: teach
+generic_get_unmapped_area{_topdown} to handle hugetlb mappings").
+
+Cc: stable@vger.kernel.org  # 6.13+
+Signed-off-by: Bibo Mao <maobibo@loongson.cn>
+Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/loongarch/mm/mmap.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/arch/loongarch/mm/mmap.c b/arch/loongarch/mm/mmap.c
+index 914e82ff3f656..1df9e99582cc6 100644
+--- a/arch/loongarch/mm/mmap.c
++++ b/arch/loongarch/mm/mmap.c
+@@ -3,6 +3,7 @@
+  * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
+  */
+ #include <linux/export.h>
++#include <linux/hugetlb.h>
+ #include <linux/io.h>
+ #include <linux/kfence.h>
+ #include <linux/memblock.h>
+@@ -63,8 +64,11 @@ static unsigned long arch_get_unmapped_area_common(struct file *filp,
+       }
+       info.length = len;
+-      info.align_mask = do_color_align ? (PAGE_MASK & SHM_ALIGN_MASK) : 0;
+       info.align_offset = pgoff << PAGE_SHIFT;
++      if (filp && is_file_hugepages(filp))
++              info.align_mask = huge_page_mask_align(filp);
++      else
++              info.align_mask = do_color_align ? (PAGE_MASK & SHM_ALIGN_MASK) : 0;
+       if (dir == DOWN) {
+               info.flags = VM_UNMAPPED_AREA_TOPDOWN;
+-- 
+2.39.5
+
diff --git a/queue-6.12/net-stmmac-fix-accessing-freed-irq-affinity_hint.patch b/queue-6.12/net-stmmac-fix-accessing-freed-irq-affinity_hint.patch
new file mode 100644 (file)
index 0000000..09d2b3f
--- /dev/null
@@ -0,0 +1,63 @@
+From e82347444599c944993255de39c51a8b8a892f5d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 18 Mar 2025 11:24:23 +0800
+Subject: net: stmmac: Fix accessing freed irq affinity_hint
+
+From: Qingfang Deng <dqfext@gmail.com>
+
+[ Upstream commit c60d101a226f18e9a8f01bb4c6ca2b47dfcb15ef ]
+
+The cpumask should not be a local variable, since its pointer is saved
+to irq_desc and may be accessed from procfs.
+To fix it, use the persistent mask cpumask_of(cpu#).
+
+Cc: stable@vger.kernel.org
+Fixes: 8deec94c6040 ("net: stmmac: set IRQ affinity hint for multi MSI vectors")
+Signed-off-by: Qingfang Deng <dqfext@gmail.com>
+Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
+Link: https://patch.msgid.link/20250318032424.112067-1-dqfext@gmail.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 11 ++++-------
+ 1 file changed, 4 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+index 0250c5cb28ff2..36328298dc9b8 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -3603,7 +3603,6 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)
+ {
+       struct stmmac_priv *priv = netdev_priv(dev);
+       enum request_irq_err irq_err;
+-      cpumask_t cpu_mask;
+       int irq_idx = 0;
+       char *int_name;
+       int ret;
+@@ -3732,9 +3731,8 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)
+                       irq_idx = i;
+                       goto irq_error;
+               }
+-              cpumask_clear(&cpu_mask);
+-              cpumask_set_cpu(i % num_online_cpus(), &cpu_mask);
+-              irq_set_affinity_hint(priv->rx_irq[i], &cpu_mask);
++              irq_set_affinity_hint(priv->rx_irq[i],
++                                    cpumask_of(i % num_online_cpus()));
+       }
+       /* Request Tx MSI irq */
+@@ -3757,9 +3755,8 @@ static int stmmac_request_irq_multi_msi(struct net_device *dev)
+                       irq_idx = i;
+                       goto irq_error;
+               }
+-              cpumask_clear(&cpu_mask);
+-              cpumask_set_cpu(i % num_online_cpus(), &cpu_mask);
+-              irq_set_affinity_hint(priv->tx_irq[i], &cpu_mask);
++              irq_set_affinity_hint(priv->tx_irq[i],
++                                    cpumask_of(i % num_online_cpus()));
+       }
+       return 0;
+-- 
+2.39.5
+
diff --git a/queue-6.12/riscv-atomic-do-proper-sign-extension-also-for-unsig.patch b/queue-6.12/riscv-atomic-do-proper-sign-extension-also-for-unsig.patch
new file mode 100644 (file)
index 0000000..829225f
--- /dev/null
@@ -0,0 +1,46 @@
+From b57b1eac9edb010056dd04b311ac8b57b6aade8e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Jul 2025 18:01:44 -0400
+Subject: riscv/atomic: Do proper sign extension also for unsigned in
+ arch_cmpxchg
+
+[ Upstream commit 1898300abf3508bca152e65b36cce5bf93d7e63e ]
+
+Sign extend also an unsigned compare value to match what lr.w is doing.
+Otherwise try_cmpxchg may spuriously return true when used on a u32 value
+that has the sign bit set, as it happens often in inode_set_ctime_current.
+
+Do this in three conversion steps.  The first conversion to long is needed
+to avoid a -Wpointer-to-int-cast warning when arch_cmpxchg is used with a
+pointer type.  Then convert to int and back to long to always sign extend
+the 32-bit value to 64-bit.
+
+Fixes: 6c58f25e6938 ("riscv/atomic: Fix sign extension for RV64I")
+Signed-off-by: Andreas Schwab <schwab@suse.de>
+Reviewed-by: Alexandre Ghiti <alexghiti@rivosinc.com>
+Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
+Tested-by: Xi Ruoyao <xry111@xry111.site>
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/r/mvmed0k4prh.fsf@suse.de
+Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/include/asm/cmpxchg.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/riscv/include/asm/cmpxchg.h b/arch/riscv/include/asm/cmpxchg.h
+index ebbce134917cc..6efa95ad033ab 100644
+--- a/arch/riscv/include/asm/cmpxchg.h
++++ b/arch/riscv/include/asm/cmpxchg.h
+@@ -169,7 +169,7 @@
+               break;                                                  \
+       case 4:                                                         \
+               __arch_cmpxchg(".w", ".w" sc_sfx, prepend, append,      \
+-                              __ret, __ptr, (long), __old, __new);    \
++                              __ret, __ptr, (long)(int)(long), __old, __new); \
+               break;                                                  \
+       case 8:                                                         \
+               __arch_cmpxchg(".d", ".d" sc_sfx, prepend, append,      \
+-- 
+2.39.5
+
index fd8002791f8972a45c9b8ff638f74af1c8c9f53b..0cbce2df087e5c323e7388b713d9abfb3cb0dace 100644 (file)
@@ -201,3 +201,29 @@ mm-vma-reset-vma-iterator-on-commit_merge-oom-failure.patch
 r8169-add-support-for-rtl8125d.patch
 net-phy-realtek-merge-the-drivers-for-internal-nbase-t-phy-s.patch
 net-phy-realtek-add-rtl8125d-internal-phy.patch
+btrfs-do-proper-folio-cleanup-when-cow_file_range-fa.patch
+iio-dac-ad3552r-changes-to-use-field_prep.patch
+iio-dac-ad3552r-extract-common-code-no-changes-in-be.patch
+iio-dac-ad3552r-common-fix-ad3541-2r-ranges.patch
+drm-xe-carve-out-wopcm-portion-from-the-stolen-memor.patch
+usb-typec-tcpm-pssourceofftimer-timeout-in-pr_swap-e.patch
+drm-msm-dp-account-for-widebus-and-yuv420-during-mod.patch
+drm-fbdev-dma-add-shadow-buffering-for-deferred-i-o.patch
+btrfs-skip-inodes-without-loaded-extent-maps-when-sh.patch
+btrfs-make-the-extent-map-shrinker-run-asynchronousl.patch
+btrfs-do-regular-iput-instead-of-delayed-iput-during.patch
+riscv-atomic-do-proper-sign-extension-also-for-unsig.patch
+loongarch-set-hugetlb-mmap-base-address-aligned-with.patch
+arm64-dts-rockchip-add-avdd-hdmi-supplies-to-rockpro.patch
+alsa-hda-realtek-bass-speaker-fixup-for-asus-um5606k.patch
+drm-amdkfd-remove-gfx-12-trap-handler-page-size-cap.patch
+drm-amdkfd-fix-instruction-hazard-in-gfx12-trap-hand.patch
+kvm-arm64-set-hcr_el2.tid1-unconditionally.patch
+net-stmmac-fix-accessing-freed-irq-affinity_hint.patch
+spi-spi-mem-extend-spi-mem-operations-with-a-per-ope.patch
+spi-spi-mem-add-a-new-controller-capability.patch
+spi-fsl-qspi-support-per-spi-mem-operation-frequency.patch
+spi-fsl-qspi-use-devm-function-instead-of-driver-rem.patch
+btrfs-zoned-fix-extent-range-end-unlock-in-cow_file_.patch
+btrfs-fix-use-after-free-on-inode-when-scanning-root.patch
+spi-fsl-qspi-fix-double-cleanup-in-probe-error-path.patch
diff --git a/queue-6.12/spi-fsl-qspi-fix-double-cleanup-in-probe-error-path.patch b/queue-6.12/spi-fsl-qspi-fix-double-cleanup-in-probe-error-path.patch
new file mode 100644 (file)
index 0000000..99d418b
--- /dev/null
@@ -0,0 +1,61 @@
+From 83ec22b65ff203f224c9406a773026b6101f9571 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 10 Apr 2025 14:56:09 +0800
+Subject: spi: fsl-qspi: Fix double cleanup in probe error path
+
+From: Kevin Hao <haokexin@gmail.com>
+
+[ Upstream commit 5d07ab2a7fa1305e429d9221716582f290b58078 ]
+
+Commit 40369bfe717e ("spi: fsl-qspi: use devm function instead of driver
+remove") introduced managed cleanup via fsl_qspi_cleanup(), but
+incorrectly retain manual cleanup in two scenarios:
+
+- On devm_add_action_or_reset() failure, the function automatically call
+  fsl_qspi_cleanup(). However, the current code still jumps to
+  err_destroy_mutex, repeating cleanup.
+
+- After the fsl_qspi_cleanup() action is added successfully, there is no
+  need to manually perform the cleanup in the subsequent error path.
+  However, the current code still jumps to err_destroy_mutex on spi
+  controller failure, repeating cleanup.
+
+Skip redundant manual cleanup calls to fix these issues.
+
+Cc: stable@vger.kernel.org
+Fixes: 40369bfe717e ("spi: fsl-qspi: use devm function instead of driver remove")
+Signed-off-by: Kevin Hao <haokexin@gmail.com>
+Link: https://patch.msgid.link/20250410-spi-v1-1-56e867cc19cf@gmail.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-fsl-qspi.c | 7 ++-----
+ 1 file changed, 2 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
+index 5c59fddb32c1b..2f54dc09d11b1 100644
+--- a/drivers/spi/spi-fsl-qspi.c
++++ b/drivers/spi/spi-fsl-qspi.c
+@@ -949,17 +949,14 @@ static int fsl_qspi_probe(struct platform_device *pdev)
+       ret = devm_add_action_or_reset(dev, fsl_qspi_cleanup, q);
+       if (ret)
+-              goto err_destroy_mutex;
++              goto err_put_ctrl;
+       ret = devm_spi_register_controller(dev, ctlr);
+       if (ret)
+-              goto err_destroy_mutex;
++              goto err_put_ctrl;
+       return 0;
+-err_destroy_mutex:
+-      mutex_destroy(&q->lock);
+-
+ err_disable_clk:
+       fsl_qspi_clk_disable_unprep(q);
+-- 
+2.39.5
+
diff --git a/queue-6.12/spi-fsl-qspi-support-per-spi-mem-operation-frequency.patch b/queue-6.12/spi-fsl-qspi-support-per-spi-mem-operation-frequency.patch
new file mode 100644 (file)
index 0000000..5bc4648
--- /dev/null
@@ -0,0 +1,76 @@
+From 0eb0997cc79b65fff30e73b5572d5633cf7752f4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 18:05:53 +0100
+Subject: spi: fsl-qspi: Support per spi-mem operation frequency switches
+
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+
+[ Upstream commit 2438db5253eb17a7c0ccb15aea4252a150dda057 ]
+
+Every ->exec_op() call correctly configures the spi bus speed to the
+maximum allowed frequency for the memory using the constant spi default
+parameter. Since we can now have per-operation constraints, let's use
+the value that comes from the spi-mem operation structure instead. In
+case there is no specific limitation for this operation, the default spi
+device value will be given anyway.
+
+The per-operation frequency capability is thus advertised to the spi-mem
+core.
+
+Cc: Han Xu <han.xu@nxp.com>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20241224-winbond-6-11-rc1-quad-support-v2-8-ad218dbc406f@bootlin.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-fsl-qspi.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
+index 79bac30e79af6..ce86f44b0e93f 100644
+--- a/drivers/spi/spi-fsl-qspi.c
++++ b/drivers/spi/spi-fsl-qspi.c
+@@ -522,9 +522,10 @@ static void fsl_qspi_invalidate(struct fsl_qspi *q)
+       qspi_writel(q, reg, q->iobase + QUADSPI_MCR);
+ }
+-static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi)
++static void fsl_qspi_select_mem(struct fsl_qspi *q, struct spi_device *spi,
++                              const struct spi_mem_op *op)
+ {
+-      unsigned long rate = spi->max_speed_hz;
++      unsigned long rate = op->max_freq;
+       int ret;
+       if (q->selected == spi_get_chipselect(spi, 0))
+@@ -652,7 +653,7 @@ static int fsl_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+       fsl_qspi_readl_poll_tout(q, base + QUADSPI_SR, (QUADSPI_SR_IP_ACC_MASK |
+                                QUADSPI_SR_AHB_ACC_MASK), 10, 1000);
+-      fsl_qspi_select_mem(q, mem->spi);
++      fsl_qspi_select_mem(q, mem->spi, op);
+       if (needs_amba_base_offset(q))
+               addr_offset = q->memmap_phy;
+@@ -839,6 +840,10 @@ static const struct spi_controller_mem_ops fsl_qspi_mem_ops = {
+       .get_name = fsl_qspi_get_name,
+ };
++static const struct spi_controller_mem_caps fsl_qspi_mem_caps = {
++      .per_op_freq = true,
++};
++
+ static int fsl_qspi_probe(struct platform_device *pdev)
+ {
+       struct spi_controller *ctlr;
+@@ -923,6 +928,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
+       ctlr->bus_num = -1;
+       ctlr->num_chipselect = 4;
+       ctlr->mem_ops = &fsl_qspi_mem_ops;
++      ctlr->mem_caps = &fsl_qspi_mem_caps;
+       fsl_qspi_default_setup(q);
+-- 
+2.39.5
+
diff --git a/queue-6.12/spi-fsl-qspi-use-devm-function-instead-of-driver-rem.patch b/queue-6.12/spi-fsl-qspi-use-devm-function-instead-of-driver-rem.patch
new file mode 100644 (file)
index 0000000..9217c93
--- /dev/null
@@ -0,0 +1,96 @@
+From 201bd9e3cb2e7ccdd87022ea46227dd746514823 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 Mar 2025 17:41:51 -0500
+Subject: spi: fsl-qspi: use devm function instead of driver remove
+
+From: Han Xu <han.xu@nxp.com>
+
+[ Upstream commit 40369bfe717e96e26650eeecfa5a6363563df6e4 ]
+
+Driver use devm APIs to manage clk/irq/resources and register the spi
+controller, but the legacy remove function will be called first during
+device detach and trigger kernel panic. Drop the remove function and use
+devm_add_action_or_reset() for driver cleanup to ensure the release
+sequence.
+
+Trigger kernel panic on i.MX8MQ by
+echo 30bb0000.spi >/sys/bus/platform/drivers/fsl-quadspi/unbind
+
+Cc: stable@vger.kernel.org
+Fixes: 8fcb830a00f0 ("spi: spi-fsl-qspi: use devm_spi_register_controller")
+Reported-by: Kevin Hao <haokexin@gmail.com>
+Signed-off-by: Han Xu <han.xu@nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20250326224152.2147099-1-han.xu@nxp.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-fsl-qspi.c | 31 +++++++++++++++++--------------
+ 1 file changed, 17 insertions(+), 14 deletions(-)
+
+diff --git a/drivers/spi/spi-fsl-qspi.c b/drivers/spi/spi-fsl-qspi.c
+index ce86f44b0e93f..5c59fddb32c1b 100644
+--- a/drivers/spi/spi-fsl-qspi.c
++++ b/drivers/spi/spi-fsl-qspi.c
+@@ -844,6 +844,19 @@ static const struct spi_controller_mem_caps fsl_qspi_mem_caps = {
+       .per_op_freq = true,
+ };
++static void fsl_qspi_cleanup(void *data)
++{
++      struct fsl_qspi *q = data;
++
++      /* disable the hardware */
++      qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
++      qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
++
++      fsl_qspi_clk_disable_unprep(q);
++
++      mutex_destroy(&q->lock);
++}
++
+ static int fsl_qspi_probe(struct platform_device *pdev)
+ {
+       struct spi_controller *ctlr;
+@@ -934,6 +947,10 @@ static int fsl_qspi_probe(struct platform_device *pdev)
+       ctlr->dev.of_node = np;
++      ret = devm_add_action_or_reset(dev, fsl_qspi_cleanup, q);
++      if (ret)
++              goto err_destroy_mutex;
++
+       ret = devm_spi_register_controller(dev, ctlr);
+       if (ret)
+               goto err_destroy_mutex;
+@@ -953,19 +970,6 @@ static int fsl_qspi_probe(struct platform_device *pdev)
+       return ret;
+ }
+-static void fsl_qspi_remove(struct platform_device *pdev)
+-{
+-      struct fsl_qspi *q = platform_get_drvdata(pdev);
+-
+-      /* disable the hardware */
+-      qspi_writel(q, QUADSPI_MCR_MDIS_MASK, q->iobase + QUADSPI_MCR);
+-      qspi_writel(q, 0x0, q->iobase + QUADSPI_RSER);
+-
+-      fsl_qspi_clk_disable_unprep(q);
+-
+-      mutex_destroy(&q->lock);
+-}
+-
+ static int fsl_qspi_suspend(struct device *dev)
+ {
+       return 0;
+@@ -1003,7 +1007,6 @@ static struct platform_driver fsl_qspi_driver = {
+               .pm =   &fsl_qspi_pm_ops,
+       },
+       .probe          = fsl_qspi_probe,
+-      .remove_new     = fsl_qspi_remove,
+ };
+ module_platform_driver(fsl_qspi_driver);
+-- 
+2.39.5
+
diff --git a/queue-6.12/spi-spi-mem-add-a-new-controller-capability.patch b/queue-6.12/spi-spi-mem-add-a-new-controller-capability.patch
new file mode 100644 (file)
index 0000000..bbbc1c1
--- /dev/null
@@ -0,0 +1,71 @@
+From 16d2efcbbac33cd7d1be2d934b9bfb3f24425544 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 18:05:47 +0100
+Subject: spi: spi-mem: Add a new controller capability
+
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+
+[ Upstream commit 1248c9b8d54120950fda10fbeb98fb8932b4d45c ]
+
+There are spi devices with multiple frequency limitations depending on
+the invoked command. We probably do not want to afford running at the
+lowest supported frequency all the time, so if we want to get the most
+of our hardware, we need to allow per-operation frequency limitations.
+
+Among all the SPI memory controllers, I believe all are capable of
+changing the spi frequency on the fly. Some of the drivers do not make
+any frequency setup though. And some others will derive a per chip
+prescaler value which will be used forever.
+
+Actually changing the frequency on the fly is something new in Linux, so
+we need to carefully flag the drivers which do and do not support it. A
+controller capability is created for that, and the presence for this
+capability will always be checked before accepting such pattern.
+
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Reviewed-by: Tudor Ambarus <tudor.ambarus@linaro.org>
+Link: https://patch.msgid.link/20241224-winbond-6-11-rc1-quad-support-v2-2-ad218dbc406f@bootlin.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-mem.c       | 6 ++++++
+ include/linux/spi/spi-mem.h | 2 ++
+ 2 files changed, 8 insertions(+)
+
+diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
+index f8b598ba962d9..d0ae20d433d61 100644
+--- a/drivers/spi/spi-mem.c
++++ b/drivers/spi/spi-mem.c
+@@ -188,6 +188,12 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
+           op->max_freq < mem->spi->controller->min_speed_hz)
+               return false;
++      if (op->max_freq &&
++          op->max_freq < mem->spi->max_speed_hz) {
++              if (!spi_mem_controller_is_capable(ctlr, per_op_freq))
++                      return false;
++      }
++
+       return spi_mem_check_buswidth(mem, op);
+ }
+ EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
+diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
+index 44b7ecee0e74c..0f00d74beb24c 100644
+--- a/include/linux/spi/spi-mem.h
++++ b/include/linux/spi/spi-mem.h
+@@ -306,10 +306,12 @@ struct spi_controller_mem_ops {
+  * struct spi_controller_mem_caps - SPI memory controller capabilities
+  * @dtr: Supports DTR operations
+  * @ecc: Supports operations with error correction
++ * @per_op_freq: Supports per operation frequency switching
+  */
+ struct spi_controller_mem_caps {
+       bool dtr;
+       bool ecc;
++      bool per_op_freq;
+ };
+ #define spi_mem_controller_is_capable(ctlr, cap)      \
+-- 
+2.39.5
+
diff --git a/queue-6.12/spi-spi-mem-extend-spi-mem-operations-with-a-per-ope.patch b/queue-6.12/spi-spi-mem-extend-spi-mem-operations-with-a-per-ope.patch
new file mode 100644 (file)
index 0000000..5831735
--- /dev/null
@@ -0,0 +1,220 @@
+From 00311c92a5496aed8892149e05fc9b8d66c78b36 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 24 Dec 2024 18:05:46 +0100
+Subject: spi: spi-mem: Extend spi-mem operations with a per-operation maximum
+ frequency
+
+From: Miquel Raynal <miquel.raynal@bootlin.com>
+
+[ Upstream commit 0fefeade90e74bc8f40ab0e460f483565c492e28 ]
+
+In the spi subsystem, the bus frequency is derived as follows:
+- the controller may expose a minimum and maximum operating frequency
+- the hardware description, through the spi peripheral properties,
+  advise what is the maximum acceptable frequency from a device/wiring
+  point of view.
+Transfers must be observed at a frequency which fits both (so in
+practice, the lowest maximum).
+
+Actually, this second point mixes two information and already takes the
+lowest frequency among:
+- what the spi device is capable of (what is written in the component
+  datasheet)
+- what the wiring allows (electromagnetic sensibility, crossovers,
+  terminations, antenna effect, etc).
+
+This logic works until spi devices are no longer capable of sustaining
+their highest frequency regardless of the operation. Spi memories are
+typically subject to such variation. Some devices are capable of
+spitting their internally stored data (essentially in read mode) at a
+very fast rate, typically up to 166MHz on Winbond SPI-NAND chips, using
+"fast" commands. However, some of the low-end operations, such as
+regular page read-from-cache commands, are more limited and can only be
+executed at 54MHz at most. This is currently a problem in the SPI-NAND
+subsystem. Another situation, even if not yet supported, will be with
+DTR commands, when the data is latched on both edges of the clock. The
+same chips as mentioned previously are in this case limited to
+80MHz. Yet another example might be continuous reads, which, under
+certain circumstances, can also run at most at 104 or 120MHz.
+
+As a matter of fact, the "one frequency per chip" policy is outdated and
+more fine grain configuration is needed: we need to allow per-operation
+frequency limitations. So far, all datasheets I encountered advertise a
+maximum default frequency, which need to be lowered for certain specific
+operations. So based on the current infrastructure, we can still expect
+firmware (device trees in general) to continued advertising the same
+maximum speed which is a mix between the PCB limitations and the chip
+maximum capability, and expect per-operation lower frequencies when this
+is relevant.
+
+Add a `struct spi_mem_op` member to carry this information. Not
+providing this field explicitly from upper layers means that there is no
+further constraint and the default spi device maximum speed will be
+carried instead. The SPI_MEM_OP() macro is also expanded with an
+optional frequency argument, because virtually all operations can be
+subject to such a limitation, and this will allow for a smooth and
+discrete transition.
+
+For controller drivers which do not implement the spi-mem interface, the
+per-transfer speed is also set acordingly to a lower (than the maximum
+default) speed when relevant.
+
+Acked-by: Pratyush Yadav <pratyush@kernel.org>
+Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
+Link: https://patch.msgid.link/20241224-winbond-6-11-rc1-quad-support-v2-1-ad218dbc406f@bootlin.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/mtd/nand/spi/core.c |  2 ++
+ drivers/spi/spi-mem.c       | 28 ++++++++++++++++++++++++++++
+ include/linux/spi/spi-mem.h | 12 +++++++++++-
+ 3 files changed, 41 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
+index 4d76f9f71a0e9..075f513157603 100644
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1214,6 +1214,8 @@ spinand_select_op_variant(struct spinand_device *spinand,
+                       if (ret)
+                               break;
++                      spi_mem_adjust_op_freq(spinand->spimem, &op);
++
+                       if (!spi_mem_supports_op(spinand->spimem, &op))
+                               break;
+diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
+index 17b8baf749e6a..f8b598ba962d9 100644
+--- a/drivers/spi/spi-mem.c
++++ b/drivers/spi/spi-mem.c
+@@ -184,6 +184,10 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
+                       return false;
+       }
++      if (op->max_freq && mem->spi->controller->min_speed_hz &&
++          op->max_freq < mem->spi->controller->min_speed_hz)
++              return false;
++
+       return spi_mem_check_buswidth(mem, op);
+ }
+ EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
+@@ -361,6 +365,9 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+       u8 *tmpbuf;
+       int ret;
++      /* Make sure the operation frequency is correct before going futher */
++      spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op);
++
+       ret = spi_mem_check_op(op);
+       if (ret)
+               return ret;
+@@ -407,6 +414,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+       xfers[xferpos].tx_buf = tmpbuf;
+       xfers[xferpos].len = op->cmd.nbytes;
+       xfers[xferpos].tx_nbits = op->cmd.buswidth;
++      xfers[xferpos].speed_hz = op->max_freq;
+       spi_message_add_tail(&xfers[xferpos], &msg);
+       xferpos++;
+       totalxferlen++;
+@@ -421,6 +429,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+               xfers[xferpos].tx_buf = tmpbuf + 1;
+               xfers[xferpos].len = op->addr.nbytes;
+               xfers[xferpos].tx_nbits = op->addr.buswidth;
++              xfers[xferpos].speed_hz = op->max_freq;
+               spi_message_add_tail(&xfers[xferpos], &msg);
+               xferpos++;
+               totalxferlen += op->addr.nbytes;
+@@ -432,6 +441,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+               xfers[xferpos].len = op->dummy.nbytes;
+               xfers[xferpos].tx_nbits = op->dummy.buswidth;
+               xfers[xferpos].dummy_data = 1;
++              xfers[xferpos].speed_hz = op->max_freq;
+               spi_message_add_tail(&xfers[xferpos], &msg);
+               xferpos++;
+               totalxferlen += op->dummy.nbytes;
+@@ -447,6 +457,7 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
+               }
+               xfers[xferpos].len = op->data.nbytes;
++              xfers[xferpos].speed_hz = op->max_freq;
+               spi_message_add_tail(&xfers[xferpos], &msg);
+               xferpos++;
+               totalxferlen += op->data.nbytes;
+@@ -525,6 +536,23 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
+ }
+ EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
++/**
++ * spi_mem_adjust_op_freq() - Adjust the frequency of a SPI mem operation to
++ *                          match controller, PCB and chip limitations
++ * @mem: the SPI memory
++ * @op: the operation to adjust
++ *
++ * 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.
++ */
++void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op)
++{
++      if (!op->max_freq || op->max_freq > mem->spi->max_speed_hz)
++              op->max_freq = mem->spi->max_speed_hz;
++}
++EXPORT_SYMBOL_GPL(spi_mem_adjust_op_freq);
++
+ 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 f866d5c8ed32a..44b7ecee0e74c 100644
+--- a/include/linux/spi/spi-mem.h
++++ b/include/linux/spi/spi-mem.h
+@@ -68,6 +68,9 @@ enum spi_mem_data_dir {
+       SPI_MEM_DATA_OUT,
+ };
++#define SPI_MEM_OP_MAX_FREQ(__freq)                           \
++      .max_freq = __freq
++
+ /**
+  * struct spi_mem_op - describes a SPI memory operation
+  * @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
+@@ -95,6 +98,9 @@ enum spi_mem_data_dir {
+  *             operation does not involve transferring data
+  * @data.buf.in: input buffer (must be DMA-able)
+  * @data.buf.out: output buffer (must be DMA-able)
++ * @max_freq: frequency limitation wrt this operation. 0 means there is no
++ *          specific constraint and the highest achievable frequency can be
++ *          attempted.
+  */
+ struct spi_mem_op {
+       struct {
+@@ -132,14 +138,17 @@ struct spi_mem_op {
+                       const void *out;
+               } buf;
+       } data;
++
++      unsigned int max_freq;
+ };
+-#define SPI_MEM_OP(__cmd, __addr, __dummy, __data)            \
++#define SPI_MEM_OP(__cmd, __addr, __dummy, __data, ...)               \
+       {                                                       \
+               .cmd = __cmd,                                   \
+               .addr = __addr,                                 \
+               .dummy = __dummy,                               \
+               .data = __data,                                 \
++              __VA_ARGS__                                     \
+       }
+ /**
+@@ -365,6 +374,7 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
+ #endif /* CONFIG_SPI_MEM */
+ 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);
+ bool spi_mem_supports_op(struct spi_mem *mem,
+                        const struct spi_mem_op *op);
+-- 
+2.39.5
+
diff --git a/queue-6.12/usb-typec-tcpm-pssourceofftimer-timeout-in-pr_swap-e.patch b/queue-6.12/usb-typec-tcpm-pssourceofftimer-timeout-in-pr_swap-e.patch
new file mode 100644 (file)
index 0000000..98b05c9
--- /dev/null
@@ -0,0 +1,54 @@
+From 2c935319f2f2929f170a9c965e04fb6e920aac87 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Jul 2025 17:41:11 -0400
+Subject: usb: typec: tcpm: PSSourceOffTimer timeout in PR_Swap enters
+ ERROR_RECOVERY
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+[ Upstream commit 659f5d55feb75782bd46cf130da3c1f240afe9ba ]
+
+As PD2.0 spec ("6.5.6.2 PSSourceOffTimer"),the PSSourceOffTimer is
+used by the Policy Engine in Dual-Role Power device that is currently
+acting as a Sink to timeout on a PS_RDY Message during a Power Role
+Swap sequence. This condition leads to a Hard Reset for USB Type-A and
+Type-B Plugs and Error Recovery for Type-C plugs and return to USB
+Default Operation.
+
+Therefore, after PSSourceOffTimer timeout, the tcpm state machine should
+switch from PR_SWAP_SNK_SRC_SINK_OFF to ERROR_RECOVERY. This can also
+solve the test items in the USB power delivery compliance test:
+TEST.PD.PROT.SNK.12 PR_Swap – PSSourceOffTimer Timeout
+
+[1] https://usb.org/document-library/usb-power-delivery-compliance-test-specification-0/USB_PD3_CTS_Q4_2025_OR.zip
+
+Fixes: f0690a25a140 ("staging: typec: USB Type-C Port Manager (tcpm)")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Jos Wang <joswang@lenovo.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Tested-by: Amit Sunil Dhamne <amitsd@google.com>
+Link: https://lore.kernel.org/r/20250213134921.3798-1-joswang1221@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/typec/tcpm/tcpm.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
+index 1d8e760df483c..9838a2c8c1b85 100644
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -5566,8 +5566,7 @@ static void run_state_machine(struct tcpm_port *port)
+               tcpm_set_auto_vbus_discharge_threshold(port, TYPEC_PWR_MODE_USB,
+                                                      port->pps_data.active, 0);
+               tcpm_set_charge(port, false);
+-              tcpm_set_state(port, hard_reset_state(port),
+-                             PD_T_PS_SOURCE_OFF);
++              tcpm_set_state(port, ERROR_RECOVERY, PD_T_PS_SOURCE_OFF);
+               break;
+       case PR_SWAP_SNK_SRC_SOURCE_ON:
+               tcpm_enable_auto_vbus_discharge(port, true);
+-- 
+2.39.5
+