diff options
27 files changed, 5098 insertions, 0 deletions
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 index 0000000000..5316241eab --- /dev/null +++ b/queue-6.12/alsa-hda-realtek-bass-speaker-fixup-for-asus-um5606k.patch @@ -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 index 0000000000..46d8526990 --- /dev/null +++ b/queue-6.12/arm64-dts-rockchip-add-avdd-hdmi-supplies-to-rockpro.patch @@ -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 index 0000000000..7ad39a914c --- /dev/null +++ b/queue-6.12/btrfs-do-proper-folio-cleanup-when-cow_file_range-fa.patch @@ -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 index 0000000000..e15489786e --- /dev/null +++ b/queue-6.12/btrfs-do-regular-iput-instead-of-delayed-iput-during.patch @@ -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 index 0000000000..a27a16bcdf --- /dev/null +++ b/queue-6.12/btrfs-fix-use-after-free-on-inode-when-scanning-root.patch @@ -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 index 0000000000..cff2e2ea54 --- /dev/null +++ b/queue-6.12/btrfs-make-the-extent-map-shrinker-run-asynchronousl.patch @@ -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 index 0000000000..91401e4adb --- /dev/null +++ b/queue-6.12/btrfs-skip-inodes-without-loaded-extent-maps-when-sh.patch @@ -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 index 0000000000..a02c3dc14c --- /dev/null +++ b/queue-6.12/btrfs-zoned-fix-extent-range-end-unlock-in-cow_file_.patch @@ -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 index 0000000000..a35498d35f --- /dev/null +++ b/queue-6.12/drm-amdkfd-fix-instruction-hazard-in-gfx12-trap-hand.patch @@ -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 index 0000000000..563c466e1f --- /dev/null +++ b/queue-6.12/drm-amdkfd-remove-gfx-12-trap-handler-page-size-cap.patch @@ -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 index 0000000000..ce3396e163 --- /dev/null +++ b/queue-6.12/drm-fbdev-dma-add-shadow-buffering-for-deferred-i-o.patch @@ -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 index 0000000000..9537b57b4a --- /dev/null +++ b/queue-6.12/drm-msm-dp-account-for-widebus-and-yuv420-during-mod.patch @@ -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 index 0000000000..f96c40e744 --- /dev/null +++ b/queue-6.12/drm-xe-carve-out-wopcm-portion-from-the-stolen-memor.patch @@ -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 index 0000000000..5381763973 --- /dev/null +++ b/queue-6.12/iio-dac-ad3552r-changes-to-use-field_prep.patch @@ -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 index 0000000000..16b62681be --- /dev/null +++ b/queue-6.12/iio-dac-ad3552r-common-fix-ad3541-2r-ranges.patch @@ -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 index 0000000000..4c83641978 --- /dev/null +++ b/queue-6.12/iio-dac-ad3552r-extract-common-code-no-changes-in-be.patch @@ -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 index 0000000000..0a730fe17f --- /dev/null +++ b/queue-6.12/kvm-arm64-set-hcr_el2.tid1-unconditionally.patch @@ -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, ¶ms); ++ if (params.Op1 == 1 && params.CRn == 0 && ++ params.CRm == 0 && params.Op2 == 7 /* AIDR */) + return kvm_emulate_cp15_id_reg(vcpu, ¶ms); + + return kvm_handle_cp_32(vcpu, ¶ms, 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 index 0000000000..d96a803c3c --- /dev/null +++ b/queue-6.12/loongarch-set-hugetlb-mmap-base-address-aligned-with.patch @@ -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 index 0000000000..09d2b3fa49 --- /dev/null +++ b/queue-6.12/net-stmmac-fix-accessing-freed-irq-affinity_hint.patch @@ -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 index 0000000000..829225fd10 --- /dev/null +++ b/queue-6.12/riscv-atomic-do-proper-sign-extension-also-for-unsig.patch @@ -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 + diff --git a/queue-6.12/series b/queue-6.12/series index fd8002791f..0cbce2df08 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -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 index 0000000000..99d418b99f --- /dev/null +++ b/queue-6.12/spi-fsl-qspi-fix-double-cleanup-in-probe-error-path.patch @@ -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 index 0000000000..5bc46487b3 --- /dev/null +++ b/queue-6.12/spi-fsl-qspi-support-per-spi-mem-operation-frequency.patch @@ -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 index 0000000000..9217c93921 --- /dev/null +++ b/queue-6.12/spi-fsl-qspi-use-devm-function-instead-of-driver-rem.patch @@ -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 index 0000000000..bbbc1c1bdc --- /dev/null +++ b/queue-6.12/spi-spi-mem-add-a-new-controller-capability.patch @@ -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 index 0000000000..5831735a27 --- /dev/null +++ b/queue-6.12/spi-spi-mem-extend-spi-mem-operations-with-a-per-ope.patch @@ -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 index 0000000000..98b05c94c5 --- /dev/null +++ b/queue-6.12/usb-typec-tcpm-pssourceofftimer-timeout-in-pr_swap-e.patch @@ -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 + |