--- /dev/null
+From d52bf582fb8877d648d6ba5f6f11aac09eea4639 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 24 Jul 2021 15:41:21 +0800
+Subject: ext4: add new helper interface ext4_try_to_trim_range()
+
+From: Wang Jianchao <wangjianchao@kuaishou.com>
+
+[ Upstream commit 6920b3913235f517728bb69abe9b39047a987113 ]
+
+There is no functional change in this patch but just split the
+codes, which serachs free block and does trim, into a new function
+ext4_try_to_trim_range. This is preparing for the following async
+backgroup discard.
+
+Reviewed-by: Andreas Dilger <adilger@dilger.ca>
+Signed-off-by: Wang Jianchao <wangjianchao@kuaishou.com>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20210724074124.25731-3-jianchao.wan9@gmail.com
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Stable-dep-of: 45e4ab320c9b ("ext4: move setting of trimmed bit into ext4_try_to_trim_range()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/mballoc.c | 102 ++++++++++++++++++++++++++--------------------
+ 1 file changed, 57 insertions(+), 45 deletions(-)
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 7b81094831754..51def652098b3 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -5184,6 +5184,54 @@ __acquires(bitlock)
+ return ret;
+ }
+
++static int ext4_try_to_trim_range(struct super_block *sb,
++ struct ext4_buddy *e4b, ext4_grpblk_t start,
++ ext4_grpblk_t max, ext4_grpblk_t minblocks)
++{
++ ext4_grpblk_t next, count, free_count;
++ void *bitmap;
++ int ret = 0;
++
++ bitmap = e4b->bd_bitmap;
++ start = (e4b->bd_info->bb_first_free > start) ?
++ e4b->bd_info->bb_first_free : start;
++ count = 0;
++ free_count = 0;
++
++ while (start <= max) {
++ start = mb_find_next_zero_bit(bitmap, max + 1, start);
++ if (start > max)
++ break;
++ next = mb_find_next_bit(bitmap, max + 1, start);
++
++ if ((next - start) >= minblocks) {
++ ret = ext4_trim_extent(sb, start, next - start, e4b);
++ if (ret && ret != -EOPNOTSUPP)
++ break;
++ ret = 0;
++ count += next - start;
++ }
++ free_count += next - start;
++ start = next + 1;
++
++ if (fatal_signal_pending(current)) {
++ count = -ERESTARTSYS;
++ break;
++ }
++
++ if (need_resched()) {
++ ext4_unlock_group(sb, e4b->bd_group);
++ cond_resched();
++ ext4_lock_group(sb, e4b->bd_group);
++ }
++
++ if ((e4b->bd_info->bb_free - free_count) < minblocks)
++ break;
++ }
++
++ return count;
++}
++
+ /**
+ * ext4_trim_all_free -- function to trim all free space in alloc. group
+ * @sb: super block for file system
+@@ -5207,10 +5255,8 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ ext4_grpblk_t start, ext4_grpblk_t max,
+ ext4_grpblk_t minblocks)
+ {
+- void *bitmap;
+- ext4_grpblk_t next, count = 0, free_count = 0;
+ struct ext4_buddy e4b;
+- int ret = 0;
++ int ret;
+
+ trace_ext4_trim_all_free(sb, group, start, max);
+
+@@ -5220,57 +5266,23 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ ret, group);
+ return ret;
+ }
+- bitmap = e4b.bd_bitmap;
+
+ ext4_lock_group(sb, group);
+- if (EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) &&
+- minblocks >= atomic_read(&EXT4_SB(sb)->s_last_trim_minblks))
+- goto out;
+-
+- start = (e4b.bd_info->bb_first_free > start) ?
+- e4b.bd_info->bb_first_free : start;
+-
+- while (start <= max) {
+- start = mb_find_next_zero_bit(bitmap, max + 1, start);
+- if (start > max)
+- break;
+- next = mb_find_next_bit(bitmap, max + 1, start);
+-
+- if ((next - start) >= minblocks) {
+- ret = ext4_trim_extent(sb, start, next - start, &e4b);
+- if (ret && ret != -EOPNOTSUPP)
+- break;
+- ret = 0;
+- count += next - start;
+- }
+- free_count += next - start;
+- start = next + 1;
+-
+- if (fatal_signal_pending(current)) {
+- count = -ERESTARTSYS;
+- break;
+- }
+-
+- if (need_resched()) {
+- ext4_unlock_group(sb, group);
+- cond_resched();
+- ext4_lock_group(sb, group);
+- }
+
+- if ((e4b.bd_info->bb_free - free_count) < minblocks)
+- break;
++ if (!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) ||
++ minblocks < atomic_read(&EXT4_SB(sb)->s_last_trim_minblks)) {
++ ret = ext4_try_to_trim_range(sb, &e4b, start, max, minblocks);
++ if (ret >= 0)
++ EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
++ } else {
++ ret = 0;
+ }
+
+- if (!ret) {
+- ret = count;
+- EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
+- }
+-out:
+ ext4_unlock_group(sb, group);
+ ext4_mb_unload_buddy(&e4b);
+
+ ext4_debug("trimmed %d blocks in the group %d\n",
+- count, group);
++ ret, group);
+
+ return ret;
+ }
+--
+2.40.1
+
--- /dev/null
+From 2d6c87ae043945d0e573fec55db8b466e4c79251 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Nov 2021 15:51:21 +0100
+Subject: ext4: change s_last_trim_minblks type to unsigned long
+
+From: Lukas Czerner <lczerner@redhat.com>
+
+[ Upstream commit 2327fb2e23416cfb2795ccca2f77d4d65925be99 ]
+
+There is no good reason for the s_last_trim_minblks to be atomic. There is
+no data integrity needed and there is no real danger in setting and
+reading it in a racy manner. Change it to be unsigned long, the same type
+as s_clusters_per_group which is the maximum that's allowed.
+
+Signed-off-by: Lukas Czerner <lczerner@redhat.com>
+Suggested-by: Andreas Dilger <adilger@dilger.ca>
+Reviewed-by: Andreas Dilger <adilger@dilger.ca>
+Link: https://lore.kernel.org/r/20211103145122.17338-1-lczerner@redhat.com
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Stable-dep-of: 45e4ab320c9b ("ext4: move setting of trimmed bit into ext4_try_to_trim_range()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/ext4.h | 2 +-
+ fs/ext4/mballoc.c | 4 ++--
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
+index 909f231a387d7..fa2579abea7df 100644
+--- a/fs/ext4/ext4.h
++++ b/fs/ext4/ext4.h
+@@ -1500,7 +1500,7 @@ struct ext4_sb_info {
+ struct task_struct *s_mmp_tsk;
+
+ /* record the last minlen when FITRIM is called. */
+- atomic_t s_last_trim_minblks;
++ unsigned long s_last_trim_minblks;
+
+ /* Reference to checksum algorithm driver via cryptoapi */
+ struct crypto_shash *s_chksum_driver;
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 58a0d2ea314b7..b76e8b8f01a10 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -5269,7 +5269,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ ext4_lock_group(sb, group);
+
+ if (!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) ||
+- minblocks < atomic_read(&EXT4_SB(sb)->s_last_trim_minblks)) {
++ minblocks < EXT4_SB(sb)->s_last_trim_minblks) {
+ ret = ext4_try_to_trim_range(sb, &e4b, start, max, minblocks);
+ if (ret >= 0)
+ EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
+@@ -5378,7 +5378,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ }
+
+ if (!ret)
+- atomic_set(&EXT4_SB(sb)->s_last_trim_minblks, minlen);
++ EXT4_SB(sb)->s_last_trim_minblks = minlen;
+
+ out:
+ range->len = EXT4_C2B(EXT4_SB(sb), trimmed) << sb->s_blocksize_bits;
+--
+2.40.1
+
--- /dev/null
+From 63fc38e35ab0e5b042f060431aae33af99041217 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 Sep 2023 17:04:55 +0200
+Subject: ext4: do not let fstrim block system suspend
+
+From: Jan Kara <jack@suse.cz>
+
+[ Upstream commit 5229a658f6453362fbb9da6bf96872ef25a7097e ]
+
+Len Brown has reported that system suspend sometimes fail due to
+inability to freeze a task working in ext4_trim_fs() for one minute.
+Trimming a large filesystem on a disk that slowly processes discard
+requests can indeed take a long time. Since discard is just an advisory
+call, it is perfectly fine to interrupt it at any time and the return
+number of discarded blocks until that moment. Do that when we detect the
+task is being frozen.
+
+Cc: stable@kernel.org
+Reported-by: Len Brown <lenb@kernel.org>
+Suggested-by: Dave Chinner <david@fromorbit.com>
+References: https://bugzilla.kernel.org/show_bug.cgi?id=216322
+Signed-off-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20230913150504.9054-2-jack@suse.cz
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/mballoc.c | 12 ++++++++++--
+ 1 file changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 94b3bf8173e20..fb2f255c48e81 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -16,6 +16,7 @@
+ #include <linux/slab.h>
+ #include <linux/nospec.h>
+ #include <linux/backing-dev.h>
++#include <linux/freezer.h>
+ #include <trace/events/ext4.h>
+
+ #ifdef CONFIG_EXT4_DEBUG
+@@ -5194,6 +5195,11 @@ static ext4_grpblk_t ext4_last_grp_cluster(struct super_block *sb,
+ EXT4_CLUSTER_BITS(sb);
+ }
+
++static bool ext4_trim_interrupted(void)
++{
++ return fatal_signal_pending(current) || freezing(current);
++}
++
+ static int ext4_try_to_trim_range(struct super_block *sb,
+ struct ext4_buddy *e4b, ext4_grpblk_t start,
+ ext4_grpblk_t max, ext4_grpblk_t minblocks)
+@@ -5225,8 +5231,8 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ free_count += next - start;
+ start = next + 1;
+
+- if (fatal_signal_pending(current))
+- return -ERESTARTSYS;
++ if (ext4_trim_interrupted())
++ return count;
+
+ if (need_resched()) {
+ ext4_unlock_group(sb, e4b->bd_group);
+@@ -5353,6 +5359,8 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ end = EXT4_CLUSTERS_PER_GROUP(sb) - 1;
+
+ for (group = first_group; group <= last_group; group++) {
++ if (ext4_trim_interrupted())
++ break;
+ grp = ext4_get_group_info(sb, group);
+ /* We only do this if the grp has never been initialized */
+ if (unlikely(EXT4_MB_GRP_NEED_INIT(grp))) {
+--
+2.40.1
+
--- /dev/null
+From e4ed3fc2904824cbb013485288c7dc6264b33f95 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 17 Apr 2022 20:03:15 +0300
+Subject: ext4: mark group as trimmed only if it was fully scanned
+
+From: Dmitry Monakhov <dmtrmonakhov@yandex-team.ru>
+
+[ Upstream commit d63c00ea435a5352f486c259665a4ced60399421 ]
+
+Otherwise nonaligned fstrim calls will works inconveniently for iterative
+scanners, for example:
+
+// trim [0,16MB] for group-1, but mark full group as trimmed
+fstrim -o $((1024*1024*128)) -l $((1024*1024*16)) ./m
+// handle [16MB,16MB] for group-1, do nothing because group already has the flag.
+fstrim -o $((1024*1024*144)) -l $((1024*1024*16)) ./m
+
+[ Update function documentation for ext4_trim_all_free -- TYT ]
+
+Signed-off-by: Dmitry Monakhov <dmtrmonakhov@yandex-team.ru>
+Link: https://lore.kernel.org/r/1650214995-860245-1-git-send-email-dmtrmonakhov@yandex-team.ru
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Cc: stable@kernel.org
+Stable-dep-of: 45e4ab320c9b ("ext4: move setting of trimmed bit into ext4_try_to_trim_range()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/mballoc.c | 18 ++++++++++++------
+ 1 file changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index b76e8b8f01a10..e926b8c3ea893 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -5238,6 +5238,7 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ * @start: first group block to examine
+ * @max: last group block to examine
+ * @minblocks: minimum extent block count
++ * @set_trimmed: set the trimmed flag if at least one block is trimmed
+ *
+ * ext4_trim_all_free walks through group's buddy bitmap searching for free
+ * extents. When the free block is found, ext4_trim_extent is called to TRIM
+@@ -5252,7 +5253,7 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ static ext4_grpblk_t
+ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ ext4_grpblk_t start, ext4_grpblk_t max,
+- ext4_grpblk_t minblocks)
++ ext4_grpblk_t minblocks, bool set_trimmed)
+ {
+ struct ext4_buddy e4b;
+ int ret;
+@@ -5271,7 +5272,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ if (!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) ||
+ minblocks < EXT4_SB(sb)->s_last_trim_minblks) {
+ ret = ext4_try_to_trim_range(sb, &e4b, start, max, minblocks);
+- if (ret >= 0)
++ if (ret >= 0 && set_trimmed)
+ EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
+ } else {
+ ret = 0;
+@@ -5308,6 +5309,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ ext4_fsblk_t first_data_blk =
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
+ ext4_fsblk_t max_blks = ext4_blocks_count(EXT4_SB(sb)->s_es);
++ bool whole_group, eof = false;
+ int ret = 0;
+
+ start = range->start >> sb->s_blocksize_bits;
+@@ -5326,8 +5328,10 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ if (minlen > EXT4_CLUSTERS_PER_GROUP(sb))
+ goto out;
+ }
+- if (end >= max_blks)
++ if (end >= max_blks - 1) {
+ end = max_blks - 1;
++ eof = true;
++ }
+ if (end <= first_data_blk)
+ goto out;
+ if (start < first_data_blk)
+@@ -5341,6 +5345,7 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+
+ /* end now represents the last cluster to discard in this group */
+ end = EXT4_CLUSTERS_PER_GROUP(sb) - 1;
++ whole_group = true;
+
+ for (group = first_group; group <= last_group; group++) {
+ grp = ext4_get_group_info(sb, group);
+@@ -5357,12 +5362,13 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ * change it for the last group, note that last_cluster is
+ * already computed earlier by ext4_get_group_no_and_offset()
+ */
+- if (group == last_group)
++ if (group == last_group) {
+ end = last_cluster;
+-
++ whole_group = eof ? true : end == EXT4_CLUSTERS_PER_GROUP(sb) - 1;
++ }
+ if (grp->bb_free >= minlen) {
+ cnt = ext4_trim_all_free(sb, group, first_cluster,
+- end, minlen);
++ end, minlen, whole_group);
+ if (cnt < 0) {
+ ret = cnt;
+ break;
+--
+2.40.1
+
--- /dev/null
+From 7615b899fabc012b520ccf99eb10a9507834b98b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 13 Sep 2023 17:04:54 +0200
+Subject: ext4: move setting of trimmed bit into ext4_try_to_trim_range()
+
+From: Jan Kara <jack@suse.cz>
+
+[ Upstream commit 45e4ab320c9b5fa67b1fc3b6a9b381cfcc0c8488 ]
+
+Currently we set the group's trimmed bit in ext4_trim_all_free() based
+on return value of ext4_try_to_trim_range(). However when we will want
+to abort trimming because of suspend attempt, we want to return success
+from ext4_try_to_trim_range() but not set the trimmed bit. Instead
+implementing awkward propagation of this information, just move setting
+of trimmed bit into ext4_try_to_trim_range() when the whole group is
+trimmed.
+
+Cc: stable@kernel.org
+Signed-off-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20230913150504.9054-1-jack@suse.cz
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/mballoc.c | 46 +++++++++++++++++++++++++---------------------
+ 1 file changed, 25 insertions(+), 21 deletions(-)
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 22da6b1143e5d..94b3bf8173e20 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -5184,14 +5184,27 @@ __acquires(bitlock)
+ return ret;
+ }
+
++static ext4_grpblk_t ext4_last_grp_cluster(struct super_block *sb,
++ ext4_group_t grp)
++{
++ if (grp < ext4_get_groups_count(sb))
++ return EXT4_CLUSTERS_PER_GROUP(sb) - 1;
++ return (ext4_blocks_count(EXT4_SB(sb)->s_es) -
++ ext4_group_first_block_no(sb, grp) - 1) >>
++ EXT4_CLUSTER_BITS(sb);
++}
++
+ static int ext4_try_to_trim_range(struct super_block *sb,
+ struct ext4_buddy *e4b, ext4_grpblk_t start,
+ ext4_grpblk_t max, ext4_grpblk_t minblocks)
+ {
+ ext4_grpblk_t next, count, free_count;
++ bool set_trimmed = false;
+ void *bitmap;
+
+ bitmap = e4b->bd_bitmap;
++ if (start == 0 && max >= ext4_last_grp_cluster(sb, e4b->bd_group))
++ set_trimmed = true;
+ start = max(e4b->bd_info->bb_first_free, start);
+ count = 0;
+ free_count = 0;
+@@ -5206,16 +5219,14 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ int ret = ext4_trim_extent(sb, start, next - start, e4b);
+
+ if (ret && ret != -EOPNOTSUPP)
+- break;
++ return count;
+ count += next - start;
+ }
+ free_count += next - start;
+ start = next + 1;
+
+- if (fatal_signal_pending(current)) {
+- count = -ERESTARTSYS;
+- break;
+- }
++ if (fatal_signal_pending(current))
++ return -ERESTARTSYS;
+
+ if (need_resched()) {
+ ext4_unlock_group(sb, e4b->bd_group);
+@@ -5227,6 +5238,9 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ break;
+ }
+
++ if (set_trimmed)
++ EXT4_MB_GRP_SET_TRIMMED(e4b->bd_info);
++
+ return count;
+ }
+
+@@ -5237,7 +5251,6 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ * @start: first group block to examine
+ * @max: last group block to examine
+ * @minblocks: minimum extent block count
+- * @set_trimmed: set the trimmed flag if at least one block is trimmed
+ *
+ * ext4_trim_all_free walks through group's buddy bitmap searching for free
+ * extents. When the free block is found, ext4_trim_extent is called to TRIM
+@@ -5252,7 +5265,7 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ static ext4_grpblk_t
+ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ ext4_grpblk_t start, ext4_grpblk_t max,
+- ext4_grpblk_t minblocks, bool set_trimmed)
++ ext4_grpblk_t minblocks)
+ {
+ struct ext4_buddy e4b;
+ int ret;
+@@ -5269,13 +5282,10 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ ext4_lock_group(sb, group);
+
+ if (!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) ||
+- minblocks < EXT4_SB(sb)->s_last_trim_minblks) {
++ minblocks < EXT4_SB(sb)->s_last_trim_minblks)
+ ret = ext4_try_to_trim_range(sb, &e4b, start, max, minblocks);
+- if (ret >= 0 && set_trimmed)
+- EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info);
+- } else {
++ else
+ ret = 0;
+- }
+
+ ext4_unlock_group(sb, group);
+ ext4_mb_unload_buddy(&e4b);
+@@ -5308,7 +5318,6 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ ext4_fsblk_t first_data_blk =
+ le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block);
+ ext4_fsblk_t max_blks = ext4_blocks_count(EXT4_SB(sb)->s_es);
+- bool whole_group, eof = false;
+ int ret = 0;
+
+ start = range->start >> sb->s_blocksize_bits;
+@@ -5327,10 +5336,8 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ if (minlen > EXT4_CLUSTERS_PER_GROUP(sb))
+ goto out;
+ }
+- if (end >= max_blks - 1) {
++ if (end >= max_blks - 1)
+ end = max_blks - 1;
+- eof = true;
+- }
+ if (end <= first_data_blk)
+ goto out;
+ if (start < first_data_blk)
+@@ -5344,7 +5351,6 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+
+ /* end now represents the last cluster to discard in this group */
+ end = EXT4_CLUSTERS_PER_GROUP(sb) - 1;
+- whole_group = true;
+
+ for (group = first_group; group <= last_group; group++) {
+ grp = ext4_get_group_info(sb, group);
+@@ -5361,13 +5367,11 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range)
+ * change it for the last group, note that last_cluster is
+ * already computed earlier by ext4_get_group_no_and_offset()
+ */
+- if (group == last_group) {
++ if (group == last_group)
+ end = last_cluster;
+- whole_group = eof ? true : end == EXT4_CLUSTERS_PER_GROUP(sb) - 1;
+- }
+ if (grp->bb_free >= minlen) {
+ cnt = ext4_trim_all_free(sb, group, first_cluster,
+- end, minlen, whole_group);
++ end, minlen);
+ if (cnt < 0) {
+ ret = cnt;
+ break;
+--
+2.40.1
+
--- /dev/null
+From 5b2e280bd439a66416f6278a45bfc619118ee65c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 24 Jul 2021 15:41:20 +0800
+Subject: ext4: remove the 'group' parameter of ext4_trim_extent
+
+From: Wang Jianchao <wangjianchao@kuaishou.com>
+
+[ Upstream commit bd2eea8d0a6b6a9aca22f20bf74f73b71d8808af ]
+
+Get rid of the 'group' parameter of ext4_trim_extent as we can get
+it from the 'e4b'.
+
+Reviewed-by: Andreas Dilger <adilger@dilger.ca>
+Signed-off-by: Wang Jianchao <wangjianchao@kuaishou.com>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20210724074124.25731-2-jianchao.wan9@gmail.com
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Stable-dep-of: 45e4ab320c9b ("ext4: move setting of trimmed bit into ext4_try_to_trim_range()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/mballoc.c | 9 ++++-----
+ 1 file changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 7692c12b85285..7b81094831754 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -5149,19 +5149,19 @@ int ext4_group_add_blocks(handle_t *handle, struct super_block *sb,
+ * @sb: super block for the file system
+ * @start: starting block of the free extent in the alloc. group
+ * @count: number of blocks to TRIM
+- * @group: alloc. group we are working with
+ * @e4b: ext4 buddy for the group
+ *
+ * Trim "count" blocks starting at "start" in the "group". To assure that no
+ * one will allocate those blocks, mark it as used in buddy bitmap. This must
+ * be called with under the group lock.
+ */
+-static int ext4_trim_extent(struct super_block *sb, int start, int count,
+- ext4_group_t group, struct ext4_buddy *e4b)
++static int ext4_trim_extent(struct super_block *sb,
++ int start, int count, struct ext4_buddy *e4b)
+ __releases(bitlock)
+ __acquires(bitlock)
+ {
+ struct ext4_free_extent ex;
++ ext4_group_t group = e4b->bd_group;
+ int ret = 0;
+
+ trace_ext4_trim_extent(sb, group, start, count);
+@@ -5237,8 +5237,7 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group,
+ next = mb_find_next_bit(bitmap, max + 1, start);
+
+ if ((next - start) >= minblocks) {
+- ret = ext4_trim_extent(sb, start,
+- next - start, group, &e4b);
++ ret = ext4_trim_extent(sb, start, next - start, &e4b);
+ if (ret && ret != -EOPNOTSUPP)
+ break;
+ ret = 0;
+--
+2.40.1
+
--- /dev/null
+From 4dbbba480ea84526dfbf9badd67d2c0ee5bf9434 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 1 Aug 2023 22:32:00 +0800
+Subject: ext4: replace the traditional ternary conditional operator with with
+ max()/min()
+
+From: Kemeng Shi <shikemeng@huaweicloud.com>
+
+[ Upstream commit de8bf0e5ee7482585450357c6d4eddec8efc5cb7 ]
+
+Replace the traditional ternary conditional operator with with max()/min()
+
+Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com>
+Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com>
+Link: https://lore.kernel.org/r/20230801143204.2284343-7-shikemeng@huaweicloud.com
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Stable-dep-of: 45e4ab320c9b ("ext4: move setting of trimmed bit into ext4_try_to_trim_range()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/mballoc.c | 6 ++----
+ 1 file changed, 2 insertions(+), 4 deletions(-)
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index e926b8c3ea893..22da6b1143e5d 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -5192,8 +5192,7 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ void *bitmap;
+
+ bitmap = e4b->bd_bitmap;
+- start = (e4b->bd_info->bb_first_free > start) ?
+- e4b->bd_info->bb_first_free : start;
++ start = max(e4b->bd_info->bb_first_free, start);
+ count = 0;
+ free_count = 0;
+
+@@ -5413,8 +5412,7 @@ ext4_mballoc_query_range(
+
+ ext4_lock_group(sb, group);
+
+- start = (e4b.bd_info->bb_first_free > start) ?
+- e4b.bd_info->bb_first_free : start;
++ start = max(e4b.bd_info->bb_first_free, start);
+ if (end >= EXT4_CLUSTERS_PER_GROUP(sb))
+ end = EXT4_CLUSTERS_PER_GROUP(sb) - 1;
+
+--
+2.40.1
+
--- /dev/null
+From 21abc74977d5eb29ce86d5eb9d66b9bad594bf87 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 20 Aug 2021 14:08:53 +0200
+Subject: ext4: scope ret locally in ext4_try_to_trim_range()
+
+From: Lukas Bulwahn <lukas.bulwahn@gmail.com>
+
+[ Upstream commit afcc4e32f606dbfb47aa7309172c89174b86e74c ]
+
+As commit 6920b3913235 ("ext4: add new helper interface
+ext4_try_to_trim_range()") moves some code into the separate function
+ext4_try_to_trim_range(), the use of the variable ret within that
+function is more limited and can be adjusted as well.
+
+Scope the use of the variable ret locally and drop dead assignments.
+
+No functional change.
+
+Signed-off-by: Lukas Bulwahn <lukas.bulwahn@gmail.com>
+Link: https://lore.kernel.org/r/20210820120853.23134-1-lukas.bulwahn@gmail.com
+Signed-off-by: Theodore Ts'o <tytso@mit.edu>
+Stable-dep-of: 45e4ab320c9b ("ext4: move setting of trimmed bit into ext4_try_to_trim_range()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ext4/mballoc.c | 5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 51def652098b3..58a0d2ea314b7 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -5190,7 +5190,6 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ {
+ ext4_grpblk_t next, count, free_count;
+ void *bitmap;
+- int ret = 0;
+
+ bitmap = e4b->bd_bitmap;
+ start = (e4b->bd_info->bb_first_free > start) ?
+@@ -5205,10 +5204,10 @@ static int ext4_try_to_trim_range(struct super_block *sb,
+ next = mb_find_next_bit(bitmap, max + 1, start);
+
+ if ((next - start) >= minblocks) {
+- ret = ext4_trim_extent(sb, start, next - start, e4b);
++ int ret = ext4_trim_extent(sb, start, next - start, e4b);
++
+ if (ret && ret != -EOPNOTSUPP)
+ break;
+- ret = 0;
+ count += next - start;
+ }
+ free_count += next - start;
+--
+2.40.1
+
--- /dev/null
+From 85b2d545e7b9ab18722f944b01307672b1737ad4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 12 Jul 2023 11:56:51 -0700
+Subject: Input: i8042 - add quirk for TUXEDO Gemini 17 Gen1/Clevo PD70PN
+
+From: Werner Sembach <wse@tuxedocomputers.com>
+
+[ Upstream commit eb09074bdb05ffd6bfe77f8b4a41b76ef78c997b ]
+
+The touchpad of this device is both connected via PS/2 and i2c. This causes
+strange behavior when both driver fight for control. The easy fix is to
+prevent the PS/2 driver from accessing the mouse port as the full feature
+set of the touchpad is only supported in the i2c interface anyway.
+
+The strange behavior in this case is, that when an external screen is
+connected and the notebook is closed, the pointer on the external screen is
+moving to the lower right corner. When the notebook is opened again, this
+movement stops, but the touchpad clicks are unresponsive afterwards until
+reboot.
+
+Signed-off-by: Werner Sembach <wse@tuxedocomputers.com>
+Cc: stable@vger.kernel.org
+Link: https://lore.kernel.org/r/20230607173331.851192-1-wse@tuxedocomputers.com
+Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/input/serio/i8042-x86ia64io.h | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
+index da2bf8259330e..0cf9a37873261 100644
+--- a/drivers/input/serio/i8042-x86ia64io.h
++++ b/drivers/input/serio/i8042-x86ia64io.h
+@@ -1188,6 +1188,13 @@ static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
+ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
+ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
++ /* See comment on TUXEDO InfinityBook S17 Gen6 / Clevo NS70MU above */
++ {
++ .matches = {
++ DMI_MATCH(DMI_BOARD_NAME, "PD5x_7xPNP_PNR_PNN_PNT"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_NOAUX)
++ },
+ {
+ .matches = {
+ DMI_MATCH(DMI_BOARD_NAME, "X170SM"),
+--
+2.40.1
+
--- /dev/null
+From 91a3f294febcd7987d48cf7e248db51be5a93290 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 28 Aug 2023 15:10:18 -0700
+Subject: scsi: megaraid_sas: Fix deadlock on firmware crashdump
+
+From: Junxiao Bi <junxiao.bi@oracle.com>
+
+[ Upstream commit 0b0747d507bffb827e40fc0f9fb5883fffc23477 ]
+
+The following processes run into a deadlock. CPU 41 was waiting for CPU 29
+to handle a CSD request while holding spinlock "crashdump_lock", but CPU 29
+was hung by that spinlock with IRQs disabled.
+
+ PID: 17360 TASK: ffff95c1090c5c40 CPU: 41 COMMAND: "mrdiagd"
+ !# 0 [ffffb80edbf37b58] __read_once_size at ffffffff9b871a40 include/linux/compiler.h:185:0
+ !# 1 [ffffb80edbf37b58] atomic_read at ffffffff9b871a40 arch/x86/include/asm/atomic.h:27:0
+ !# 2 [ffffb80edbf37b58] dump_stack at ffffffff9b871a40 lib/dump_stack.c:54:0
+ # 3 [ffffb80edbf37b78] csd_lock_wait_toolong at ffffffff9b131ad5 kernel/smp.c:364:0
+ # 4 [ffffb80edbf37b78] __csd_lock_wait at ffffffff9b131ad5 kernel/smp.c:384:0
+ # 5 [ffffb80edbf37bf8] csd_lock_wait at ffffffff9b13267a kernel/smp.c:394:0
+ # 6 [ffffb80edbf37bf8] smp_call_function_many at ffffffff9b13267a kernel/smp.c:843:0
+ # 7 [ffffb80edbf37c50] smp_call_function at ffffffff9b13279d kernel/smp.c:867:0
+ # 8 [ffffb80edbf37c50] on_each_cpu at ffffffff9b13279d kernel/smp.c:976:0
+ # 9 [ffffb80edbf37c78] flush_tlb_kernel_range at ffffffff9b085c4b arch/x86/mm/tlb.c:742:0
+ #10 [ffffb80edbf37cb8] __purge_vmap_area_lazy at ffffffff9b23a1e0 mm/vmalloc.c:701:0
+ #11 [ffffb80edbf37ce0] try_purge_vmap_area_lazy at ffffffff9b23a2cc mm/vmalloc.c:722:0
+ #12 [ffffb80edbf37ce0] free_vmap_area_noflush at ffffffff9b23a2cc mm/vmalloc.c:754:0
+ #13 [ffffb80edbf37cf8] free_unmap_vmap_area at ffffffff9b23bb3b mm/vmalloc.c:764:0
+ #14 [ffffb80edbf37cf8] remove_vm_area at ffffffff9b23bb3b mm/vmalloc.c:1509:0
+ #15 [ffffb80edbf37d18] __vunmap at ffffffff9b23bb8a mm/vmalloc.c:1537:0
+ #16 [ffffb80edbf37d40] vfree at ffffffff9b23bc85 mm/vmalloc.c:1612:0
+ #17 [ffffb80edbf37d58] megasas_free_host_crash_buffer [megaraid_sas] at ffffffffc020b7f2 drivers/scsi/megaraid/megaraid_sas_fusion.c:3932:0
+ #18 [ffffb80edbf37d80] fw_crash_state_store [megaraid_sas] at ffffffffc01f804d drivers/scsi/megaraid/megaraid_sas_base.c:3291:0
+ #19 [ffffb80edbf37dc0] dev_attr_store at ffffffff9b56dd7b drivers/base/core.c:758:0
+ #20 [ffffb80edbf37dd0] sysfs_kf_write at ffffffff9b326acf fs/sysfs/file.c:144:0
+ #21 [ffffb80edbf37de0] kernfs_fop_write at ffffffff9b325fd4 fs/kernfs/file.c:316:0
+ #22 [ffffb80edbf37e20] __vfs_write at ffffffff9b29418a fs/read_write.c:480:0
+ #23 [ffffb80edbf37ea8] vfs_write at ffffffff9b294462 fs/read_write.c:544:0
+ #24 [ffffb80edbf37ee8] SYSC_write at ffffffff9b2946ec fs/read_write.c:590:0
+ #25 [ffffb80edbf37ee8] SyS_write at ffffffff9b2946ec fs/read_write.c:582:0
+ #26 [ffffb80edbf37f30] do_syscall_64 at ffffffff9b003ca9 arch/x86/entry/common.c:298:0
+ #27 [ffffb80edbf37f58] entry_SYSCALL_64 at ffffffff9ba001b1 arch/x86/entry/entry_64.S:238:0
+
+ PID: 17355 TASK: ffff95c1090c3d80 CPU: 29 COMMAND: "mrdiagd"
+ !# 0 [ffffb80f2d3c7d30] __read_once_size at ffffffff9b0f2ab0 include/linux/compiler.h:185:0
+ !# 1 [ffffb80f2d3c7d30] native_queued_spin_lock_slowpath at ffffffff9b0f2ab0 kernel/locking/qspinlock.c:368:0
+ # 2 [ffffb80f2d3c7d58] pv_queued_spin_lock_slowpath at ffffffff9b0f244b arch/x86/include/asm/paravirt.h:674:0
+ # 3 [ffffb80f2d3c7d58] queued_spin_lock_slowpath at ffffffff9b0f244b arch/x86/include/asm/qspinlock.h:53:0
+ # 4 [ffffb80f2d3c7d68] queued_spin_lock at ffffffff9b8961a6 include/asm-generic/qspinlock.h:90:0
+ # 5 [ffffb80f2d3c7d68] do_raw_spin_lock_flags at ffffffff9b8961a6 include/linux/spinlock.h:173:0
+ # 6 [ffffb80f2d3c7d68] __raw_spin_lock_irqsave at ffffffff9b8961a6 include/linux/spinlock_api_smp.h:122:0
+ # 7 [ffffb80f2d3c7d68] _raw_spin_lock_irqsave at ffffffff9b8961a6 kernel/locking/spinlock.c:160:0
+ # 8 [ffffb80f2d3c7d88] fw_crash_buffer_store [megaraid_sas] at ffffffffc01f8129 drivers/scsi/megaraid/megaraid_sas_base.c:3205:0
+ # 9 [ffffb80f2d3c7dc0] dev_attr_store at ffffffff9b56dd7b drivers/base/core.c:758:0
+ #10 [ffffb80f2d3c7dd0] sysfs_kf_write at ffffffff9b326acf fs/sysfs/file.c:144:0
+ #11 [ffffb80f2d3c7de0] kernfs_fop_write at ffffffff9b325fd4 fs/kernfs/file.c:316:0
+ #12 [ffffb80f2d3c7e20] __vfs_write at ffffffff9b29418a fs/read_write.c:480:0
+ #13 [ffffb80f2d3c7ea8] vfs_write at ffffffff9b294462 fs/read_write.c:544:0
+ #14 [ffffb80f2d3c7ee8] SYSC_write at ffffffff9b2946ec fs/read_write.c:590:0
+ #15 [ffffb80f2d3c7ee8] SyS_write at ffffffff9b2946ec fs/read_write.c:582:0
+ #16 [ffffb80f2d3c7f30] do_syscall_64 at ffffffff9b003ca9 arch/x86/entry/common.c:298:0
+ #17 [ffffb80f2d3c7f58] entry_SYSCALL_64 at ffffffff9ba001b1 arch/x86/entry/entry_64.S:238:0
+
+The lock is used to synchronize different sysfs operations, it doesn't
+protect any resource that will be touched by an interrupt. Consequently
+it's not required to disable IRQs. Replace the spinlock with a mutex to fix
+the deadlock.
+
+Signed-off-by: Junxiao Bi <junxiao.bi@oracle.com>
+Link: https://lore.kernel.org/r/20230828221018.19471-1-junxiao.bi@oracle.com
+Reviewed-by: Mike Christie <michael.christie@oracle.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/megaraid/megaraid_sas.h | 2 +-
+ drivers/scsi/megaraid/megaraid_sas_base.c | 21 +++++++++------------
+ 2 files changed, 10 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
+index 80ae48b65b608..0d7cca9365aac 100644
+--- a/drivers/scsi/megaraid/megaraid_sas.h
++++ b/drivers/scsi/megaraid/megaraid_sas.h
+@@ -2194,7 +2194,7 @@ struct megasas_instance {
+ u32 support_morethan256jbod; /* FW support for more than 256 PD/JBOD */
+ bool use_seqnum_jbod_fp; /* Added for PD sequence */
+ bool smp_affinity_enable;
+- spinlock_t crashdump_lock;
++ struct mutex crashdump_lock;
+
+ struct megasas_register_set __iomem *reg_set;
+ u32 __iomem *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY];
+diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
+index 2b6b6d3deba86..bdfa36712fcc4 100644
+--- a/drivers/scsi/megaraid/megaraid_sas_base.c
++++ b/drivers/scsi/megaraid/megaraid_sas_base.c
+@@ -3004,14 +3004,13 @@ megasas_fw_crash_buffer_store(struct device *cdev,
+ struct megasas_instance *instance =
+ (struct megasas_instance *) shost->hostdata;
+ int val = 0;
+- unsigned long flags;
+
+ if (kstrtoint(buf, 0, &val) != 0)
+ return -EINVAL;
+
+- spin_lock_irqsave(&instance->crashdump_lock, flags);
++ mutex_lock(&instance->crashdump_lock);
+ instance->fw_crash_buffer_offset = val;
+- spin_unlock_irqrestore(&instance->crashdump_lock, flags);
++ mutex_unlock(&instance->crashdump_lock);
+ return strlen(buf);
+ }
+
+@@ -3027,17 +3026,16 @@ megasas_fw_crash_buffer_show(struct device *cdev,
+ unsigned long dmachunk = CRASH_DMA_BUF_SIZE;
+ unsigned long chunk_left_bytes;
+ unsigned long src_addr;
+- unsigned long flags;
+ u32 buff_offset;
+
+- spin_lock_irqsave(&instance->crashdump_lock, flags);
++ mutex_lock(&instance->crashdump_lock);
+ buff_offset = instance->fw_crash_buffer_offset;
+ if (!instance->crash_dump_buf ||
+ !((instance->fw_crash_state == AVAILABLE) ||
+ (instance->fw_crash_state == COPYING))) {
+ dev_err(&instance->pdev->dev,
+ "Firmware crash dump is not available\n");
+- spin_unlock_irqrestore(&instance->crashdump_lock, flags);
++ mutex_unlock(&instance->crashdump_lock);
+ return -EINVAL;
+ }
+
+@@ -3046,7 +3044,7 @@ megasas_fw_crash_buffer_show(struct device *cdev,
+ if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) {
+ dev_err(&instance->pdev->dev,
+ "Firmware crash dump offset is out of range\n");
+- spin_unlock_irqrestore(&instance->crashdump_lock, flags);
++ mutex_unlock(&instance->crashdump_lock);
+ return 0;
+ }
+
+@@ -3058,7 +3056,7 @@ megasas_fw_crash_buffer_show(struct device *cdev,
+ src_addr = (unsigned long)instance->crash_buf[buff_offset / dmachunk] +
+ (buff_offset % dmachunk);
+ memcpy(buf, (void *)src_addr, size);
+- spin_unlock_irqrestore(&instance->crashdump_lock, flags);
++ mutex_unlock(&instance->crashdump_lock);
+
+ return size;
+ }
+@@ -3083,7 +3081,6 @@ megasas_fw_crash_state_store(struct device *cdev,
+ struct megasas_instance *instance =
+ (struct megasas_instance *) shost->hostdata;
+ int val = 0;
+- unsigned long flags;
+
+ if (kstrtoint(buf, 0, &val) != 0)
+ return -EINVAL;
+@@ -3097,9 +3094,9 @@ megasas_fw_crash_state_store(struct device *cdev,
+ instance->fw_crash_state = val;
+
+ if ((val == COPIED) || (val == COPY_ERROR)) {
+- spin_lock_irqsave(&instance->crashdump_lock, flags);
++ mutex_lock(&instance->crashdump_lock);
+ megasas_free_host_crash_buffer(instance);
+- spin_unlock_irqrestore(&instance->crashdump_lock, flags);
++ mutex_unlock(&instance->crashdump_lock);
+ if (val == COPY_ERROR)
+ dev_info(&instance->pdev->dev, "application failed to "
+ "copy Firmware crash dump\n");
+@@ -6463,7 +6460,7 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance)
+ init_waitqueue_head(&instance->int_cmd_wait_q);
+ init_waitqueue_head(&instance->abort_cmd_wait_q);
+
+- spin_lock_init(&instance->crashdump_lock);
++ mutex_init(&instance->crashdump_lock);
+ spin_lock_init(&instance->mfi_pool_lock);
+ spin_lock_init(&instance->hba_lock);
+ spin_lock_init(&instance->stream_lock);
+--
+2.40.1
+
--- /dev/null
+From 55ce88cf3cd84bb969f14dda7cd9030faffd71b6 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 7 May 2019 10:05:36 -0700
+Subject: scsi: megaraid_sas: Load balance completions across all MSI-X
+
+From: Shivasharan S <shivasharan.srikanteshwara@broadcom.com>
+
+[ Upstream commit 1d15d9098ad12b0021ac5a6b851f26d1ab021e5a ]
+
+Driver will use "reply descriptor post queues" in round robin fashion when
+the combined MSI-X mode is not enabled. With this IO completions are
+distributed and load balanced across all the available reply descriptor
+post queues equally.
+
+This is enabled only if combined MSI-X mode is not enabled in firmware.
+This improves performance and also fixes soft lockups.
+
+When load balancing is enabled, IRQ affinity from driver needs to be
+disabled.
+
+Signed-off-by: Kashyap Desai <kashyap.desai@broadcom.com>
+Signed-off-by: Shivasharan S <shivasharan.srikanteshwara@broadcom.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: 0b0747d507bf ("scsi: megaraid_sas: Fix deadlock on firmware crashdump")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/megaraid/megaraid_sas.h | 3 +++
+ drivers/scsi/megaraid/megaraid_sas_base.c | 22 +++++++++++++++++----
+ drivers/scsi/megaraid/megaraid_sas_fusion.c | 18 +++++++++++++----
+ 3 files changed, 35 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h
+index 67d356d847176..80ae48b65b608 100644
+--- a/drivers/scsi/megaraid/megaraid_sas.h
++++ b/drivers/scsi/megaraid/megaraid_sas.h
+@@ -2193,6 +2193,7 @@ struct megasas_instance {
+ u32 secure_jbod_support;
+ u32 support_morethan256jbod; /* FW support for more than 256 PD/JBOD */
+ bool use_seqnum_jbod_fp; /* Added for PD sequence */
++ bool smp_affinity_enable;
+ spinlock_t crashdump_lock;
+
+ struct megasas_register_set __iomem *reg_set;
+@@ -2210,6 +2211,7 @@ struct megasas_instance {
+ u16 ldio_threshold;
+ u16 cur_can_queue;
+ u32 max_sectors_per_req;
++ bool msix_load_balance;
+ struct megasas_aen_event *ev;
+
+ struct megasas_cmd **cmd_list;
+@@ -2246,6 +2248,7 @@ struct megasas_instance {
+ atomic_t sge_holes_type1;
+ atomic_t sge_holes_type2;
+ atomic_t sge_holes_type3;
++ atomic64_t total_io_count;
+
+ struct megasas_instance_template *instancet;
+ struct tasklet_struct isr_tasklet;
+diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c
+index 8d1df03386b4f..2b6b6d3deba86 100644
+--- a/drivers/scsi/megaraid/megaraid_sas_base.c
++++ b/drivers/scsi/megaraid/megaraid_sas_base.c
+@@ -5101,6 +5101,7 @@ megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe)
+ &instance->irq_context[j]);
+ /* Retry irq register for IO_APIC*/
+ instance->msix_vectors = 0;
++ instance->msix_load_balance = false;
+ if (is_probe) {
+ pci_free_irq_vectors(instance->pdev);
+ return megasas_setup_irqs_ioapic(instance);
+@@ -5109,6 +5110,7 @@ megasas_setup_irqs_msix(struct megasas_instance *instance, u8 is_probe)
+ }
+ }
+ }
++
+ return 0;
+ }
+
+@@ -5364,6 +5366,12 @@ static int megasas_init_fw(struct megasas_instance *instance)
+ if (rdpq_enable)
+ instance->is_rdpq = (scratch_pad_2 & MR_RDPQ_MODE_OFFSET) ?
+ 1 : 0;
++
++ if (!instance->msix_combined) {
++ instance->msix_load_balance = true;
++ instance->smp_affinity_enable = false;
++ }
++
+ fw_msix_count = instance->msix_vectors;
+ /* Save 1-15 reply post index address to local memory
+ * Index 0 is already saved from reg offset
+@@ -5382,17 +5390,20 @@ static int megasas_init_fw(struct megasas_instance *instance)
+ instance->msix_vectors);
+ } else /* MFI adapters */
+ instance->msix_vectors = 1;
++
+ /* Don't bother allocating more MSI-X vectors than cpus */
+ instance->msix_vectors = min(instance->msix_vectors,
+ (unsigned int)num_online_cpus());
+- if (smp_affinity_enable)
++ if (instance->smp_affinity_enable)
+ irq_flags |= PCI_IRQ_AFFINITY;
+ i = pci_alloc_irq_vectors(instance->pdev, 1,
+ instance->msix_vectors, irq_flags);
+- if (i > 0)
++ if (i > 0) {
+ instance->msix_vectors = i;
+- else
++ } else {
+ instance->msix_vectors = 0;
++ instance->msix_load_balance = false;
++ }
+ }
+ /*
+ * MSI-X host index 0 is common for all adapter.
+@@ -6447,6 +6458,7 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance)
+ INIT_LIST_HEAD(&instance->internal_reset_pending_q);
+
+ atomic_set(&instance->fw_outstanding, 0);
++ atomic64_set(&instance->total_io_count, 0);
+
+ init_waitqueue_head(&instance->int_cmd_wait_q);
+ init_waitqueue_head(&instance->abort_cmd_wait_q);
+@@ -6469,6 +6481,8 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance)
+ instance->last_time = 0;
+ instance->disableOnlineCtrlReset = 1;
+ instance->UnevenSpanSupport = 0;
++ instance->smp_affinity_enable = smp_affinity_enable ? true : false;
++ instance->msix_load_balance = false;
+
+ if (instance->adapter_type != MFI_SERIES) {
+ INIT_WORK(&instance->work_init, megasas_fusion_ocr_wq);
+@@ -6818,7 +6832,7 @@ megasas_resume(struct pci_dev *pdev)
+ /* Now re-enable MSI-X */
+ if (instance->msix_vectors) {
+ irq_flags = PCI_IRQ_MSIX;
+- if (smp_affinity_enable)
++ if (instance->smp_affinity_enable)
+ irq_flags |= PCI_IRQ_AFFINITY;
+ }
+ rval = pci_alloc_irq_vectors(instance->pdev, 1,
+diff --git a/drivers/scsi/megaraid/megaraid_sas_fusion.c b/drivers/scsi/megaraid/megaraid_sas_fusion.c
+index b400167f9ad42..294e1a3a6adfa 100644
+--- a/drivers/scsi/megaraid/megaraid_sas_fusion.c
++++ b/drivers/scsi/megaraid/megaraid_sas_fusion.c
+@@ -2641,8 +2641,13 @@ megasas_build_ldio_fusion(struct megasas_instance *instance,
+ fp_possible = (io_info.fpOkForIo > 0) ? true : false;
+ }
+
+- cmd->request_desc->SCSIIO.MSIxIndex =
+- instance->reply_map[raw_smp_processor_id()];
++ if (instance->msix_load_balance)
++ cmd->request_desc->SCSIIO.MSIxIndex =
++ (mega_mod64(atomic64_add_return(1, &instance->total_io_count),
++ instance->msix_vectors));
++ else
++ cmd->request_desc->SCSIIO.MSIxIndex =
++ instance->reply_map[raw_smp_processor_id()];
+
+ praid_context = &io_request->RaidContext;
+
+@@ -2969,8 +2974,13 @@ megasas_build_syspd_fusion(struct megasas_instance *instance,
+
+ cmd->request_desc->SCSIIO.DevHandle = io_request->DevHandle;
+
+- cmd->request_desc->SCSIIO.MSIxIndex =
+- instance->reply_map[raw_smp_processor_id()];
++ if (instance->msix_load_balance)
++ cmd->request_desc->SCSIIO.MSIxIndex =
++ (mega_mod64(atomic64_add_return(1, &instance->total_io_count),
++ instance->msix_vectors));
++ else
++ cmd->request_desc->SCSIIO.MSIxIndex =
++ instance->reply_map[raw_smp_processor_id()];
+
+ if (!fp_possible) {
+ /* system pd firmware path */
+--
+2.40.1
+
--- /dev/null
+From e3c515b3a49c366558f2825541335093c9aeb991 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Dec 2018 09:33:44 -0800
+Subject: scsi: qla2xxx: Add protection mask module parameters
+
+From: Martin K. Petersen <martin.petersen@oracle.com>
+
+[ Upstream commit 7855d2ba1172d716d96a628af7c5bafa5725ac57 ]
+
+Allow user to selectively enable/disable DIF/DIX protection
+capabilities mask.
+
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Himanshu Madhani <hmadhani@marvell.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Stable-dep-of: e9105c4b7a92 ("scsi: qla2xxx: Remove unsupported ql2xenabledif option")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/qla2xxx/qla_os.c | 36 +++++++++++++++++++++++++++--------
+ 1 file changed, 28 insertions(+), 8 deletions(-)
+
+diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
+index 4580774b2c3e7..27514d0abe845 100644
+--- a/drivers/scsi/qla2xxx/qla_os.c
++++ b/drivers/scsi/qla2xxx/qla_os.c
+@@ -277,6 +277,20 @@ MODULE_PARM_DESC(qla2xuseresexchforels,
+ "Reserve 1/2 of emergency exchanges for ELS.\n"
+ " 0 (default): disabled");
+
++int ql2xprotmask;
++module_param(ql2xprotmask, int, 0644);
++MODULE_PARM_DESC(ql2xprotmask,
++ "Override DIF/DIX protection capabilities mask\n"
++ "Default is 0 which sets protection mask based on "
++ "capabilities reported by HBA firmware.\n");
++
++int ql2xprotguard;
++module_param(ql2xprotguard, int, 0644);
++MODULE_PARM_DESC(ql2xprotguard, "Override choice of DIX checksum\n"
++ " 0 -- Let HBA firmware decide\n"
++ " 1 -- Force T10 CRC\n"
++ " 2 -- Force IP checksum\n");
++
+ /*
+ * SCSI host template entry points
+ */
+@@ -3293,13 +3307,16 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+ "Registering for DIF/DIX type 1 and 3 protection.\n");
+ if (ql2xenabledif == 1)
+ prot = SHOST_DIX_TYPE0_PROTECTION;
+- scsi_host_set_prot(host,
+- prot | SHOST_DIF_TYPE1_PROTECTION
+- | SHOST_DIF_TYPE2_PROTECTION
+- | SHOST_DIF_TYPE3_PROTECTION
+- | SHOST_DIX_TYPE1_PROTECTION
+- | SHOST_DIX_TYPE2_PROTECTION
+- | SHOST_DIX_TYPE3_PROTECTION);
++ if (ql2xprotmask)
++ scsi_host_set_prot(host, ql2xprotmask);
++ else
++ scsi_host_set_prot(host,
++ prot | SHOST_DIF_TYPE1_PROTECTION
++ | SHOST_DIF_TYPE2_PROTECTION
++ | SHOST_DIF_TYPE3_PROTECTION
++ | SHOST_DIX_TYPE1_PROTECTION
++ | SHOST_DIX_TYPE2_PROTECTION
++ | SHOST_DIX_TYPE3_PROTECTION);
+
+ guard = SHOST_DIX_GUARD_CRC;
+
+@@ -3307,7 +3324,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+ (ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha)))
+ guard |= SHOST_DIX_GUARD_IP;
+
+- scsi_host_set_guard(host, guard);
++ if (ql2xprotguard)
++ scsi_host_set_guard(host, ql2xprotguard);
++ else
++ scsi_host_set_guard(host, guard);
+ } else
+ base_vha->flags.difdix_supported = 0;
+ }
+--
+2.40.1
+
--- /dev/null
+From a845e49035f5cbcc27f60c2c9d081049a0a90bfd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 21 Aug 2023 18:30:42 +0530
+Subject: scsi: qla2xxx: Remove unsupported ql2xenabledif option
+
+From: Manish Rangankar <mrangankar@marvell.com>
+
+[ Upstream commit e9105c4b7a9208a21a9bda133707624f12ddabc2 ]
+
+User accidently passed module parameter ql2xenabledif=1 which is
+unsupported. However, driver still initialized which lead to guard tag
+errors during device discovery.
+
+Remove unsupported ql2xenabledif=1 option and validate the user input.
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Manish Rangankar <mrangankar@marvell.com>
+Signed-off-by: Nilesh Javali <njavali@marvell.com>
+Link: https://lore.kernel.org/r/20230821130045.34850-7-njavali@marvell.com
+Reviewed-by: Himanshu Madhani <himanshu.madhani@oracle.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/qla2xxx/qla_attr.c | 2 --
+ drivers/scsi/qla2xxx/qla_dbg.c | 2 +-
+ drivers/scsi/qla2xxx/qla_os.c | 9 +++++++--
+ 3 files changed, 8 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
+index 6c9095d0aa0f4..9a25e92ef1abe 100644
+--- a/drivers/scsi/qla2xxx/qla_attr.c
++++ b/drivers/scsi/qla2xxx/qla_attr.c
+@@ -2088,8 +2088,6 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
+ vha->flags.difdix_supported = 1;
+ ql_dbg(ql_dbg_user, vha, 0x7082,
+ "Registered for DIF/DIX type 1 and 3 protection.\n");
+- if (ql2xenabledif == 1)
+- prot = SHOST_DIX_TYPE0_PROTECTION;
+ scsi_host_set_prot(vha->host,
+ prot | SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
+index 36871760a5d37..fcbadd41856c2 100644
+--- a/drivers/scsi/qla2xxx/qla_dbg.c
++++ b/drivers/scsi/qla2xxx/qla_dbg.c
+@@ -22,7 +22,7 @@
+ * | Queue Command and IO tracing | 0x3074 | 0x300b |
+ * | | | 0x3027-0x3028 |
+ * | | | 0x303d-0x3041 |
+- * | | | 0x302d,0x3033 |
++ * | | | 0x302e,0x3033 |
+ * | | | 0x3036,0x3038 |
+ * | | | 0x303a |
+ * | DPC Thread | 0x4023 | 0x4002,0x4013 |
+diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
+index 27514d0abe845..36dca08166f29 100644
+--- a/drivers/scsi/qla2xxx/qla_os.c
++++ b/drivers/scsi/qla2xxx/qla_os.c
+@@ -3069,6 +3069,13 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+ host->max_id = ha->max_fibre_devices;
+ host->cmd_per_lun = 3;
+ host->unique_id = host->host_no;
++
++ if (ql2xenabledif && ql2xenabledif != 2) {
++ ql_log(ql_log_warn, base_vha, 0x302d,
++ "Invalid value for ql2xenabledif, resetting it to default (2)\n");
++ ql2xenabledif = 2;
++ }
++
+ if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif)
+ host->max_cmd_len = 32;
+ else
+@@ -3305,8 +3312,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+ base_vha->flags.difdix_supported = 1;
+ ql_dbg(ql_dbg_init, base_vha, 0x00f1,
+ "Registering for DIF/DIX type 1 and 3 protection.\n");
+- if (ql2xenabledif == 1)
+- prot = SHOST_DIX_TYPE0_PROTECTION;
+ if (ql2xprotmask)
+ scsi_host_set_prot(host, ql2xprotmask);
+ else
+--
+2.40.1
+
team-fix-null-ptr-deref-when-team-device-type-is-cha.patch
gpio-tb10x-fix-an-error-handling-path-in-tb10x_gpio_.patch
i2c-mux-demux-pinctrl-check-the-return-value-of-devm.patch
+input-i8042-add-quirk-for-tuxedo-gemini-17-gen1-clev.patch
+scsi-qla2xxx-add-protection-mask-module-parameters.patch
+scsi-qla2xxx-remove-unsupported-ql2xenabledif-option.patch
+usb-typec-group-all-tcpci-tcpm-code-together.patch
+usb-typec-tcpm-refactor-tcpm_handle_vdm_request-payl.patch
+usb-typec-tcpm-refactor-tcpm_handle_vdm_request.patch
+usb-typec-bus-verify-partner-exists-in-typec_altmode.patch
+scsi-megaraid_sas-load-balance-completions-across-al.patch
+scsi-megaraid_sas-fix-deadlock-on-firmware-crashdump.patch
+ext4-remove-the-group-parameter-of-ext4_trim_extent.patch
+ext4-add-new-helper-interface-ext4_try_to_trim_range.patch
+ext4-scope-ret-locally-in-ext4_try_to_trim_range.patch
+ext4-change-s_last_trim_minblks-type-to-unsigned-lon.patch
+ext4-mark-group-as-trimmed-only-if-it-was-fully-scan.patch
+ext4-replace-the-traditional-ternary-conditional-ope.patch
+ext4-move-setting-of-trimmed-bit-into-ext4_try_to_tr.patch
+ext4-do-not-let-fstrim-block-system-suspend.patch
--- /dev/null
+From a73fdd2d1d0ccdd40a7832fc85a8a7d6239b688e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 14 Aug 2023 18:05:59 +0000
+Subject: usb: typec: bus: verify partner exists in typec_altmode_attention
+
+From: RD Babiera <rdbabiera@google.com>
+
+[ Upstream commit f23643306430f86e2f413ee2b986e0773e79da31 ]
+
+Some usb hubs will negotiate DisplayPort Alt mode with the device
+but will then negotiate a data role swap after entering the alt
+mode. The data role swap causes the device to unregister all alt
+modes, however the usb hub will still send Attention messages
+even after failing to reregister the Alt Mode. type_altmode_attention
+currently does not verify whether or not a device's altmode partner
+exists, which results in a NULL pointer error when dereferencing
+the typec_altmode and typec_altmode_ops belonging to the altmode
+partner.
+
+Verify the presence of a device's altmode partner before sending
+the Attention message to the Alt Mode driver.
+
+Fixes: 8a37d87d72f0 ("usb: typec: Bus type for alternate modes")
+Cc: stable@vger.kernel.org
+Signed-off-by: RD Babiera <rdbabiera@google.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Link: https://lore.kernel.org/r/20230814180559.923475-1-rdbabiera@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/typec/bus.c | 12 ++++++++++--
+ drivers/usb/typec/tcpm/tcpm.c | 3 ++-
+ include/linux/usb/typec_altmode.h | 2 +-
+ 3 files changed, 13 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c
+index 7605963f71ede..31188354bca04 100644
+--- a/drivers/usb/typec/bus.c
++++ b/drivers/usb/typec/bus.c
+@@ -146,12 +146,20 @@ EXPORT_SYMBOL_GPL(typec_altmode_exit);
+ *
+ * Notifies the partner of @adev about Attention command.
+ */
+-void typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
++int typec_altmode_attention(struct typec_altmode *adev, u32 vdo)
+ {
+- struct typec_altmode *pdev = &to_altmode(adev)->partner->adev;
++ struct altmode *partner = to_altmode(adev)->partner;
++ struct typec_altmode *pdev;
++
++ if (!partner)
++ return -ENODEV;
++
++ pdev = &partner->adev;
+
+ if (pdev->ops && pdev->ops->attention)
+ pdev->ops->attention(pdev, vdo);
++
++ return 0;
+ }
+ EXPORT_SYMBOL_GPL(typec_altmode_attention);
+
+diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
+index 9cb781e6a0b53..c5132f2942f71 100644
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -1258,7 +1258,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
+ }
+ break;
+ case ADEV_ATTENTION:
+- typec_altmode_attention(adev, p[1]);
++ if (typec_altmode_attention(adev, p[1]))
++ tcpm_log(port, "typec_altmode_attention no port partner altmode");
+ break;
+ }
+ }
+diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h
+index 9a88c74a1d0d0..969b7c5040875 100644
+--- a/include/linux/usb/typec_altmode.h
++++ b/include/linux/usb/typec_altmode.h
+@@ -67,7 +67,7 @@ struct typec_altmode_ops {
+
+ int typec_altmode_enter(struct typec_altmode *altmode);
+ int typec_altmode_exit(struct typec_altmode *altmode);
+-void typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
++int typec_altmode_attention(struct typec_altmode *altmode, u32 vdo);
+ int typec_altmode_vdm(struct typec_altmode *altmode,
+ const u32 header, const u32 *vdo, int count);
+ int typec_altmode_notify(struct typec_altmode *altmode, unsigned long conf,
+--
+2.40.1
+
--- /dev/null
+From f2618e9c223c548dead24007d7ee7ab21531cab4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Sep 2018 14:23:47 +0300
+Subject: usb: typec: Group all TCPCI/TCPM code together
+
+From: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+
+[ Upstream commit ae8a2ca8a2215c7e31e6d874f7303801bb15fbbc ]
+
+Moving all the drivers that depend on the Port Controller
+Manager under a new directory drivers/usb/typec/tcpm/ and
+making Guenter Roeck the designated reviewer of that code.
+
+Acked-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: f23643306430 ("usb: typec: bus: verify partner exists in typec_altmode_attention")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ MAINTAINERS | 6 +++
+ drivers/usb/typec/Kconfig | 45 +---------------
+ drivers/usb/typec/Makefile | 6 +--
+ drivers/usb/typec/fusb302/Kconfig | 7 ---
+ drivers/usb/typec/fusb302/Makefile | 2 -
+ drivers/usb/typec/tcpm/Kconfig | 52 +++++++++++++++++++
+ drivers/usb/typec/tcpm/Makefile | 7 +++
+ drivers/usb/typec/{fusb302 => tcpm}/fusb302.c | 0
+ .../usb/typec/{fusb302 => tcpm}/fusb302_reg.h | 0
+ drivers/usb/typec/{ => tcpm}/tcpci.c | 0
+ drivers/usb/typec/{ => tcpm}/tcpci.h | 0
+ drivers/usb/typec/{ => tcpm}/tcpci_rt1711h.c | 0
+ drivers/usb/typec/{ => tcpm}/tcpm.c | 0
+ .../usb/typec/{typec_wcove.c => tcpm/wcove.c} | 0
+ 14 files changed, 67 insertions(+), 58 deletions(-)
+ delete mode 100644 drivers/usb/typec/fusb302/Kconfig
+ delete mode 100644 drivers/usb/typec/fusb302/Makefile
+ create mode 100644 drivers/usb/typec/tcpm/Kconfig
+ create mode 100644 drivers/usb/typec/tcpm/Makefile
+ rename drivers/usb/typec/{fusb302 => tcpm}/fusb302.c (100%)
+ rename drivers/usb/typec/{fusb302 => tcpm}/fusb302_reg.h (100%)
+ rename drivers/usb/typec/{ => tcpm}/tcpci.c (100%)
+ rename drivers/usb/typec/{ => tcpm}/tcpci.h (100%)
+ rename drivers/usb/typec/{ => tcpm}/tcpci_rt1711h.c (100%)
+ rename drivers/usb/typec/{ => tcpm}/tcpm.c (100%)
+ rename drivers/usb/typec/{typec_wcove.c => tcpm/wcove.c} (100%)
+
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 59003315a9597..bf33725ca5157 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -15307,6 +15307,12 @@ F: Documentation/driver-api/usb/typec_bus.rst
+ F: drivers/usb/typec/altmodes/
+ F: include/linux/usb/typec_altmode.h
+
++USB TYPEC PORT CONTROLLER DRIVERS
++M: Guenter Roeck <linux@roeck-us.net>
++L: linux-usb@vger.kernel.org
++S: Maintained
++F: drivers/usb/typec/tcpm/
++
+ USB UHCI DRIVER
+ M: Alan Stern <stern@rowland.harvard.edu>
+ L: linux-usb@vger.kernel.org
+diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig
+index 8445890accdfe..e078f23e3f8d5 100644
+--- a/drivers/usb/typec/Kconfig
++++ b/drivers/usb/typec/Kconfig
+@@ -45,50 +45,7 @@ menuconfig TYPEC
+
+ if TYPEC
+
+-config TYPEC_TCPM
+- tristate "USB Type-C Port Controller Manager"
+- depends on USB
+- select USB_ROLE_SWITCH
+- select POWER_SUPPLY
+- help
+- The Type-C Port Controller Manager provides a USB PD and USB Type-C
+- state machine for use with Type-C Port Controllers.
+-
+-if TYPEC_TCPM
+-
+-config TYPEC_TCPCI
+- tristate "Type-C Port Controller Interface driver"
+- depends on I2C
+- select REGMAP_I2C
+- help
+- Type-C Port Controller driver for TCPCI-compliant controller.
+-
+-config TYPEC_RT1711H
+- tristate "Richtek RT1711H Type-C chip driver"
+- depends on I2C
+- select TYPEC_TCPCI
+- help
+- Richtek RT1711H Type-C chip driver that works with
+- Type-C Port Controller Manager to provide USB PD and USB
+- Type-C functionalities.
+-
+-source "drivers/usb/typec/fusb302/Kconfig"
+-
+-config TYPEC_WCOVE
+- tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
+- depends on ACPI
+- depends on INTEL_SOC_PMIC
+- depends on INTEL_PMC_IPC
+- depends on BXT_WC_PMIC_OPREGION
+- help
+- This driver adds support for USB Type-C detection on Intel Broxton
+- platforms that have Intel Whiskey Cove PMIC. The driver can detect the
+- role and cable orientation.
+-
+- To compile this driver as module, choose M here: the module will be
+- called typec_wcove
+-
+-endif # TYPEC_TCPM
++source "drivers/usb/typec/tcpm/Kconfig"
+
+ source "drivers/usb/typec/ucsi/Kconfig"
+
+diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile
+index 45b0aef428a82..6696b7263d61a 100644
+--- a/drivers/usb/typec/Makefile
++++ b/drivers/usb/typec/Makefile
+@@ -2,11 +2,7 @@
+ obj-$(CONFIG_TYPEC) += typec.o
+ typec-y := class.o mux.o bus.o
+ obj-$(CONFIG_TYPEC) += altmodes/
+-obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
+-obj-y += fusb302/
+-obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
++obj-$(CONFIG_TYPEC_TCPM) += tcpm/
+ obj-$(CONFIG_TYPEC_UCSI) += ucsi/
+ obj-$(CONFIG_TYPEC_TPS6598X) += tps6598x.o
+ obj-$(CONFIG_TYPEC) += mux/
+-obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
+-obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
+diff --git a/drivers/usb/typec/fusb302/Kconfig b/drivers/usb/typec/fusb302/Kconfig
+deleted file mode 100644
+index fce099ff39fea..0000000000000
+--- a/drivers/usb/typec/fusb302/Kconfig
++++ /dev/null
+@@ -1,7 +0,0 @@
+-config TYPEC_FUSB302
+- tristate "Fairchild FUSB302 Type-C chip driver"
+- depends on I2C
+- help
+- The Fairchild FUSB302 Type-C chip driver that works with
+- Type-C Port Controller Manager to provide USB PD and USB
+- Type-C functionalities.
+diff --git a/drivers/usb/typec/fusb302/Makefile b/drivers/usb/typec/fusb302/Makefile
+deleted file mode 100644
+index 3b51b33631a08..0000000000000
+--- a/drivers/usb/typec/fusb302/Makefile
++++ /dev/null
+@@ -1,2 +0,0 @@
+-# SPDX-License-Identifier: GPL-2.0
+-obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
+diff --git a/drivers/usb/typec/tcpm/Kconfig b/drivers/usb/typec/tcpm/Kconfig
+new file mode 100644
+index 0000000000000..f03ea8a617686
+--- /dev/null
++++ b/drivers/usb/typec/tcpm/Kconfig
+@@ -0,0 +1,52 @@
++config TYPEC_TCPM
++ tristate "USB Type-C Port Controller Manager"
++ depends on USB
++ select USB_ROLE_SWITCH
++ select POWER_SUPPLY
++ help
++ The Type-C Port Controller Manager provides a USB PD and USB Type-C
++ state machine for use with Type-C Port Controllers.
++
++if TYPEC_TCPM
++
++config TYPEC_TCPCI
++ tristate "Type-C Port Controller Interface driver"
++ depends on I2C
++ select REGMAP_I2C
++ help
++ Type-C Port Controller driver for TCPCI-compliant controller.
++
++if TYPEC_TCPCI
++
++config TYPEC_RT1711H
++ tristate "Richtek RT1711H Type-C chip driver"
++ help
++ Richtek RT1711H Type-C chip driver that works with
++ Type-C Port Controller Manager to provide USB PD and USB
++ Type-C functionalities.
++
++endif # TYPEC_TCPCI
++
++config TYPEC_FUSB302
++ tristate "Fairchild FUSB302 Type-C chip driver"
++ depends on I2C
++ help
++ The Fairchild FUSB302 Type-C chip driver that works with
++ Type-C Port Controller Manager to provide USB PD and USB
++ Type-C functionalities.
++
++config TYPEC_WCOVE
++ tristate "Intel WhiskeyCove PMIC USB Type-C PHY driver"
++ depends on ACPI
++ depends on INTEL_SOC_PMIC
++ depends on INTEL_PMC_IPC
++ depends on BXT_WC_PMIC_OPREGION
++ help
++ This driver adds support for USB Type-C on Intel Broxton platforms
++ that have Intel Whiskey Cove PMIC. The driver works with USB Type-C
++ Port Controller Manager to provide USB PD and Type-C functionalities.
++
++ To compile this driver as module, choose M here: the module will be
++ called typec_wcove.ko
++
++endif # TYPEC_TCPM
+diff --git a/drivers/usb/typec/tcpm/Makefile b/drivers/usb/typec/tcpm/Makefile
+new file mode 100644
+index 0000000000000..a5ff6c8eb8922
+--- /dev/null
++++ b/drivers/usb/typec/tcpm/Makefile
+@@ -0,0 +1,7 @@
++# SPDX-License-Identifier: GPL-2.0
++obj-$(CONFIG_TYPEC_TCPM) += tcpm.o
++obj-$(CONFIG_TYPEC_FUSB302) += fusb302.o
++obj-$(CONFIG_TYPEC_WCOVE) += typec_wcove.o
++typec_wcove-y := wcove.o
++obj-$(CONFIG_TYPEC_TCPCI) += tcpci.o
++obj-$(CONFIG_TYPEC_RT1711H) += tcpci_rt1711h.o
+diff --git a/drivers/usb/typec/fusb302/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c
+similarity index 100%
+rename from drivers/usb/typec/fusb302/fusb302.c
+rename to drivers/usb/typec/tcpm/fusb302.c
+diff --git a/drivers/usb/typec/fusb302/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h
+similarity index 100%
+rename from drivers/usb/typec/fusb302/fusb302_reg.h
+rename to drivers/usb/typec/tcpm/fusb302_reg.h
+diff --git a/drivers/usb/typec/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
+similarity index 100%
+rename from drivers/usb/typec/tcpci.c
+rename to drivers/usb/typec/tcpm/tcpci.c
+diff --git a/drivers/usb/typec/tcpci.h b/drivers/usb/typec/tcpm/tcpci.h
+similarity index 100%
+rename from drivers/usb/typec/tcpci.h
+rename to drivers/usb/typec/tcpm/tcpci.h
+diff --git a/drivers/usb/typec/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c
+similarity index 100%
+rename from drivers/usb/typec/tcpci_rt1711h.c
+rename to drivers/usb/typec/tcpm/tcpci_rt1711h.c
+diff --git a/drivers/usb/typec/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
+similarity index 100%
+rename from drivers/usb/typec/tcpm.c
+rename to drivers/usb/typec/tcpm/tcpm.c
+diff --git a/drivers/usb/typec/typec_wcove.c b/drivers/usb/typec/tcpm/wcove.c
+similarity index 100%
+rename from drivers/usb/typec/typec_wcove.c
+rename to drivers/usb/typec/tcpm/wcove.c
+--
+2.40.1
+
--- /dev/null
+From 85a3921f0d91a275cdd0ede4cf05bf66ce215388 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2020 19:46:59 +0200
+Subject: usb: typec: tcpm: Refactor tcpm_handle_vdm_request payload handling
+
+From: Hans de Goede <hdegoede@redhat.com>
+
+[ Upstream commit 8afe9a3548f9d1805dcea6d97978f2179c8403a3 ]
+
+Refactor the tcpm_handle_vdm_request payload handling by doing the
+endianness conversion only once directly inside tcpm_handle_vdm_request
+itself instead of doing it multiple times inside various helper functions
+called by tcpm_handle_vdm_request.
+
+This is a preparation patch for some further refactoring to fix an AB BA
+lock inversion between the tcpm code and some altmode drivers.
+
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://lore.kernel.org/r/20200724174702.61754-3-hdegoede@redhat.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: f23643306430 ("usb: typec: bus: verify partner exists in typec_altmode_attention")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/typec/tcpm/tcpm.c | 49 ++++++++++++++++-------------------
+ 1 file changed, 22 insertions(+), 27 deletions(-)
+
+diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
+index 0fdae44c9b8cf..7487efbd8c2d3 100644
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -957,16 +957,15 @@ static void tcpm_queue_vdm(struct tcpm_port *port, const u32 header,
+ port->vdm_state = VDM_STATE_READY;
+ }
+
+-static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload,
+- int cnt)
++static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt)
+ {
+- u32 vdo = le32_to_cpu(payload[VDO_INDEX_IDH]);
+- u32 product = le32_to_cpu(payload[VDO_INDEX_PRODUCT]);
++ u32 vdo = p[VDO_INDEX_IDH];
++ u32 product = p[VDO_INDEX_PRODUCT];
+
+ memset(&port->mode_data, 0, sizeof(port->mode_data));
+
+ port->partner_ident.id_header = vdo;
+- port->partner_ident.cert_stat = le32_to_cpu(payload[VDO_INDEX_CSTAT]);
++ port->partner_ident.cert_stat = p[VDO_INDEX_CSTAT];
+ port->partner_ident.product = product;
+
+ typec_partner_set_identity(port->partner);
+@@ -976,17 +975,15 @@ static void svdm_consume_identity(struct tcpm_port *port, const __le32 *payload,
+ PD_PRODUCT_PID(product), product & 0xffff);
+ }
+
+-static bool svdm_consume_svids(struct tcpm_port *port, const __le32 *payload,
+- int cnt)
++static bool svdm_consume_svids(struct tcpm_port *port, const u32 *p, int cnt)
+ {
+ struct pd_mode_data *pmdata = &port->mode_data;
+ int i;
+
+ for (i = 1; i < cnt; i++) {
+- u32 p = le32_to_cpu(payload[i]);
+ u16 svid;
+
+- svid = (p >> 16) & 0xffff;
++ svid = (p[i] >> 16) & 0xffff;
+ if (!svid)
+ return false;
+
+@@ -996,7 +993,7 @@ static bool svdm_consume_svids(struct tcpm_port *port, const __le32 *payload,
+ pmdata->svids[pmdata->nsvids++] = svid;
+ tcpm_log(port, "SVID %d: 0x%x", pmdata->nsvids, svid);
+
+- svid = p & 0xffff;
++ svid = p[i] & 0xffff;
+ if (!svid)
+ return false;
+
+@@ -1026,8 +1023,7 @@ static bool svdm_consume_svids(struct tcpm_port *port, const __le32 *payload,
+ return false;
+ }
+
+-static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
+- int cnt)
++static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt)
+ {
+ struct pd_mode_data *pmdata = &port->mode_data;
+ struct typec_altmode_desc *paltmode;
+@@ -1044,7 +1040,7 @@ static void svdm_consume_modes(struct tcpm_port *port, const __le32 *payload,
+
+ paltmode->svid = pmdata->svids[pmdata->svid_index];
+ paltmode->mode = i;
+- paltmode->vdo = le32_to_cpu(payload[i]);
++ paltmode->vdo = p[i];
+
+ tcpm_log(port, " Alternate mode %d: SVID 0x%04x, VDO %d: 0x%08x",
+ pmdata->altmodes, paltmode->svid,
+@@ -1072,21 +1068,17 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
+
+ #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
+
+-static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
++static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
+ u32 *response)
+ {
+ struct typec_altmode *adev;
+ struct typec_altmode *pdev;
+ struct pd_mode_data *modep;
+- u32 p[PD_MAX_PAYLOAD];
+ int rlen = 0;
+ int cmd_type;
+ int cmd;
+ int i;
+
+- for (i = 0; i < cnt; i++)
+- p[i] = le32_to_cpu(payload[i]);
+-
+ cmd_type = PD_VDO_CMDT(p[0]);
+ cmd = PD_VDO_CMD(p[0]);
+
+@@ -1147,13 +1139,13 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
+ switch (cmd) {
+ case CMD_DISCOVER_IDENT:
+ /* 6.4.4.3.1 */
+- svdm_consume_identity(port, payload, cnt);
++ svdm_consume_identity(port, p, cnt);
+ response[0] = VDO(USB_SID_PD, 1, CMD_DISCOVER_SVID);
+ rlen = 1;
+ break;
+ case CMD_DISCOVER_SVID:
+ /* 6.4.4.3.2 */
+- if (svdm_consume_svids(port, payload, cnt)) {
++ if (svdm_consume_svids(port, p, cnt)) {
+ response[0] = VDO(USB_SID_PD, 1,
+ CMD_DISCOVER_SVID);
+ rlen = 1;
+@@ -1165,7 +1157,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
+ break;
+ case CMD_DISCOVER_MODES:
+ /* 6.4.4.3.3 */
+- svdm_consume_modes(port, payload, cnt);
++ svdm_consume_modes(port, p, cnt);
+ modep->svid_index++;
+ if (modep->svid_index < modep->nsvids) {
+ u16 svid = modep->svids[modep->svid_index];
+@@ -1228,15 +1220,18 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const __le32 *payload, int cnt,
+ static void tcpm_handle_vdm_request(struct tcpm_port *port,
+ const __le32 *payload, int cnt)
+ {
+- int rlen = 0;
++ u32 p[PD_MAX_PAYLOAD];
+ u32 response[8] = { };
+- u32 p0 = le32_to_cpu(payload[0]);
++ int i, rlen = 0;
++
++ for (i = 0; i < cnt; i++)
++ p[i] = le32_to_cpu(payload[i]);
+
+ if (port->vdm_state == VDM_STATE_BUSY) {
+ /* If UFP responded busy retry after timeout */
+- if (PD_VDO_CMDT(p0) == CMDT_RSP_BUSY) {
++ if (PD_VDO_CMDT(p[0]) == CMDT_RSP_BUSY) {
+ port->vdm_state = VDM_STATE_WAIT_RSP_BUSY;
+- port->vdo_retry = (p0 & ~VDO_CMDT_MASK) |
++ port->vdo_retry = (p[0] & ~VDO_CMDT_MASK) |
+ CMDT_INIT;
+ mod_delayed_work(port->wq, &port->vdm_state_machine,
+ msecs_to_jiffies(PD_T_VDM_BUSY));
+@@ -1245,8 +1240,8 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
+ port->vdm_state = VDM_STATE_DONE;
+ }
+
+- if (PD_VDO_SVDM(p0))
+- rlen = tcpm_pd_svdm(port, payload, cnt, response);
++ if (PD_VDO_SVDM(p[0]))
++ rlen = tcpm_pd_svdm(port, p, cnt, response);
+
+ if (rlen > 0) {
+ tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);
+--
+2.40.1
+
--- /dev/null
+From f466ff43d249d583e28dbece37f4110f9c1f8239 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 24 Jul 2020 19:47:00 +0200
+Subject: usb: typec: tcpm: Refactor tcpm_handle_vdm_request
+
+From: Hans de Goede <hdegoede@redhat.com>
+
+[ Upstream commit 95b4d51c96a87cd760c2a4f27fb28a59a27b6368 ]
+
+Refactor tcpm_handle_vdm_request and its tcpm_pd_svdm helper function so
+that reporting the results of the vdm to the altmode-driver is separated
+out into a clear separate step inside tcpm_handle_vdm_request, instead
+of being scattered over various places inside the tcpm_pd_svdm helper.
+
+This is a preparation patch for fixing an AB BA lock inversion between the
+tcpm code and some altmode drivers.
+
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Reviewed-by: Guenter Roeck <linux@roeck-us.net>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://lore.kernel.org/r/20200724174702.61754-4-hdegoede@redhat.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: f23643306430 ("usb: typec: bus: verify partner exists in typec_altmode_attention")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/typec/tcpm/tcpm.c | 76 ++++++++++++++++++++++-------------
+ 1 file changed, 48 insertions(+), 28 deletions(-)
+
+diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
+index 7487efbd8c2d3..9cb781e6a0b53 100644
+--- a/drivers/usb/typec/tcpm/tcpm.c
++++ b/drivers/usb/typec/tcpm/tcpm.c
+@@ -158,6 +158,14 @@ enum pd_msg_request {
+ PD_MSG_DATA_SOURCE_CAP,
+ };
+
++enum adev_actions {
++ ADEV_NONE = 0,
++ ADEV_NOTIFY_USB_AND_QUEUE_VDM,
++ ADEV_QUEUE_VDM,
++ ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL,
++ ADEV_ATTENTION,
++};
++
+ /* Events from low level driver */
+
+ #define TCPM_CC_EVENT BIT(0)
+@@ -1068,10 +1076,10 @@ static void tcpm_register_partner_altmodes(struct tcpm_port *port)
+
+ #define supports_modal(port) PD_IDH_MODAL_SUPP((port)->partner_ident.id_header)
+
+-static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
+- u32 *response)
++static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
++ const u32 *p, int cnt, u32 *response,
++ enum adev_actions *adev_action)
+ {
+- struct typec_altmode *adev;
+ struct typec_altmode *pdev;
+ struct pd_mode_data *modep;
+ int rlen = 0;
+@@ -1087,9 +1095,6 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
+
+ modep = &port->mode_data;
+
+- adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
+- PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
+-
+ pdev = typec_match_altmode(port->partner_altmode, ALTMODE_DISCOVERY_MAX,
+ PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
+
+@@ -1115,8 +1120,7 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
+ break;
+ case CMD_ATTENTION:
+ /* Attention command does not have response */
+- if (adev)
+- typec_altmode_attention(adev, p[1]);
++ *adev_action = ADEV_ATTENTION;
+ return 0;
+ default:
+ break;
+@@ -1170,23 +1174,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
+ case CMD_ENTER_MODE:
+ if (adev && pdev) {
+ typec_altmode_update_active(pdev, true);
+-
+- if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
+- response[0] = VDO(adev->svid, 1,
+- CMD_EXIT_MODE);
+- response[0] |= VDO_OPOS(adev->mode);
+- return 1;
+- }
++ *adev_action = ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL;
+ }
+ return 0;
+ case CMD_EXIT_MODE:
+ if (adev && pdev) {
+ typec_altmode_update_active(pdev, false);
+-
+ /* Back to USB Operation */
+- WARN_ON(typec_altmode_notify(adev,
+- TYPEC_STATE_USB,
+- NULL));
++ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;
++ return 0;
+ }
+ break;
+ default:
+@@ -1197,11 +1193,8 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
+ switch (cmd) {
+ case CMD_ENTER_MODE:
+ /* Back to USB Operation */
+- if (adev)
+- WARN_ON(typec_altmode_notify(adev,
+- TYPEC_STATE_USB,
+- NULL));
+- break;
++ *adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;
++ return 0;
+ default:
+ break;
+ }
+@@ -1211,15 +1204,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, const u32 *p, int cnt,
+ }
+
+ /* Informing the alternate mode drivers about everything */
+- if (adev)
+- typec_altmode_vdm(adev, p[0], &p[1], cnt);
+-
++ *adev_action = ADEV_QUEUE_VDM;
+ return rlen;
+ }
+
+ static void tcpm_handle_vdm_request(struct tcpm_port *port,
+ const __le32 *payload, int cnt)
+ {
++ enum adev_actions adev_action = ADEV_NONE;
++ struct typec_altmode *adev;
+ u32 p[PD_MAX_PAYLOAD];
+ u32 response[8] = { };
+ int i, rlen = 0;
+@@ -1227,6 +1220,9 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
+ for (i = 0; i < cnt; i++)
+ p[i] = le32_to_cpu(payload[i]);
+
++ adev = typec_match_altmode(port->port_altmode, ALTMODE_DISCOVERY_MAX,
++ PD_VDO_VID(p[0]), PD_VDO_OPOS(p[0]));
++
+ if (port->vdm_state == VDM_STATE_BUSY) {
+ /* If UFP responded busy retry after timeout */
+ if (PD_VDO_CMDT(p[0]) == CMDT_RSP_BUSY) {
+@@ -1241,7 +1237,31 @@ static void tcpm_handle_vdm_request(struct tcpm_port *port,
+ }
+
+ if (PD_VDO_SVDM(p[0]))
+- rlen = tcpm_pd_svdm(port, p, cnt, response);
++ rlen = tcpm_pd_svdm(port, adev, p, cnt, response, &adev_action);
++
++ if (adev) {
++ switch (adev_action) {
++ case ADEV_NONE:
++ break;
++ case ADEV_NOTIFY_USB_AND_QUEUE_VDM:
++ WARN_ON(typec_altmode_notify(adev, TYPEC_STATE_USB, NULL));
++ typec_altmode_vdm(adev, p[0], &p[1], cnt);
++ break;
++ case ADEV_QUEUE_VDM:
++ typec_altmode_vdm(adev, p[0], &p[1], cnt);
++ break;
++ case ADEV_QUEUE_VDM_SEND_EXIT_MODE_ON_FAIL:
++ if (typec_altmode_vdm(adev, p[0], &p[1], cnt)) {
++ response[0] = VDO(adev->svid, 1, CMD_EXIT_MODE);
++ response[0] |= VDO_OPOS(adev->mode);
++ rlen = 1;
++ }
++ break;
++ case ADEV_ATTENTION:
++ typec_altmode_attention(adev, p[1]);
++ break;
++ }
++ }
+
+ if (rlen > 0) {
+ tcpm_queue_vdm(port, response[0], &response[1], rlen - 1);
+--
+2.40.1
+