--- /dev/null
+From 8e9cea52c07682a98800a53b4e5afa93e8289f70 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 28 Nov 2023 22:35:05 +0100
+Subject: bus: moxtet: Add spi device table
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Sjoerd Simons <sjoerd@collabora.com>
+
+[ Upstream commit aaafe88d5500ba18b33be72458439367ef878788 ]
+
+The moxtet module fails to auto-load on. Add a SPI id table to
+allow it to do so.
+
+Signed-off-by: Sjoerd Simons <sjoerd@collabora.com>
+Cc: <stable@vger.kernel.org>
+Reviewed-by: Marek Behún <kabel@kernel.org>
+Signed-off-by: Gregory CLEMENT <gregory.clement@bootlin.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/bus/moxtet.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/bus/moxtet.c b/drivers/bus/moxtet.c
+index fd87a59837fa..fbf0818933be 100644
+--- a/drivers/bus/moxtet.c
++++ b/drivers/bus/moxtet.c
+@@ -832,6 +832,12 @@ static int moxtet_remove(struct spi_device *spi)
+ return 0;
+ }
+
++static const struct spi_device_id moxtet_spi_ids[] = {
++ { "moxtet" },
++ { },
++};
++MODULE_DEVICE_TABLE(spi, moxtet_spi_ids);
++
+ static const struct of_device_id moxtet_dt_ids[] = {
+ { .compatible = "cznic,moxtet" },
+ {},
+@@ -843,6 +849,7 @@ static struct spi_driver moxtet_spi_driver = {
+ .name = "moxtet",
+ .of_match_table = moxtet_dt_ids,
+ },
++ .id_table = moxtet_spi_ids,
+ .probe = moxtet_probe,
+ .remove = moxtet_remove,
+ };
+--
+2.43.0
+
--- /dev/null
+From febe21946d85586d4f9af1925ed41627a6d099dd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 Dec 2023 11:08:34 +0800
+Subject: crypto: lib/mpi - Fix unexpected pointer access in mpi_ec_init
+
+From: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
+
+[ Upstream commit ba3c5574203034781ac4231acf117da917efcd2a ]
+
+When the mpi_ec_ctx structure is initialized, some fields are not
+cleared, causing a crash when referencing the field when the
+structure was released. Initially, this issue was ignored because
+memory for mpi_ec_ctx is allocated with the __GFP_ZERO flag.
+For example, this error will be triggered when calculating the
+Za value for SM2 separately.
+
+Fixes: d58bb7e55a8a ("lib/mpi: Introduce ec implementation to MPI library")
+Cc: stable@vger.kernel.org # v6.5
+Signed-off-by: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ lib/mpi/ec.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/lib/mpi/ec.c b/lib/mpi/ec.c
+index 40f5908e57a4..e16dca1e23d5 100644
+--- a/lib/mpi/ec.c
++++ b/lib/mpi/ec.c
+@@ -584,6 +584,9 @@ void mpi_ec_init(struct mpi_ec_ctx *ctx, enum gcry_mpi_ec_models model,
+ ctx->a = mpi_copy(a);
+ ctx->b = mpi_copy(b);
+
++ ctx->d = NULL;
++ ctx->t.two_inv_p = NULL;
++
+ ctx->t.p_barrett = use_barrett > 0 ? mpi_barrett_init(ctx->p, 0) : NULL;
+
+ mpi_ec_get_reset(ctx);
+--
+2.43.0
+
--- /dev/null
+From e87be7a3eccf156225b7d942d0d66503cdfb6450 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 8 Sep 2023 10:27:23 +0200
+Subject: dma-buf: add dma_fence_timestamp helper
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Christian König <christian.koenig@amd.com>
+
+[ Upstream commit b83ce9cb4a465b8f9a3fa45561b721a9551f60e3 ]
+
+When a fence signals there is a very small race window where the timestamp
+isn't updated yet. sync_file solves this by busy waiting for the
+timestamp to appear, but on other ocassions didn't handled this
+correctly.
+
+Provide a dma_fence_timestamp() helper function for this and use it in
+all appropriate cases.
+
+Another alternative would be to grab the spinlock when that happens.
+
+v2 by teddy: add a wait parameter to wait for the timestamp to show up, in case
+ the accurate timestamp is needed and/or the timestamp is not based on
+ ktime (e.g. hw timestamp)
+v3 chk: drop the parameter again for unified handling
+
+Signed-off-by: Yunxiang Li <Yunxiang.Li@amd.com>
+Signed-off-by: Christian König <christian.koenig@amd.com>
+Fixes: 1774baa64f93 ("drm/scheduler: Change scheduled fence track v2")
+Reviewed-by: Alex Deucher <alexander.deucher@amd.com>
+CC: stable@vger.kernel.org
+Link: https://patchwork.freedesktop.org/patch/msgid/20230929104725.2358-1-christian.koenig@amd.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/dma-buf/dma-fence-unwrap.c | 176 +++++++++++++++++++++++++
+ drivers/dma-buf/sync_file.c | 9 +-
+ drivers/gpu/drm/scheduler/sched_main.c | 3 +-
+ include/linux/dma-fence.h | 19 +++
+ 4 files changed, 199 insertions(+), 8 deletions(-)
+ create mode 100644 drivers/dma-buf/dma-fence-unwrap.c
+
+diff --git a/drivers/dma-buf/dma-fence-unwrap.c b/drivers/dma-buf/dma-fence-unwrap.c
+new file mode 100644
+index 000000000000..628af51c81af
+--- /dev/null
++++ b/drivers/dma-buf/dma-fence-unwrap.c
+@@ -0,0 +1,176 @@
++// SPDX-License-Identifier: GPL-2.0-only
++/*
++ * dma-fence-util: misc functions for dma_fence objects
++ *
++ * Copyright (C) 2022 Advanced Micro Devices, Inc.
++ * Authors:
++ * Christian König <christian.koenig@amd.com>
++ */
++
++#include <linux/dma-fence.h>
++#include <linux/dma-fence-array.h>
++#include <linux/dma-fence-chain.h>
++#include <linux/dma-fence-unwrap.h>
++#include <linux/slab.h>
++
++/* Internal helper to start new array iteration, don't use directly */
++static struct dma_fence *
++__dma_fence_unwrap_array(struct dma_fence_unwrap *cursor)
++{
++ cursor->array = dma_fence_chain_contained(cursor->chain);
++ cursor->index = 0;
++ return dma_fence_array_first(cursor->array);
++}
++
++/**
++ * dma_fence_unwrap_first - return the first fence from fence containers
++ * @head: the entrypoint into the containers
++ * @cursor: current position inside the containers
++ *
++ * Unwraps potential dma_fence_chain/dma_fence_array containers and return the
++ * first fence.
++ */
++struct dma_fence *dma_fence_unwrap_first(struct dma_fence *head,
++ struct dma_fence_unwrap *cursor)
++{
++ cursor->chain = dma_fence_get(head);
++ return __dma_fence_unwrap_array(cursor);
++}
++EXPORT_SYMBOL_GPL(dma_fence_unwrap_first);
++
++/**
++ * dma_fence_unwrap_next - return the next fence from a fence containers
++ * @cursor: current position inside the containers
++ *
++ * Continue unwrapping the dma_fence_chain/dma_fence_array containers and return
++ * the next fence from them.
++ */
++struct dma_fence *dma_fence_unwrap_next(struct dma_fence_unwrap *cursor)
++{
++ struct dma_fence *tmp;
++
++ ++cursor->index;
++ tmp = dma_fence_array_next(cursor->array, cursor->index);
++ if (tmp)
++ return tmp;
++
++ cursor->chain = dma_fence_chain_walk(cursor->chain);
++ return __dma_fence_unwrap_array(cursor);
++}
++EXPORT_SYMBOL_GPL(dma_fence_unwrap_next);
++
++/* Implementation for the dma_fence_merge() marco, don't use directly */
++struct dma_fence *__dma_fence_unwrap_merge(unsigned int num_fences,
++ struct dma_fence **fences,
++ struct dma_fence_unwrap *iter)
++{
++ struct dma_fence_array *result;
++ struct dma_fence *tmp, **array;
++ ktime_t timestamp;
++ unsigned int i;
++ size_t count;
++
++ count = 0;
++ timestamp = ns_to_ktime(0);
++ for (i = 0; i < num_fences; ++i) {
++ dma_fence_unwrap_for_each(tmp, &iter[i], fences[i]) {
++ if (!dma_fence_is_signaled(tmp)) {
++ ++count;
++ } else {
++ ktime_t t = dma_fence_timestamp(tmp);
++
++ if (ktime_after(t, timestamp))
++ timestamp = t;
++ }
++ }
++ }
++
++ /*
++ * If we couldn't find a pending fence just return a private signaled
++ * fence with the timestamp of the last signaled one.
++ */
++ if (count == 0)
++ return dma_fence_allocate_private_stub(timestamp);
++
++ array = kmalloc_array(count, sizeof(*array), GFP_KERNEL);
++ if (!array)
++ return NULL;
++
++ /*
++ * This trashes the input fence array and uses it as position for the
++ * following merge loop. This works because the dma_fence_merge()
++ * wrapper macro is creating this temporary array on the stack together
++ * with the iterators.
++ */
++ for (i = 0; i < num_fences; ++i)
++ fences[i] = dma_fence_unwrap_first(fences[i], &iter[i]);
++
++ count = 0;
++ do {
++ unsigned int sel;
++
++restart:
++ tmp = NULL;
++ for (i = 0; i < num_fences; ++i) {
++ struct dma_fence *next;
++
++ while (fences[i] && dma_fence_is_signaled(fences[i]))
++ fences[i] = dma_fence_unwrap_next(&iter[i]);
++
++ next = fences[i];
++ if (!next)
++ continue;
++
++ /*
++ * We can't guarantee that inpute fences are ordered by
++ * context, but it is still quite likely when this
++ * function is used multiple times. So attempt to order
++ * the fences by context as we pass over them and merge
++ * fences with the same context.
++ */
++ if (!tmp || tmp->context > next->context) {
++ tmp = next;
++ sel = i;
++
++ } else if (tmp->context < next->context) {
++ continue;
++
++ } else if (dma_fence_is_later(tmp, next)) {
++ fences[i] = dma_fence_unwrap_next(&iter[i]);
++ goto restart;
++ } else {
++ fences[sel] = dma_fence_unwrap_next(&iter[sel]);
++ goto restart;
++ }
++ }
++
++ if (tmp) {
++ array[count++] = dma_fence_get(tmp);
++ fences[sel] = dma_fence_unwrap_next(&iter[sel]);
++ }
++ } while (tmp);
++
++ if (count == 0) {
++ tmp = dma_fence_allocate_private_stub(ktime_get());
++ goto return_tmp;
++ }
++
++ if (count == 1) {
++ tmp = array[0];
++ goto return_tmp;
++ }
++
++ result = dma_fence_array_create(count, array,
++ dma_fence_context_alloc(1),
++ 1, false);
++ if (!result) {
++ tmp = NULL;
++ goto return_tmp;
++ }
++ return &result->base;
++
++return_tmp:
++ kfree(array);
++ return tmp;
++}
++EXPORT_SYMBOL_GPL(__dma_fence_unwrap_merge);
+diff --git a/drivers/dma-buf/sync_file.c b/drivers/dma-buf/sync_file.c
+index 394e6e1e9686..875ae4b3b047 100644
+--- a/drivers/dma-buf/sync_file.c
++++ b/drivers/dma-buf/sync_file.c
+@@ -384,13 +384,10 @@ static int sync_fill_fence_info(struct dma_fence *fence,
+ sizeof(info->driver_name));
+
+ info->status = dma_fence_get_status(fence);
+- while (test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags) &&
+- !test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags))
+- cpu_relax();
+ info->timestamp_ns =
+- test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags) ?
+- ktime_to_ns(fence->timestamp) :
+- ktime_set(0, 0);
++ dma_fence_is_signaled(fence) ?
++ ktime_to_ns(dma_fence_timestamp(fence)) :
++ ktime_set(0, 0);
+
+ return info->status;
+ }
+diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
+index 67382621b429..e827e8a83c4e 100644
+--- a/drivers/gpu/drm/scheduler/sched_main.c
++++ b/drivers/gpu/drm/scheduler/sched_main.c
+@@ -698,8 +698,7 @@ drm_sched_get_cleanup_job(struct drm_gpu_scheduler *sched)
+ typeof(*next), list);
+ if (next)
+ next->s_fence->scheduled.timestamp =
+- job->s_fence->finished.timestamp;
+-
++ dma_fence_timestamp(&job->s_fence->finished);
+ } else {
+ job = NULL;
+ /* queue timeout for next job */
+diff --git a/include/linux/dma-fence.h b/include/linux/dma-fence.h
+index 6ffb4b2c6371..9d276655cc25 100644
+--- a/include/linux/dma-fence.h
++++ b/include/linux/dma-fence.h
+@@ -551,6 +551,25 @@ static inline void dma_fence_set_error(struct dma_fence *fence,
+ fence->error = error;
+ }
+
++/**
++ * dma_fence_timestamp - helper to get the completion timestamp of a fence
++ * @fence: fence to get the timestamp from.
++ *
++ * After a fence is signaled the timestamp is updated with the signaling time,
++ * but setting the timestamp can race with tasks waiting for the signaling. This
++ * helper busy waits for the correct timestamp to appear.
++ */
++static inline ktime_t dma_fence_timestamp(struct dma_fence *fence)
++{
++ if (WARN_ON(!test_bit(DMA_FENCE_FLAG_SIGNALED_BIT, &fence->flags)))
++ return ktime_get();
++
++ while (!test_bit(DMA_FENCE_FLAG_TIMESTAMP_BIT, &fence->flags))
++ cpu_relax();
++
++ return fence->timestamp;
++}
++
+ signed long dma_fence_wait_timeout(struct dma_fence *,
+ bool intr, signed long timeout);
+ signed long dma_fence_wait_any_timeout(struct dma_fence **fences,
+--
+2.43.0
+
--- /dev/null
+From 8a33b0569f9410057e7bab59752b8b3714bda753 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 11 Feb 2022 10:46:39 +0100
+Subject: fbdev/defio: Early-out if page is already enlisted
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 105a940416fc622406653b6fe54732897642dfbc ]
+
+Return early if a page is already in the list of dirty pages for
+deferred I/O. This can be detected if the page's list head is not
+empty. Keep the list head initialized while the page is not enlisted
+to make this work reliably.
+
+v2:
+ * update comment and fix spelling (Sam)
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Acked-by: Sam Ravnborg <sam@ravnborg.org>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220211094640.21632-2-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 26 +++++++++++++++++---------
+ 1 file changed, 17 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 0708e214c5a3..95264a621221 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -59,6 +59,7 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+ printk(KERN_ERR "no mapping available\n");
+
+ BUG_ON(!page->mapping);
++ INIT_LIST_HEAD(&page->lru);
+ page->index = vmf->pgoff;
+
+ vmf->page = page;
+@@ -118,17 +119,24 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+ */
+ lock_page(page);
+
++ /*
++ * This check is to catch the case where a new process could start
++ * writing to the same page through a new PTE. This new access
++ * can cause a call to .page_mkwrite even if the original process'
++ * PTE is marked writable.
++ *
++ * TODO: The lru field is owned by the page cache; hence the name.
++ * We dequeue in fb_deferred_io_work() after flushing the
++ * page's content into video memory. Instead of lru, fbdefio
++ * should have it's own field.
++ */
++ if (!list_empty(&page->lru))
++ goto page_already_added;
++
+ /* we loop through the pagelist before adding in order
+ to keep the pagelist sorted */
+ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+- /* this check is to catch the case where a new
+- process could start writing to the same page
+- through a new pte. this new access can cause the
+- mkwrite even when the original ps's pte is marked
+- writable */
+- if (unlikely(cur == page))
+- goto page_already_added;
+- else if (cur->index > page->index)
++ if (cur->index > page->index)
+ break;
+ }
+
+@@ -190,7 +198,7 @@ static void fb_deferred_io_work(struct work_struct *work)
+
+ /* clear the list */
+ list_for_each_safe(node, next, &fbdefio->pagelist) {
+- list_del(node);
++ list_del_init(node);
+ }
+ mutex_unlock(&fbdefio->lock);
+ }
+--
+2.43.0
+
--- /dev/null
+From d74bedcc06ac6927a4c678e4bc57cf6862734297 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 18 Mar 2022 08:50:03 +0800
+Subject: fbdev: defio: fix the pagelist corruption
+
+From: Chuansheng Liu <chuansheng.liu@intel.com>
+
+[ Upstream commit 856082f021a28221db2c32bd0531614a8382be67 ]
+
+Easily hit the below list corruption:
+==
+list_add corruption. prev->next should be next (ffffffffc0ceb090), but
+was ffffec604507edc8. (prev=ffffec604507edc8).
+WARNING: CPU: 65 PID: 3959 at lib/list_debug.c:26
+__list_add_valid+0x53/0x80
+CPU: 65 PID: 3959 Comm: fbdev Tainted: G U
+RIP: 0010:__list_add_valid+0x53/0x80
+Call Trace:
+ <TASK>
+ fb_deferred_io_mkwrite+0xea/0x150
+ do_page_mkwrite+0x57/0xc0
+ do_wp_page+0x278/0x2f0
+ __handle_mm_fault+0xdc2/0x1590
+ handle_mm_fault+0xdd/0x2c0
+ do_user_addr_fault+0x1d3/0x650
+ exc_page_fault+0x77/0x180
+ ? asm_exc_page_fault+0x8/0x30
+ asm_exc_page_fault+0x1e/0x30
+RIP: 0033:0x7fd98fc8fad1
+==
+
+Figure out the race happens when one process is adding &page->lru into
+the pagelist tail in fb_deferred_io_mkwrite(), another process is
+re-initializing the same &page->lru in fb_deferred_io_fault(), which is
+not protected by the lock.
+
+This fix is to init all the page lists one time during initialization,
+it not only fixes the list corruption, but also avoids INIT_LIST_HEAD()
+redundantly.
+
+V2: change "int i" to "unsigned int i" (Geert Uytterhoeven)
+
+Signed-off-by: Chuansheng Liu <chuansheng.liu@intel.com>
+Fixes: 105a940416fc ("fbdev/defio: Early-out if page is already enlisted")
+Cc: Thomas Zimmermann <tzimmermann@suse.de>
+Cc: Geert Uytterhoeven <geert@linux-m68k.org>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220318005003.51810-1-chuansheng.liu@intel.com
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index bead69f39ffc..0da3dfe35233 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -59,7 +59,6 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+ printk(KERN_ERR "no mapping available\n");
+
+ BUG_ON(!page->mapping);
+- INIT_LIST_HEAD(&page->lru);
+ page->index = vmf->pgoff;
+
+ vmf->page = page;
+@@ -216,6 +215,8 @@ static void fb_deferred_io_work(struct work_struct *work)
+ void fb_deferred_io_init(struct fb_info *info)
+ {
+ struct fb_deferred_io *fbdefio = info->fbdefio;
++ struct page *page;
++ unsigned int i;
+
+ BUG_ON(!fbdefio);
+ mutex_init(&fbdefio->lock);
+@@ -223,6 +224,12 @@ void fb_deferred_io_init(struct fb_info *info)
+ INIT_LIST_HEAD(&fbdefio->pagelist);
+ if (fbdefio->delay == 0) /* set a default of 1 s */
+ fbdefio->delay = HZ;
++
++ /* initialize all the page lists one time */
++ for (i = 0; i < info->fix.smem_len; i += PAGE_SIZE) {
++ page = fb_deferred_io_page(info, i);
++ INIT_LIST_HEAD(&page->lru);
++ }
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_init);
+
+--
+2.43.0
+
--- /dev/null
+From 27cb6ff6155d1aee9a23bedcc90905bf10d07f36 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 11 Feb 2022 10:46:40 +0100
+Subject: fbdev: Don't sort deferred-I/O pages by default
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 8c30e2d81bfddc5ab9f6b04db1c0f7d6ca7bdf46 ]
+
+Fbdev's deferred I/O sorts all dirty pages by default, which incurs a
+significant overhead. Make the sorting step optional and update the few
+drivers that require it. Use a FIFO list by default.
+
+Most fbdev drivers with deferred I/O build a bounding rectangle around
+the dirty pages or simply flush the whole screen. The only two affected
+DRM drivers, generic fbdev and vmwgfx, both use a bounding rectangle.
+In those cases, the exact order of the pages doesn't matter. The other
+drivers look at the page index or handle pages one-by-one. The patch
+sets the sort_pagelist flag for those, even though some of them would
+probably work correctly without sorting. Driver maintainers should update
+their driver accordingly.
+
+Sorting pages by memory offset for deferred I/O performs an implicit
+bubble-sort step on the list of dirty pages. The algorithm goes through
+the list of dirty pages and inserts each new page according to its
+index field. Even worse, list traversal always starts at the first
+entry. As video memory is most likely updated scanline by scanline, the
+algorithm traverses through the complete list for each updated page.
+
+For example, with 1024x768x32bpp each page covers exactly one scanline.
+Writing a single screen update from top to bottom requires updating
+768 pages. With an average list length of 384 entries, a screen update
+creates (768 * 384 =) 294912 compare operation.
+
+Fix this by making the sorting step opt-in and update the few drivers
+that require it. All other drivers work with unsorted page lists. Pages
+are appended to the list. Therefore, in the common case of writing the
+framebuffer top to bottom, pages are still sorted by offset, which may
+have a positive effect on performance.
+
+Playing a video [1] in mplayer's benchmark mode shows the difference
+(i7-4790, FullHD, simpledrm, kernel with debugging).
+
+ mplayer -benchmark -nosound -vo fbdev ./big_buck_bunny_720p_stereo.ogg
+
+With sorted page lists:
+
+ BENCHMARKs: VC: 32.960s VO: 73.068s A: 0.000s Sys: 2.413s = 108.441s
+ BENCHMARK%: VC: 30.3947% VO: 67.3802% A: 0.0000% Sys: 2.2251% = 100.0000%
+
+With unsorted page lists:
+
+ BENCHMARKs: VC: 31.005s VO: 42.889s A: 0.000s Sys: 2.256s = 76.150s
+ BENCHMARK%: VC: 40.7156% VO: 56.3219% A: 0.0000% Sys: 2.9625% = 100.0000%
+
+VC shows the overhead of video decoding, VO shows the overhead of the
+video output. Using unsorted page lists reduces the benchmark's run time
+by ~32s/~25%.
+
+v2:
+ * Make sorted pagelists the special case (Sam)
+ * Comment on drivers' use of pagelist (Sam)
+ * Warn about the overhead in comment
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Acked-by: Sam Ravnborg <sam@ravnborg.org>
+Acked-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
+Link: https://download.blender.org/peach/bigbuckbunny_movies/big_buck_bunny_720p_stereo.ogg # [1]
+Link: https://patchwork.freedesktop.org/patch/msgid/20220211094640.21632-3-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/staging/fbtft/fbtft-core.c | 1 +
+ drivers/video/fbdev/broadsheetfb.c | 1 +
+ drivers/video/fbdev/core/fb_defio.c | 24 +++++++++++++++++-------
+ drivers/video/fbdev/metronomefb.c | 1 +
+ drivers/video/fbdev/udlfb.c | 1 +
+ include/linux/fb.h | 1 +
+ 6 files changed, 22 insertions(+), 7 deletions(-)
+
+diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
+index 1690358b8f01..52b19ec580a4 100644
+--- a/drivers/staging/fbtft/fbtft-core.c
++++ b/drivers/staging/fbtft/fbtft-core.c
+@@ -654,6 +654,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
+ fbops->fb_blank = fbtft_fb_blank;
+
+ fbdefio->delay = HZ / fps;
++ fbdefio->sort_pagelist = true;
+ fbdefio->deferred_io = fbtft_deferred_io;
+ fb_deferred_io_init(info);
+
+diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
+index fd66f4d4a621..b9054f658838 100644
+--- a/drivers/video/fbdev/broadsheetfb.c
++++ b/drivers/video/fbdev/broadsheetfb.c
+@@ -1059,6 +1059,7 @@ static const struct fb_ops broadsheetfb_ops = {
+
+ static struct fb_deferred_io broadsheetfb_defio = {
+ .delay = HZ/4,
++ .sort_pagelist = true,
+ .deferred_io = broadsheetfb_dpy_deferred_io,
+ };
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 95264a621221..bead69f39ffc 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -92,7 +92,7 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+ struct page *page = vmf->page;
+ struct fb_info *info = vmf->vma->vm_private_data;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+- struct page *cur;
++ struct list_head *pos = &fbdefio->pagelist;
+
+ /* this is a callback we get when userspace first tries to
+ write to the page. we schedule a workqueue. that workqueue
+@@ -133,14 +133,24 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+ if (!list_empty(&page->lru))
+ goto page_already_added;
+
+- /* we loop through the pagelist before adding in order
+- to keep the pagelist sorted */
+- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+- if (cur->index > page->index)
+- break;
++ if (unlikely(fbdefio->sort_pagelist)) {
++ /*
++ * We loop through the pagelist before adding in order to
++ * keep the pagelist sorted. This has significant overhead
++ * of O(n^2) with n being the number of written pages. If
++ * possible, drivers should try to work with unsorted page
++ * lists instead.
++ */
++ struct page *cur;
++
++ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++ if (cur->index > page->index)
++ break;
++ }
++ pos = &cur->lru;
+ }
+
+- list_add_tail(&page->lru, &cur->lru);
++ list_add_tail(&page->lru, pos);
+
+ page_already_added:
+ mutex_unlock(&fbdefio->lock);
+diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
+index 952826557a0c..af858dd23ea6 100644
+--- a/drivers/video/fbdev/metronomefb.c
++++ b/drivers/video/fbdev/metronomefb.c
+@@ -568,6 +568,7 @@ static const struct fb_ops metronomefb_ops = {
+
+ static struct fb_deferred_io metronomefb_defio = {
+ .delay = HZ,
++ .sort_pagelist = true,
+ .deferred_io = metronomefb_dpy_deferred_io,
+ };
+
+diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
+index 0de7b867714a..8603898bf37e 100644
+--- a/drivers/video/fbdev/udlfb.c
++++ b/drivers/video/fbdev/udlfb.c
+@@ -982,6 +982,7 @@ static int dlfb_ops_open(struct fb_info *info, int user)
+
+ if (fbdefio) {
+ fbdefio->delay = DL_DEFIO_WRITE_DELAY;
++ fbdefio->sort_pagelist = true;
+ fbdefio->deferred_io = dlfb_dpy_deferred_io;
+ }
+
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 3d7306c9a706..9a77ab615c36 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -204,6 +204,7 @@ struct fb_pixmap {
+ struct fb_deferred_io {
+ /* delay between mkwrite and deferred handler */
+ unsigned long delay;
++ bool sort_pagelist; /* sort pagelist by offset */
+ struct mutex lock; /* mutex that protects the page list */
+ struct list_head pagelist; /* list of touched pages */
+ /* callback */
+--
+2.43.0
+
--- /dev/null
+From a67b59a0fe2dacc469e88dc08601e15ae362002a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 8 Mar 2023 11:50:12 +0100
+Subject: fbdev: Fix incorrect page mapping clearance at
+ fb_deferred_io_release()
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit fe9ae05cfbe587dda724fcf537c00bc2f287da62 ]
+
+The recent fix for the deferred I/O by the commit
+ 3efc61d95259 ("fbdev: Fix invalid page access after closing deferred I/O devices")
+caused a regression when the same fb device is opened/closed while
+it's being used. It resulted in a frozen screen even if something
+is redrawn there after the close. The breakage is because the patch
+was made under a wrong assumption of a single open; in the current
+code, fb_deferred_io_release() cleans up the page mapping of the
+pageref list and it calls cancel_delayed_work_sync() unconditionally,
+where both are no correct behavior for multiple opens.
+
+This patch adds a refcount for the opens of the device, and applies
+the cleanup only when all files get closed.
+
+As both fb_deferred_io_open() and _close() are called always in the
+fb_info lock (mutex), it's safe to use the normal int for the
+refcounting.
+
+Also, a useless BUG_ON() is dropped.
+
+Fixes: 3efc61d95259 ("fbdev: Fix invalid page access after closing deferred I/O devices")
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Reviewed-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/20230308105012.1845-1-tiwai@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 17 +++++++++++++----
+ include/linux/fb.h | 1 +
+ 2 files changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index b3f41f432ec2..7e76e53b83d5 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -288,17 +288,18 @@ void fb_deferred_io_open(struct fb_info *info,
+ struct inode *inode,
+ struct file *file)
+ {
++ struct fb_deferred_io *fbdefio = info->fbdefio;
++
+ file->f_mapping->a_ops = &fb_deferred_io_aops;
++ fbdefio->open_count++;
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_open);
+
+-void fb_deferred_io_release(struct fb_info *info)
++static void fb_deferred_io_lastclose(struct fb_info *info)
+ {
+- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct page *page;
+ int i;
+
+- BUG_ON(!fbdefio);
+ cancel_delayed_work_sync(&info->deferred_work);
+
+ /* clear out the mapping that we setup */
+@@ -307,13 +308,21 @@ void fb_deferred_io_release(struct fb_info *info)
+ page->mapping = NULL;
+ }
+ }
++
++void fb_deferred_io_release(struct fb_info *info)
++{
++ struct fb_deferred_io *fbdefio = info->fbdefio;
++
++ if (!--fbdefio->open_count)
++ fb_deferred_io_lastclose(info);
++}
+ EXPORT_SYMBOL_GPL(fb_deferred_io_release);
+
+ void fb_deferred_io_cleanup(struct fb_info *info)
+ {
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+- fb_deferred_io_release(info);
++ fb_deferred_io_lastclose(info);
+
+ kvfree(info->pagerefs);
+ mutex_destroy(&fbdefio->lock);
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 433cddf8442b..b79a833524ef 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -212,6 +212,7 @@ struct fb_deferred_io {
+ /* delay between mkwrite and deferred handler */
+ unsigned long delay;
+ bool sort_pagereflist; /* sort pagelist by offset */
++ int open_count; /* number of opened files; protected by fb_info lock */
+ struct mutex lock; /* mutex that protects the pageref list */
+ struct list_head pagereflist; /* list of pagerefs for touched pages */
+ /* callback */
+--
+2.43.0
+
--- /dev/null
+From 00cfec7001b41fd83fb3736cd71f29d09329bae2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 29 Jan 2023 09:28:56 +0100
+Subject: fbdev: Fix invalid page access after closing deferred I/O devices
+
+From: Takashi Iwai <tiwai@suse.de>
+
+[ Upstream commit 3efc61d95259956db25347e2a9562c3e54546e20 ]
+
+When a fbdev with deferred I/O is once opened and closed, the dirty
+pages still remain queued in the pageref list, and eventually later
+those may be processed in the delayed work. This may lead to a
+corruption of pages, hitting an Oops.
+
+This patch makes sure to cancel the delayed work and clean up the
+pageref list at closing the device for addressing the bug. A part of
+the cleanup code is factored out as a new helper function that is
+called from the common fb_release().
+
+Reviewed-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Tested-by: Miko Larsson <mikoxyzzz@gmail.com>
+Fixes: 56c134f7f1b5 ("fbdev: Track deferred-I/O pages in pageref struct")
+Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Link: https://patchwork.freedesktop.org/patch/msgid/20230129082856.22113-1-tiwai@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 10 +++++++++-
+ drivers/video/fbdev/core/fbmem.c | 4 ++++
+ include/linux/fb.h | 1 +
+ 3 files changed, 14 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 5faeca61d3dd..b3f41f432ec2 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -292,7 +292,7 @@ void fb_deferred_io_open(struct fb_info *info,
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_open);
+
+-void fb_deferred_io_cleanup(struct fb_info *info)
++void fb_deferred_io_release(struct fb_info *info)
+ {
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct page *page;
+@@ -306,6 +306,14 @@ void fb_deferred_io_cleanup(struct fb_info *info)
+ page = fb_deferred_io_page(info, i);
+ page->mapping = NULL;
+ }
++}
++EXPORT_SYMBOL_GPL(fb_deferred_io_release);
++
++void fb_deferred_io_cleanup(struct fb_info *info)
++{
++ struct fb_deferred_io *fbdefio = info->fbdefio;
++
++ fb_deferred_io_release(info);
+
+ kvfree(info->pagerefs);
+ mutex_destroy(&fbdefio->lock);
+diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
+index 1b288a613a6e..ec7a883715e3 100644
+--- a/drivers/video/fbdev/core/fbmem.c
++++ b/drivers/video/fbdev/core/fbmem.c
+@@ -1454,6 +1454,10 @@ __releases(&info->lock)
+ struct fb_info * const info = file->private_data;
+
+ lock_fb_info(info);
++#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
++ if (info->fbdefio)
++ fb_deferred_io_release(info);
++#endif
+ if (info->fbops->fb_release)
+ info->fbops->fb_release(info,1);
+ module_put(info->fbops->owner);
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index b322d30f6225..433cddf8442b 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -675,6 +675,7 @@ extern int fb_deferred_io_init(struct fb_info *info);
+ extern void fb_deferred_io_open(struct fb_info *info,
+ struct inode *inode,
+ struct file *file);
++extern void fb_deferred_io_release(struct fb_info *info);
+ extern void fb_deferred_io_cleanup(struct fb_info *info);
+ extern int fb_deferred_io_fsync(struct file *file, loff_t start,
+ loff_t end, int datasync);
+--
+2.43.0
+
--- /dev/null
+From 9b669dc1dd0ed0ff85993f50a0eae94b2605be86 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 18 Dec 2023 10:57:31 +0100
+Subject: fbdev: flush deferred IO before closing
+
+From: Nam Cao <namcao@linutronix.de>
+
+[ Upstream commit 33cd6ea9c0673517cdb06ad5c915c6f22e9615fc ]
+
+When framebuffer gets closed, the queued deferred IO gets cancelled. This
+can cause some last display data to vanish. This is problematic for users
+who send a still image to the framebuffer, then close the file: the image
+may never appear.
+
+To ensure none of display data get lost, flush the queued deferred IO
+first before closing.
+
+Another possible solution is to delete the cancel_delayed_work_sync()
+instead. The difference is that the display may appear some time after
+closing. However, the clearing of page mapping after this needs to be
+removed too, because the page mapping is used by the deferred work. It is
+not completely obvious whether it is okay to not clear the page mapping.
+For a patch intended for stable trees, go with the simple and obvious
+solution.
+
+Fixes: 60b59beafba8 ("fbdev: mm: Deferred IO support")
+Cc: stable@vger.kernel.org
+Signed-off-by: Nam Cao <namcao@linutronix.de>
+Reviewed-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+Signed-off-by: Helge Deller <deller@gmx.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/video/fbdev/core/fb_defio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 7e76e53b83d5..1f12c2043603 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -300,7 +300,7 @@ static void fb_deferred_io_lastclose(struct fb_info *info)
+ struct page *page;
+ int i;
+
+- cancel_delayed_work_sync(&info->deferred_work);
++ flush_delayed_work(&info->deferred_work);
+
+ /* clear out the mapping that we setup */
+ for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) {
+--
+2.43.0
+
--- /dev/null
+From 6706769caf4a001397cc1c5dde6b8010fb77c24a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Apr 2022 12:08:33 +0200
+Subject: fbdev: Rename pagelist to pagereflist for deferred I/O
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit e80eec1b871a2acb8f5c92db4c237e9ae6dd322b ]
+
+Rename various instances of pagelist to pagereflist. The list now
+stores pageref structures, so the new name is more appropriate.
+
+In their write-back helpers, several fbdev drivers refer to the
+pageref list in struct fb_deferred_io instead of using the one
+supplied as argument to the function. Convert them over to the
+supplied one. It's the same instance, so no change of behavior
+occurs.
+
+v4:
+ * fix commit message (Javier)
+
+Suggested-by: Sam Ravnborg <sam@ravnborg.org>
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220429100834.18898-5-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 7 +++----
+ drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 5 ++---
+ drivers/hid/hid-picolcd_fb.c | 2 +-
+ drivers/staging/fbtft/fbtft-core.c | 10 +++++-----
+ drivers/video/fbdev/broadsheetfb.c | 12 +++++-------
+ drivers/video/fbdev/core/fb_defio.c | 18 +++++++++---------
+ drivers/video/fbdev/hecubafb.c | 3 +--
+ drivers/video/fbdev/hyperv_fb.c | 5 ++---
+ drivers/video/fbdev/metronomefb.c | 12 +++++-------
+ drivers/video/fbdev/sh_mobile_lcdcfb.c | 16 +++++++---------
+ drivers/video/fbdev/smscufx.c | 8 +++-----
+ drivers/video/fbdev/ssd1307fb.c | 3 +--
+ drivers/video/fbdev/udlfb.c | 8 +++-----
+ drivers/video/fbdev/xen-fbfront.c | 5 ++---
+ include/drm/drm_fb_helper.h | 3 +--
+ include/linux/fb.h | 6 +++---
+ 16 files changed, 53 insertions(+), 70 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 888ec6135544..82960d5d4e73 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -683,13 +683,12 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
+ /**
+ * drm_fb_helper_deferred_io() - fbdev deferred_io callback function
+ * @info: fb_info struct pointer
+- * @pagelist: list of mmap framebuffer pages that have to be flushed
++ * @pagereflist: list of mmap framebuffer pages that have to be flushed
+ *
+ * This function is used as the &fb_deferred_io.deferred_io
+ * callback function for flushing the fbdev mmap writes.
+ */
+-void drm_fb_helper_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ unsigned long start, end, min, max;
+ struct fb_deferred_io_pageref *pageref;
+@@ -697,7 +696,7 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
+
+ min = ULONG_MAX;
+ max = 0;
+- list_for_each_entry(pageref, pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ start = pageref->offset;
+ end = start + PAGE_SIZE - 1;
+ min = min(min, start);
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+index 1f20e3c958ef..79b08e927f76 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+@@ -316,8 +316,7 @@ static int vmw_fb_pan_display(struct fb_var_screeninfo *var,
+ return 0;
+ }
+
+-static void vmw_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void vmw_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ struct vmw_fb_par *par = info->par;
+ unsigned long start, end, min, max;
+@@ -327,7 +326,7 @@ static void vmw_deferred_io(struct fb_info *info,
+
+ min = ULONG_MAX;
+ max = 0;
+- list_for_each_entry(pageref, pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *page = pageref->page;
+ start = page->index << PAGE_SHIFT;
+ end = start + PAGE_SIZE - 1;
+diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
+index 33c102a60992..8a0d1365cd72 100644
+--- a/drivers/hid/hid-picolcd_fb.c
++++ b/drivers/hid/hid-picolcd_fb.c
+@@ -432,7 +432,7 @@ static const struct fb_ops picolcdfb_ops = {
+
+
+ /* Callback from deferred IO workqueue */
+-static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
++static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ picolcd_fb_update(info);
+ }
+diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
+index 5c52b5ff9f51..7aec35ae5cd5 100644
+--- a/drivers/staging/fbtft/fbtft-core.c
++++ b/drivers/staging/fbtft/fbtft-core.c
+@@ -322,7 +322,7 @@ static void fbtft_mkdirty(struct fb_info *info, int y, int height)
+ schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+ }
+
+-static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
++static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ struct fbtft_par *par = info->par;
+ unsigned int dirty_lines_start, dirty_lines_end;
+@@ -340,7 +340,7 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+ spin_unlock(&par->dirty_lock);
+
+ /* Mark display lines as dirty */
+- list_for_each_entry(pageref, pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *page = pageref->page;
+ count++;
+ index = page->index << PAGE_SHIFT;
+@@ -654,9 +654,9 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
+ fbops->fb_setcolreg = fbtft_fb_setcolreg;
+ fbops->fb_blank = fbtft_fb_blank;
+
+- fbdefio->delay = HZ / fps;
+- fbdefio->sort_pagelist = true;
+- fbdefio->deferred_io = fbtft_deferred_io;
++ fbdefio->delay = HZ / fps;
++ fbdefio->sort_pagereflist = true;
++ fbdefio->deferred_io = fbtft_deferred_io;
+ fb_deferred_io_init(info);
+
+ snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name);
+diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
+index 2ca753d27f76..8b953d20ccdc 100644
+--- a/drivers/video/fbdev/broadsheetfb.c
++++ b/drivers/video/fbdev/broadsheetfb.c
+@@ -929,13 +929,11 @@ static void broadsheetfb_dpy_update(struct broadsheetfb_par *par)
+ }
+
+ /* this is called back from the deferred io workqueue */
+-static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void broadsheetfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ u16 y1 = 0, h = 0;
+ int prev_index = -1;
+ struct fb_deferred_io_pageref *pageref;
+- struct fb_deferred_io *fbdefio = info->fbdefio;
+ int h_inc;
+ u16 yres = info->var.yres;
+ u16 xres = info->var.xres;
+@@ -944,7 +942,7 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+ h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
+
+ /* walk the written page list and swizzle the data */
+- list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *cur = pageref->page;
+ if (prev_index < 0) {
+ /* just starting so assign first page */
+@@ -1059,9 +1057,9 @@ static const struct fb_ops broadsheetfb_ops = {
+ };
+
+ static struct fb_deferred_io broadsheetfb_defio = {
+- .delay = HZ/4,
+- .sort_pagelist = true,
+- .deferred_io = broadsheetfb_dpy_deferred_io,
++ .delay = HZ/4,
++ .sort_pagereflist = true,
++ .deferred_io = broadsheetfb_dpy_deferred_io,
+ };
+
+ static int broadsheetfb_probe(struct platform_device *dev)
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index a79af3b5faf3..5faeca61d3dd 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -41,7 +41,7 @@ static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info
+ struct page *page)
+ {
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+- struct list_head *pos = &fbdefio->pagelist;
++ struct list_head *pos = &fbdefio->pagereflist;
+ unsigned long pgoff = offset >> PAGE_SHIFT;
+ struct fb_deferred_io_pageref *pageref, *cur;
+
+@@ -63,7 +63,7 @@ static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info
+ pageref->page = page;
+ pageref->offset = pgoff << PAGE_SHIFT;
+
+- if (unlikely(fbdefio->sort_pagelist)) {
++ if (unlikely(fbdefio->sort_pagereflist)) {
+ /*
+ * We loop through the list of pagerefs before adding in
+ * order to keep the pagerefs sorted. This has significant
+@@ -71,7 +71,7 @@ static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info
+ * pages. If possible, drivers should try to work with
+ * unsorted page lists instead.
+ */
+- list_for_each_entry(cur, &info->fbdefio->pagelist, list) {
++ list_for_each_entry(cur, &fbdefio->pagereflist, list) {
+ if (cur->offset > pageref->offset)
+ break;
+ }
+@@ -163,7 +163,7 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+ mutex_lock(&fbdefio->lock);
+
+ /* first write in this cycle, notify the driver */
+- if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
++ if (fbdefio->first_io && list_empty(&fbdefio->pagereflist))
+ fbdefio->first_io(info);
+
+ pageref = fb_deferred_io_pageref_get(info, offset, page);
+@@ -228,18 +228,18 @@ static void fb_deferred_io_work(struct work_struct *work)
+
+ /* here we mkclean the pages, then do all deferred IO */
+ mutex_lock(&fbdefio->lock);
+- list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ list_for_each_entry(pageref, &fbdefio->pagereflist, list) {
+ struct page *cur = pageref->page;
+ lock_page(cur);
+ page_mkclean(cur);
+ unlock_page(cur);
+ }
+
+- /* driver's callback with pagelist */
+- fbdefio->deferred_io(info, &fbdefio->pagelist);
++ /* driver's callback with pagereflist */
++ fbdefio->deferred_io(info, &fbdefio->pagereflist);
+
+ /* clear the list */
+- list_for_each_entry_safe(pageref, next, &fbdefio->pagelist, list)
++ list_for_each_entry_safe(pageref, next, &fbdefio->pagereflist, list)
+ fb_deferred_io_pageref_put(pageref, info);
+
+ mutex_unlock(&fbdefio->lock);
+@@ -259,7 +259,7 @@ int fb_deferred_io_init(struct fb_info *info)
+
+ mutex_init(&fbdefio->lock);
+ INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
+- INIT_LIST_HEAD(&fbdefio->pagelist);
++ INIT_LIST_HEAD(&fbdefio->pagereflist);
+ if (fbdefio->delay == 0) /* set a default of 1 s */
+ fbdefio->delay = HZ;
+
+diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c
+index 00d77105161a..bd6b0dec414b 100644
+--- a/drivers/video/fbdev/hecubafb.c
++++ b/drivers/video/fbdev/hecubafb.c
+@@ -115,8 +115,7 @@ static void hecubafb_dpy_update(struct hecubafb_par *par)
+ }
+
+ /* this is called back from the deferred io workqueue */
+-static void hecubafb_dpy_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void hecubafb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ hecubafb_dpy_update(info->par);
+ }
+diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
+index 704ba7ee13c6..6a881cfd7f5c 100644
+--- a/drivers/video/fbdev/hyperv_fb.c
++++ b/drivers/video/fbdev/hyperv_fb.c
+@@ -420,8 +420,7 @@ static void hvfb_docopy(struct hvfb_par *par,
+ }
+
+ /* Deferred IO callback */
+-static void synthvid_deferred_io(struct fb_info *p,
+- struct list_head *pagelist)
++static void synthvid_deferred_io(struct fb_info *p, struct list_head *pagereflist)
+ {
+ struct hvfb_par *par = p->par;
+ struct fb_deferred_io_pageref *pageref;
+@@ -437,7 +436,7 @@ static void synthvid_deferred_io(struct fb_info *p,
+ * in synthvid_update function by clamping the y2
+ * value to yres.
+ */
+- list_for_each_entry(pageref, pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *page = pageref->page;
+ start = page->index << PAGE_SHIFT;
+ end = start + PAGE_SIZE - 1;
+diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
+index 74672539ee7b..b8df0bb7cabc 100644
+--- a/drivers/video/fbdev/metronomefb.c
++++ b/drivers/video/fbdev/metronomefb.c
+@@ -465,16 +465,14 @@ static u16 metronomefb_dpy_update_page(struct metronomefb_par *par, int index)
+ }
+
+ /* this is called back from the deferred io workqueue */
+-static void metronomefb_dpy_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void metronomefb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ u16 cksum;
+ struct fb_deferred_io_pageref *pageref;
+- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct metronomefb_par *par = info->par;
+
+ /* walk the written page list and swizzle the data */
+- list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *cur = pageref->page;
+ cksum = metronomefb_dpy_update_page(par,
+ (cur->index << PAGE_SHIFT));
+@@ -568,9 +566,9 @@ static const struct fb_ops metronomefb_ops = {
+ };
+
+ static struct fb_deferred_io metronomefb_defio = {
+- .delay = HZ,
+- .sort_pagelist = true,
+- .deferred_io = metronomefb_dpy_deferred_io,
++ .delay = HZ,
++ .sort_pagereflist = true,
++ .deferred_io = metronomefb_dpy_deferred_io,
+ };
+
+ static int metronomefb_probe(struct platform_device *dev)
+diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+index b6b5ce3505c0..e33c016c5428 100644
+--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
++++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+@@ -435,8 +435,7 @@ static struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = {
+ .read_data = lcdc_sys_read_data,
+ };
+
+-static int sh_mobile_lcdc_sginit(struct fb_info *info,
+- struct list_head *pagelist)
++static int sh_mobile_lcdc_sginit(struct fb_info *info, struct list_head *pagereflist)
+ {
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
+@@ -445,7 +444,7 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
+
+ sg_init_table(ch->sglist, nr_pages_max);
+
+- list_for_each_entry(pageref, pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *page = pageref->page;
+ sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
+ }
+@@ -453,8 +452,7 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
+ return nr_pages;
+ }
+
+-static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void sh_mobile_lcdc_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ const struct sh_mobile_lcdc_panel_cfg *panel = &ch->cfg->panel_cfg;
+@@ -463,7 +461,7 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+ sh_mobile_lcdc_clk_on(ch->lcdc);
+
+ /*
+- * It's possible to get here without anything on the pagelist via
++ * It's possible to get here without anything on the pagereflist via
+ * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
+ * invocation. In the former case, the acceleration routines are
+ * stepped in to when using the framebuffer console causing the
+@@ -473,12 +471,12 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
+ * acceleration routines have their own methods for writing in
+ * that still need to be updated.
+ *
+- * The fsync() and empty pagelist case could be optimized for,
++ * The fsync() and empty pagereflist case could be optimized for,
+ * but we don't bother, as any application exhibiting such
+ * behaviour is fundamentally broken anyways.
+ */
+- if (!list_empty(pagelist)) {
+- unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
++ if (!list_empty(pagereflist)) {
++ unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagereflist);
+
+ /* trigger panel update */
+ dma_map_sg(ch->lcdc->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
+index bf3d151abd6f..14e4e106d26b 100644
+--- a/drivers/video/fbdev/smscufx.c
++++ b/drivers/video/fbdev/smscufx.c
+@@ -953,12 +953,10 @@ static void ufx_ops_fillrect(struct fb_info *info,
+ * Touching ANY framebuffer memory that triggers a page fault
+ * in fb_defio will cause a deadlock, when it also tries to
+ * grab the same mutex. */
+-static void ufx_dpy_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void ufx_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+- struct fb_deferred_io_pageref *pageref;
+- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct ufx_data *dev = info->par;
++ struct fb_deferred_io_pageref *pageref;
+
+ if (!fb_defio)
+ return;
+@@ -967,7 +965,7 @@ static void ufx_dpy_deferred_io(struct fb_info *info,
+ return;
+
+ /* walk the written page list and render each to device */
+- list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ /* create a rectangle of full screen width that encloses the
+ * entire dirty framebuffer page */
+ struct page *cur = pageref->page;
+diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
+index 1e2f71c2f8a8..7acf7c0b263e 100644
+--- a/drivers/video/fbdev/ssd1307fb.c
++++ b/drivers/video/fbdev/ssd1307fb.c
+@@ -370,8 +370,7 @@ static const struct fb_ops ssd1307fb_ops = {
+ .fb_imageblit = ssd1307fb_imageblit,
+ };
+
+-static void ssd1307fb_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void ssd1307fb_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ ssd1307fb_update_display(info->par);
+ }
+diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
+index c187163fe580..12bd5a7318e1 100644
+--- a/drivers/video/fbdev/udlfb.c
++++ b/drivers/video/fbdev/udlfb.c
+@@ -780,11 +780,9 @@ static void dlfb_ops_fillrect(struct fb_info *info,
+ * in fb_defio will cause a deadlock, when it also tries to
+ * grab the same mutex.
+ */
+-static void dlfb_dpy_deferred_io(struct fb_info *info,
+- struct list_head *pagelist)
++static void dlfb_dpy_deferred_io(struct fb_info *info, struct list_head *pagereflist)
+ {
+ struct fb_deferred_io_pageref *pageref;
+- struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct dlfb_data *dlfb = info->par;
+ struct urb *urb;
+ char *cmd;
+@@ -810,7 +808,7 @@ static void dlfb_dpy_deferred_io(struct fb_info *info,
+ cmd = urb->transfer_buffer;
+
+ /* walk the written page list and render each to device */
+- list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *cur = pageref->page;
+
+ if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
+@@ -983,7 +981,7 @@ static int dlfb_ops_open(struct fb_info *info, int user)
+
+ if (fbdefio) {
+ fbdefio->delay = DL_DEFIO_WRITE_DELAY;
+- fbdefio->sort_pagelist = true;
++ fbdefio->sort_pagereflist = true;
+ fbdefio->deferred_io = dlfb_dpy_deferred_io;
+ }
+
+diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
+index 00d9502ee25a..37e3f226f78c 100644
+--- a/drivers/video/fbdev/xen-fbfront.c
++++ b/drivers/video/fbdev/xen-fbfront.c
+@@ -181,8 +181,7 @@ static void xenfb_refresh(struct xenfb_info *info,
+ xenfb_do_update(info, x1, y1, x2 - x1 + 1, y2 - y1 + 1);
+ }
+
+-static void xenfb_deferred_io(struct fb_info *fb_info,
+- struct list_head *pagelist)
++static void xenfb_deferred_io(struct fb_info *fb_info, struct list_head *pagereflist)
+ {
+ struct xenfb_info *info = fb_info->par;
+ struct fb_deferred_io_pageref *pageref;
+@@ -191,7 +190,7 @@ static void xenfb_deferred_io(struct fb_info *fb_info,
+
+ miny = INT_MAX;
+ maxy = 0;
+- list_for_each_entry(pageref, pagelist, list) {
++ list_for_each_entry(pageref, pagereflist, list) {
+ struct page *page = pageref->page;
+ beg = page->index << PAGE_SHIFT;
+ end = beg + PAGE_SIZE - 1;
+diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
+index 3af4624368d8..329607ca65c0 100644
+--- a/include/drm/drm_fb_helper.h
++++ b/include/drm/drm_fb_helper.h
+@@ -229,8 +229,7 @@ void drm_fb_helper_fill_info(struct fb_info *info,
+ struct drm_fb_helper *fb_helper,
+ struct drm_fb_helper_surface_size *sizes);
+
+-void drm_fb_helper_deferred_io(struct fb_info *info,
+- struct list_head *pagelist);
++void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist);
+
+ ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
+ size_t count, loff_t *ppos);
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 768de6534a82..b322d30f6225 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -211,9 +211,9 @@ struct fb_deferred_io_pageref {
+ struct fb_deferred_io {
+ /* delay between mkwrite and deferred handler */
+ unsigned long delay;
+- bool sort_pagelist; /* sort pagelist by offset */
+- struct mutex lock; /* mutex that protects the page list */
+- struct list_head pagelist; /* list of touched pages */
++ bool sort_pagereflist; /* sort pagelist by offset */
++ struct mutex lock; /* mutex that protects the pageref list */
++ struct list_head pagereflist; /* list of pagerefs for touched pages */
+ /* callback */
+ void (*first_io)(struct fb_info *info);
+ void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
+--
+2.43.0
+
--- /dev/null
+From 1177a6fb2dee82cb4c03bc1068dbfb7ff9172408 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Apr 2022 12:08:31 +0200
+Subject: fbdev: Track deferred-I/O pages in pageref struct
+
+From: Thomas Zimmermann <tzimmermann@suse.de>
+
+[ Upstream commit 56c134f7f1b58be08bdb0ca8372474a4a5165f31 ]
+
+Store the per-page state for fbdev's deferred I/O in struct
+fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
+that have to be written back to video memory. Update all affected
+drivers.
+
+As with pages before, fbdev acquires a pageref when an mmaped page
+of the framebuffer is being written to. It holds the pageref in a
+list of all currently written pagerefs until it flushes the written
+pages to video memory. Writeback occurs periodically. After writeback
+fbdev releases all pagerefs and builds up a new dirty list until the
+next writeback occurs.
+
+Using pagerefs has a number of benefits.
+
+For pages of the framebuffer, the deferred I/O code used struct
+page.lru as an entry into the list of dirty pages. The lru field is
+owned by the page cache, which makes deferred I/O incompatible with
+some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
+struct fb_deferred_io_pageref now provides an entry into a list of
+dirty framebuffer pages, freeing lru for use with the page cache.
+
+Drivers also assumed that struct page.index is the page offset into
+the framebuffer. This is not true for DRM buffers, which are located
+at various offset within a mapped area. struct fb_deferred_io_pageref
+explicitly stores an offset into the framebuffer. struct page.index
+is now only the page offset into the mapped area.
+
+These changes will allow DRM to use fbdev deferred I/O without an
+intermediate shadow buffer.
+
+v3:
+ * use pageref->offset for sorting
+ * fix grammar in comment
+v2:
+ * minor fixes in commit message
+
+Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
+Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20220429100834.18898-3-tzimmermann@suse.de
+Stable-dep-of: 33cd6ea9c067 ("fbdev: flush deferred IO before closing")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_fb_helper.c | 6 +-
+ drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 5 +-
+ drivers/staging/fbtft/fbtft-core.c | 5 +-
+ drivers/video/fbdev/broadsheetfb.c | 5 +-
+ drivers/video/fbdev/core/fb_defio.c | 156 ++++++++++++++++---------
+ drivers/video/fbdev/hyperv_fb.c | 5 +-
+ drivers/video/fbdev/metronomefb.c | 5 +-
+ drivers/video/fbdev/sh_mobile_lcdcfb.c | 6 +-
+ drivers/video/fbdev/smscufx.c | 5 +-
+ drivers/video/fbdev/udlfb.c | 5 +-
+ drivers/video/fbdev/xen-fbfront.c | 5 +-
+ include/linux/fb.h | 11 +-
+ 12 files changed, 145 insertions(+), 74 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 3d9c0444df40..888ec6135544 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -692,13 +692,13 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+ {
+ unsigned long start, end, min, max;
+- struct page *page;
++ struct fb_deferred_io_pageref *pageref;
+ u32 y1, y2;
+
+ min = ULONG_MAX;
+ max = 0;
+- list_for_each_entry(page, pagelist, lru) {
+- start = page->index << PAGE_SHIFT;
++ list_for_each_entry(pageref, pagelist, list) {
++ start = pageref->offset;
+ end = start + PAGE_SIZE - 1;
+ min = min(min, start);
+ max = max(max, end);
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+index f18ed03a8b2d..1f20e3c958ef 100644
+--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+@@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
+ struct vmw_fb_par *par = info->par;
+ unsigned long start, end, min, max;
+ unsigned long flags;
+- struct page *page;
++ struct fb_deferred_io_pageref *pageref;
+ int y1, y2;
+
+ min = ULONG_MAX;
+ max = 0;
+- list_for_each_entry(page, pagelist, lru) {
++ list_for_each_entry(pageref, pagelist, list) {
++ struct page *page = pageref->page;
+ start = page->index << PAGE_SHIFT;
+ end = start + PAGE_SIZE - 1;
+ min = min(min, start);
+diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
+index 52b19ec580a4..5c52b5ff9f51 100644
+--- a/drivers/staging/fbtft/fbtft-core.c
++++ b/drivers/staging/fbtft/fbtft-core.c
+@@ -326,7 +326,7 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+ {
+ struct fbtft_par *par = info->par;
+ unsigned int dirty_lines_start, dirty_lines_end;
+- struct page *page;
++ struct fb_deferred_io_pageref *pageref;
+ unsigned long index;
+ unsigned int y_low = 0, y_high = 0;
+ int count = 0;
+@@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
+ spin_unlock(&par->dirty_lock);
+
+ /* Mark display lines as dirty */
+- list_for_each_entry(page, pagelist, lru) {
++ list_for_each_entry(pageref, pagelist, list) {
++ struct page *page = pageref->page;
+ count++;
+ index = page->index << PAGE_SHIFT;
+ y_low = index / info->fix.line_length;
+diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
+index b9054f658838..2ca753d27f76 100644
+--- a/drivers/video/fbdev/broadsheetfb.c
++++ b/drivers/video/fbdev/broadsheetfb.c
+@@ -934,7 +934,7 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+ {
+ u16 y1 = 0, h = 0;
+ int prev_index = -1;
+- struct page *cur;
++ struct fb_deferred_io_pageref *pageref;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ int h_inc;
+ u16 yres = info->var.yres;
+@@ -944,7 +944,8 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
+ h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
+
+ /* walk the written page list and swizzle the data */
+- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++ list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ struct page *cur = pageref->page;
+ if (prev_index < 0) {
+ /* just starting so assign first page */
+ y1 = (cur->index << PAGE_SHIFT) / xres;
+diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
+index 0da3dfe35233..a79af3b5faf3 100644
+--- a/drivers/video/fbdev/core/fb_defio.c
++++ b/drivers/video/fbdev/core/fb_defio.c
+@@ -36,6 +36,60 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs
+ return page;
+ }
+
++static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info *info,
++ unsigned long offset,
++ struct page *page)
++{
++ struct fb_deferred_io *fbdefio = info->fbdefio;
++ struct list_head *pos = &fbdefio->pagelist;
++ unsigned long pgoff = offset >> PAGE_SHIFT;
++ struct fb_deferred_io_pageref *pageref, *cur;
++
++ if (WARN_ON_ONCE(pgoff >= info->npagerefs))
++ return NULL; /* incorrect allocation size */
++
++ /* 1:1 mapping between pageref and page offset */
++ pageref = &info->pagerefs[pgoff];
++
++ /*
++ * This check is to catch the case where a new process could start
++ * writing to the same page through a new PTE. This new access
++ * can cause a call to .page_mkwrite even if the original process'
++ * PTE is marked writable.
++ */
++ if (!list_empty(&pageref->list))
++ goto pageref_already_added;
++
++ pageref->page = page;
++ pageref->offset = pgoff << PAGE_SHIFT;
++
++ if (unlikely(fbdefio->sort_pagelist)) {
++ /*
++ * We loop through the list of pagerefs before adding in
++ * order to keep the pagerefs sorted. This has significant
++ * overhead of O(n^2) with n being the number of written
++ * pages. If possible, drivers should try to work with
++ * unsorted page lists instead.
++ */
++ list_for_each_entry(cur, &info->fbdefio->pagelist, list) {
++ if (cur->offset > pageref->offset)
++ break;
++ }
++ pos = &cur->list;
++ }
++
++ list_add_tail(&pageref->list, pos);
++
++pageref_already_added:
++ return pageref;
++}
++
++static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
++ struct fb_info *info)
++{
++ list_del_init(&pageref->list);
++}
++
+ /* this is to find and return the vmalloc-ed fb pages */
+ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+ {
+@@ -59,7 +113,7 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
+ printk(KERN_ERR "no mapping available\n");
+
+ BUG_ON(!page->mapping);
+- page->index = vmf->pgoff;
++ page->index = vmf->pgoff; /* for page_mkclean() */
+
+ vmf->page = page;
+ return 0;
+@@ -91,7 +145,11 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+ struct page *page = vmf->page;
+ struct fb_info *info = vmf->vma->vm_private_data;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+- struct list_head *pos = &fbdefio->pagelist;
++ struct fb_deferred_io_pageref *pageref;
++ unsigned long offset;
++ vm_fault_t ret;
++
++ offset = (vmf->address - vmf->vma->vm_start);
+
+ /* this is a callback we get when userspace first tries to
+ write to the page. we schedule a workqueue. that workqueue
+@@ -108,6 +166,12 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+ if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
+ fbdefio->first_io(info);
+
++ pageref = fb_deferred_io_pageref_get(info, offset, page);
++ if (WARN_ON_ONCE(!pageref)) {
++ ret = VM_FAULT_OOM;
++ goto err_mutex_unlock;
++ }
++
+ /*
+ * We want the page to remain locked from ->page_mkwrite until
+ * the PTE is marked dirty to avoid page_mkclean() being called
+@@ -116,47 +180,17 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+ * Do this by locking the page here and informing the caller
+ * about it with VM_FAULT_LOCKED.
+ */
+- lock_page(page);
+-
+- /*
+- * This check is to catch the case where a new process could start
+- * writing to the same page through a new PTE. This new access
+- * can cause a call to .page_mkwrite even if the original process'
+- * PTE is marked writable.
+- *
+- * TODO: The lru field is owned by the page cache; hence the name.
+- * We dequeue in fb_deferred_io_work() after flushing the
+- * page's content into video memory. Instead of lru, fbdefio
+- * should have it's own field.
+- */
+- if (!list_empty(&page->lru))
+- goto page_already_added;
+-
+- if (unlikely(fbdefio->sort_pagelist)) {
+- /*
+- * We loop through the pagelist before adding in order to
+- * keep the pagelist sorted. This has significant overhead
+- * of O(n^2) with n being the number of written pages. If
+- * possible, drivers should try to work with unsorted page
+- * lists instead.
+- */
+- struct page *cur;
+-
+- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+- if (cur->index > page->index)
+- break;
+- }
+- pos = &cur->lru;
+- }
+-
+- list_add_tail(&page->lru, pos);
++ lock_page(pageref->page);
+
+-page_already_added:
+ mutex_unlock(&fbdefio->lock);
+
+ /* come back after delay to process the deferred IO */
+ schedule_delayed_work(&info->deferred_work, fbdefio->delay);
+ return VM_FAULT_LOCKED;
++
++err_mutex_unlock:
++ mutex_unlock(&fbdefio->lock);
++ return ret;
+ }
+
+ static const struct vm_operations_struct fb_deferred_io_vm_ops = {
+@@ -188,15 +222,14 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
+ /* workqueue callback */
+ static void fb_deferred_io_work(struct work_struct *work)
+ {
+- struct fb_info *info = container_of(work, struct fb_info,
+- deferred_work.work);
+- struct list_head *node, *next;
+- struct page *cur;
++ struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
++ struct fb_deferred_io_pageref *pageref, *next;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+
+ /* here we mkclean the pages, then do all deferred IO */
+ mutex_lock(&fbdefio->lock);
+- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++ list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ struct page *cur = pageref->page;
+ lock_page(cur);
+ page_mkclean(cur);
+ unlock_page(cur);
+@@ -206,30 +239,48 @@ static void fb_deferred_io_work(struct work_struct *work)
+ fbdefio->deferred_io(info, &fbdefio->pagelist);
+
+ /* clear the list */
+- list_for_each_safe(node, next, &fbdefio->pagelist) {
+- list_del_init(node);
+- }
++ list_for_each_entry_safe(pageref, next, &fbdefio->pagelist, list)
++ fb_deferred_io_pageref_put(pageref, info);
++
+ mutex_unlock(&fbdefio->lock);
+ }
+
+-void fb_deferred_io_init(struct fb_info *info)
++int fb_deferred_io_init(struct fb_info *info)
+ {
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+- struct page *page;
+- unsigned int i;
++ struct fb_deferred_io_pageref *pagerefs;
++ unsigned long npagerefs, i;
++ int ret;
+
+ BUG_ON(!fbdefio);
++
++ if (WARN_ON(!info->fix.smem_len))
++ return -EINVAL;
++
+ mutex_init(&fbdefio->lock);
+ INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
+ INIT_LIST_HEAD(&fbdefio->pagelist);
+ if (fbdefio->delay == 0) /* set a default of 1 s */
+ fbdefio->delay = HZ;
+
+- /* initialize all the page lists one time */
+- for (i = 0; i < info->fix.smem_len; i += PAGE_SIZE) {
+- page = fb_deferred_io_page(info, i);
+- INIT_LIST_HEAD(&page->lru);
++ npagerefs = DIV_ROUND_UP(info->fix.smem_len, PAGE_SIZE);
++
++ /* alloc a page ref for each page of the display memory */
++ pagerefs = kvcalloc(npagerefs, sizeof(*pagerefs), GFP_KERNEL);
++ if (!pagerefs) {
++ ret = -ENOMEM;
++ goto err;
+ }
++ for (i = 0; i < npagerefs; ++i)
++ INIT_LIST_HEAD(&pagerefs[i].list);
++ info->npagerefs = npagerefs;
++ info->pagerefs = pagerefs;
++
++ return 0;
++
++err:
++ mutex_destroy(&fbdefio->lock);
++ return ret;
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_init);
+
+@@ -256,6 +307,7 @@ void fb_deferred_io_cleanup(struct fb_info *info)
+ page->mapping = NULL;
+ }
+
++ kvfree(info->pagerefs);
+ mutex_destroy(&fbdefio->lock);
+ }
+ EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
+diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
+index de865e197c8d..704ba7ee13c6 100644
+--- a/drivers/video/fbdev/hyperv_fb.c
++++ b/drivers/video/fbdev/hyperv_fb.c
+@@ -424,7 +424,7 @@ static void synthvid_deferred_io(struct fb_info *p,
+ struct list_head *pagelist)
+ {
+ struct hvfb_par *par = p->par;
+- struct page *page;
++ struct fb_deferred_io_pageref *pageref;
+ unsigned long start, end;
+ int y1, y2, miny, maxy;
+
+@@ -437,7 +437,8 @@ static void synthvid_deferred_io(struct fb_info *p,
+ * in synthvid_update function by clamping the y2
+ * value to yres.
+ */
+- list_for_each_entry(page, pagelist, lru) {
++ list_for_each_entry(pageref, pagelist, list) {
++ struct page *page = pageref->page;
+ start = page->index << PAGE_SHIFT;
+ end = start + PAGE_SIZE - 1;
+ y1 = start / p->fix.line_length;
+diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
+index af858dd23ea6..74672539ee7b 100644
+--- a/drivers/video/fbdev/metronomefb.c
++++ b/drivers/video/fbdev/metronomefb.c
+@@ -469,12 +469,13 @@ static void metronomefb_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+ {
+ u16 cksum;
+- struct page *cur;
++ struct fb_deferred_io_pageref *pageref;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct metronomefb_par *par = info->par;
+
+ /* walk the written page list and swizzle the data */
+- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++ list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ struct page *cur = pageref->page;
+ cksum = metronomefb_dpy_update_page(par,
+ (cur->index << PAGE_SHIFT));
+ par->metromem_img_csum -= par->csum_table[cur->index];
+diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+index aa4ebe3192ec..b6b5ce3505c0 100644
+--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
++++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
+@@ -440,13 +440,15 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
+ {
+ struct sh_mobile_lcdc_chan *ch = info->par;
+ unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
+- struct page *page;
++ struct fb_deferred_io_pageref *pageref;
+ int nr_pages = 0;
+
+ sg_init_table(ch->sglist, nr_pages_max);
+
+- list_for_each_entry(page, pagelist, lru)
++ list_for_each_entry(pageref, pagelist, list) {
++ struct page *page = pageref->page;
+ sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
++ }
+
+ return nr_pages;
+ }
+diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
+index b3295cd7fd4f..bf3d151abd6f 100644
+--- a/drivers/video/fbdev/smscufx.c
++++ b/drivers/video/fbdev/smscufx.c
+@@ -956,7 +956,7 @@ static void ufx_ops_fillrect(struct fb_info *info,
+ static void ufx_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+ {
+- struct page *cur;
++ struct fb_deferred_io_pageref *pageref;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct ufx_data *dev = info->par;
+
+@@ -967,9 +967,10 @@ static void ufx_dpy_deferred_io(struct fb_info *info,
+ return;
+
+ /* walk the written page list and render each to device */
+- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++ list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+ /* create a rectangle of full screen width that encloses the
+ * entire dirty framebuffer page */
++ struct page *cur = pageref->page;
+ const int x = 0;
+ const int width = dev->info->var.xres;
+ const int y = (cur->index << PAGE_SHIFT) / (width * 2);
+diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
+index 8603898bf37e..c187163fe580 100644
+--- a/drivers/video/fbdev/udlfb.c
++++ b/drivers/video/fbdev/udlfb.c
+@@ -783,7 +783,7 @@ static void dlfb_ops_fillrect(struct fb_info *info,
+ static void dlfb_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+ {
+- struct page *cur;
++ struct fb_deferred_io_pageref *pageref;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct dlfb_data *dlfb = info->par;
+ struct urb *urb;
+@@ -810,7 +810,8 @@ static void dlfb_dpy_deferred_io(struct fb_info *info,
+ cmd = urb->transfer_buffer;
+
+ /* walk the written page list and render each to device */
+- list_for_each_entry(cur, &fbdefio->pagelist, lru) {
++ list_for_each_entry(pageref, &fbdefio->pagelist, list) {
++ struct page *cur = pageref->page;
+
+ if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
+ &cmd, cur->index << PAGE_SHIFT,
+diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
+index 5ec51445bee8..00d9502ee25a 100644
+--- a/drivers/video/fbdev/xen-fbfront.c
++++ b/drivers/video/fbdev/xen-fbfront.c
+@@ -185,13 +185,14 @@ static void xenfb_deferred_io(struct fb_info *fb_info,
+ struct list_head *pagelist)
+ {
+ struct xenfb_info *info = fb_info->par;
+- struct page *page;
++ struct fb_deferred_io_pageref *pageref;
+ unsigned long beg, end;
+ int y1, y2, miny, maxy;
+
+ miny = INT_MAX;
+ maxy = 0;
+- list_for_each_entry(page, pagelist, lru) {
++ list_for_each_entry(pageref, pagelist, list) {
++ struct page *page = pageref->page;
+ beg = page->index << PAGE_SHIFT;
+ end = beg + PAGE_SIZE - 1;
+ y1 = beg / fb_info->fix.line_length;
+diff --git a/include/linux/fb.h b/include/linux/fb.h
+index 9a77ab615c36..768de6534a82 100644
+--- a/include/linux/fb.h
++++ b/include/linux/fb.h
+@@ -201,6 +201,13 @@ struct fb_pixmap {
+ };
+
+ #ifdef CONFIG_FB_DEFERRED_IO
++struct fb_deferred_io_pageref {
++ struct page *page;
++ unsigned long offset;
++ /* private */
++ struct list_head list;
++};
++
+ struct fb_deferred_io {
+ /* delay between mkwrite and deferred handler */
+ unsigned long delay;
+@@ -469,6 +476,8 @@ struct fb_info {
+ #endif
+ #ifdef CONFIG_FB_DEFERRED_IO
+ struct delayed_work deferred_work;
++ unsigned long npagerefs;
++ struct fb_deferred_io_pageref *pagerefs;
+ struct fb_deferred_io *fbdefio;
+ #endif
+
+@@ -662,7 +671,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
+
+ /* drivers/video/fb_defio.c */
+ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
+-extern void fb_deferred_io_init(struct fb_info *info);
++extern int fb_deferred_io_init(struct fb_info *info);
+ extern void fb_deferred_io_open(struct fb_info *info,
+ struct inode *inode,
+ struct file *file);
+--
+2.43.0
+
--- /dev/null
+From 7e80290871316bb942dbb58af4881108959ec77f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 22 Apr 2022 11:03:12 +0200
+Subject: mwifiex: Select firmware based on strapping
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Andrejs Cainikovs <andrejs.cainikovs@toradex.com>
+
+[ Upstream commit 255ca28a659d3cfb069f73c7644853ed93aecdb0 ]
+
+Some WiFi/Bluetooth modules might have different host connection
+options, allowing to either use SDIO for both WiFi and Bluetooth,
+or SDIO for WiFi and UART for Bluetooth. It is possible to detect
+whether a module has SDIO-SDIO or SDIO-UART connection by reading
+its host strap register.
+
+This change introduces a way to automatically select appropriate
+firmware depending of the connection method, and removes a need
+of symlinking or overwriting the original firmware file with a
+required one.
+
+Host strap register used in this commit comes from the NXP driver [1]
+hosted at Code Aurora.
+
+[1] https://source.codeaurora.org/external/imx/linux-imx/tree/drivers/net/wireless/nxp/mxm_wifiex/wlan_src/mlinux/moal_sdio_mmc.c?h=rel_imx_5.4.70_2.3.2&id=688b67b2c7220b01521ffe560da7eee33042c7bd#n1274
+
+Signed-off-by: Andrejs Cainikovs <andrejs.cainikovs@toradex.com>
+Reviewed-by: Alvin Šipraga <alsi@bang-olufsen.dk>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/20220422090313.125857-2-andrejs.cainikovs@toradex.com
+Stable-dep-of: 1c5d463c0770 ("wifi: mwifiex: add extra delay for firmware ready")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sdio.c | 21 ++++++++++++++++++++-
+ drivers/net/wireless/marvell/mwifiex/sdio.h | 5 +++++
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index b09e60fedeb1..016065a56e6c 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -182,6 +182,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8997 = {
+ .host_int_rsr_reg = 0x4,
+ .host_int_status_reg = 0x0C,
+ .host_int_mask_reg = 0x08,
++ .host_strap_reg = 0xF4,
++ .host_strap_mask = 0x01,
++ .host_strap_value = 0x00,
+ .status_reg_0 = 0xE8,
+ .status_reg_1 = 0xE9,
+ .sdio_int_mask = 0xff,
+@@ -283,6 +286,9 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8987 = {
+ .host_int_rsr_reg = 0x4,
+ .host_int_status_reg = 0x0C,
+ .host_int_mask_reg = 0x08,
++ .host_strap_reg = 0xF4,
++ .host_strap_mask = 0x01,
++ .host_strap_value = 0x00,
+ .status_reg_0 = 0xE8,
+ .status_reg_1 = 0xE9,
+ .sdio_int_mask = 0xff,
+@@ -537,6 +543,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+ struct mwifiex_sdio_device *data = (void *)id->driver_data;
+
+ card->firmware = data->firmware;
++ card->firmware_sdiouart = data->firmware_sdiouart;
+ card->reg = data->reg;
+ card->max_ports = data->max_ports;
+ card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+@@ -2440,6 +2447,7 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
+ int ret;
+ struct sdio_mmc_card *card = adapter->card;
+ struct sdio_func *func = card->func;
++ const char *firmware = card->firmware;
+
+ /* save adapter pointer in card */
+ card->adapter = adapter;
+@@ -2456,7 +2464,18 @@ static int mwifiex_register_dev(struct mwifiex_adapter *adapter)
+ return ret;
+ }
+
+- strcpy(adapter->fw_name, card->firmware);
++ /* Select correct firmware (sdsd or sdiouart) firmware based on the strapping
++ * option
++ */
++ if (card->firmware_sdiouart) {
++ u8 val;
++
++ mwifiex_read_reg(adapter, card->reg->host_strap_reg, &val);
++ if ((val & card->reg->host_strap_mask) == card->reg->host_strap_value)
++ firmware = card->firmware_sdiouart;
++ }
++ strcpy(adapter->fw_name, firmware);
++
+ if (card->fw_dump_enh) {
+ adapter->mem_type_mapping_tbl = generic_mem_type_map;
+ adapter->num_mem_types = 1;
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
+index 5648512c9300..ad2c28cbb630 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
+@@ -196,6 +196,9 @@ struct mwifiex_sdio_card_reg {
+ u8 host_int_rsr_reg;
+ u8 host_int_status_reg;
+ u8 host_int_mask_reg;
++ u8 host_strap_reg;
++ u8 host_strap_mask;
++ u8 host_strap_value;
+ u8 status_reg_0;
+ u8 status_reg_1;
+ u8 sdio_int_mask;
+@@ -241,6 +244,7 @@ struct sdio_mmc_card {
+
+ struct completion fw_done;
+ const char *firmware;
++ const char *firmware_sdiouart;
+ const struct mwifiex_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+@@ -274,6 +278,7 @@ struct sdio_mmc_card {
+
+ struct mwifiex_sdio_device {
+ const char *firmware;
++ const char *firmware_sdiouart;
+ const struct mwifiex_sdio_card_reg *reg;
+ u8 max_ports;
+ u8 mp_agg_pkt_limit;
+--
+2.43.0
+
--- /dev/null
+From 701fdb2fff3ddaf22147d8d0b2032d4b8322efcf Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 5 Dec 2021 19:00:43 +0100
+Subject: scripts: decode_stacktrace: demangle Rust symbols
+
+From: Miguel Ojeda <ojeda@kernel.org>
+
+[ Upstream commit 99115db4ecc87af73415939439ec604ea0531e6f ]
+
+Recent versions of both Binutils (`c++filt`) and LLVM (`llvm-cxxfilt`)
+provide Rust v0 mangling support.
+
+Reviewed-by: Kees Cook <keescook@chromium.org>
+Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Co-developed-by: Alex Gaynor <alex.gaynor@gmail.com>
+Signed-off-by: Alex Gaynor <alex.gaynor@gmail.com>
+Co-developed-by: Wedson Almeida Filho <wedsonaf@google.com>
+Signed-off-by: Wedson Almeida Filho <wedsonaf@google.com>
+Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
+Stable-dep-of: efbd63983533 ("scripts/decode_stacktrace.sh: optionally use LLVM utilities")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ scripts/decode_stacktrace.sh | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
+index 7075e26ab2c4..564c5632e1a2 100755
+--- a/scripts/decode_stacktrace.sh
++++ b/scripts/decode_stacktrace.sh
+@@ -8,6 +8,14 @@ usage() {
+ echo " $0 -r <release> | <vmlinux> [<base path>|auto] [<modules path>]"
+ }
+
++# Try to find a Rust demangler
++if type llvm-cxxfilt >/dev/null 2>&1 ; then
++ cppfilt=llvm-cxxfilt
++elif type c++filt >/dev/null 2>&1 ; then
++ cppfilt=c++filt
++ cppfilt_opts=-i
++fi
++
+ if [[ $1 == "-r" ]] ; then
+ vmlinux=""
+ basepath="auto"
+@@ -180,6 +188,12 @@ parse_symbol() {
+ # In the case of inlines, move everything to same line
+ code=${code//$'\n'/' '}
+
++ # Demangle if the name looks like a Rust symbol and if
++ # we got a Rust demangler
++ if [[ $name =~ ^_R && $cppfilt != "" ]] ; then
++ name=$("$cppfilt" "$cppfilt_opts" "$name")
++ fi
++
+ # Replace old address with pretty line numbers
+ symbol="$segment$name ($code)"
+ }
+--
+2.43.0
+
--- /dev/null
+From 48d25a8f063530bb98041d6266c3e01040e40e48 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Sep 2023 03:48:17 +0000
+Subject: scripts/decode_stacktrace.sh: optionally use LLVM utilities
+
+From: Carlos Llamas <cmllamas@google.com>
+
+[ Upstream commit efbd6398353315b7018e6943e41fee9ec35e875f ]
+
+GNU's addr2line can have problems parsing a vmlinux built with LLVM,
+particularly when LTO was used. In order to decode the traces correctly
+this patch adds the ability to switch to LLVM's utilities readelf and
+addr2line. The same approach is followed by Will in [1].
+
+Before:
+ $ scripts/decode_stacktrace.sh vmlinux < kernel.log
+ [17716.240635] Call trace:
+ [17716.240646] skb_cow_data (??:?)
+ [17716.240654] esp6_input (ld-temp.o:?)
+ [17716.240666] xfrm_input (ld-temp.o:?)
+ [17716.240674] xfrm6_rcv (??:?)
+ [...]
+
+After:
+ $ LLVM=1 scripts/decode_stacktrace.sh vmlinux < kernel.log
+ [17716.240635] Call trace:
+ [17716.240646] skb_cow_data (include/linux/skbuff.h:2172 net/core/skbuff.c:4503)
+ [17716.240654] esp6_input (net/ipv6/esp6.c:977)
+ [17716.240666] xfrm_input (net/xfrm/xfrm_input.c:659)
+ [17716.240674] xfrm6_rcv (net/ipv6/xfrm6_input.c:172)
+ [...]
+
+Note that one could set CROSS_COMPILE=llvm- instead to hack around this
+issue. However, doing so can break the decodecode routine as it will
+force the selection of other LLVM utilities down the line e.g. llvm-as.
+
+[1] https://lore.kernel.org/all/20230914131225.13415-3-will@kernel.org/
+
+Link: https://lkml.kernel.org/r/20230929034836.403735-1-cmllamas@google.com
+Signed-off-by: Carlos Llamas <cmllamas@google.com>
+Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
+Reviewed-by: Elliot Berman <quic_eberman@quicinc.com>
+Tested-by: Justin Stitt <justinstitt@google.com>
+Cc: Will Deacon <will@kernel.org>
+Cc: John Stultz <jstultz@google.com>
+Cc: Masahiro Yamada <masahiroy@kernel.org>
+Cc: Nathan Chancellor <nathan@kernel.org>
+Cc: Tom Rix <trix@redhat.com>
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ scripts/decode_stacktrace.sh | 19 +++++++++++++++++--
+ 1 file changed, 17 insertions(+), 2 deletions(-)
+
+diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
+index 564c5632e1a2..bfe5a4082d8e 100755
+--- a/scripts/decode_stacktrace.sh
++++ b/scripts/decode_stacktrace.sh
+@@ -16,6 +16,21 @@ elif type c++filt >/dev/null 2>&1 ; then
+ cppfilt_opts=-i
+ fi
+
++UTIL_SUFFIX=
++if [[ -z ${LLVM:-} ]]; then
++ UTIL_PREFIX=${CROSS_COMPILE:-}
++else
++ UTIL_PREFIX=llvm-
++ if [[ ${LLVM} == */ ]]; then
++ UTIL_PREFIX=${LLVM}${UTIL_PREFIX}
++ elif [[ ${LLVM} == -* ]]; then
++ UTIL_SUFFIX=${LLVM}
++ fi
++fi
++
++READELF=${UTIL_PREFIX}readelf${UTIL_SUFFIX}
++ADDR2LINE=${UTIL_PREFIX}addr2line${UTIL_SUFFIX}
++
+ if [[ $1 == "-r" ]] ; then
+ vmlinux=""
+ basepath="auto"
+@@ -75,7 +90,7 @@ find_module() {
+
+ if [[ "$modpath" != "" ]] ; then
+ for fn in $(find "$modpath" -name "${module//_/[-_]}.ko*") ; do
+- if readelf -WS "$fn" | grep -qwF .debug_line ; then
++ if ${READELF} -WS "$fn" | grep -qwF .debug_line ; then
+ echo $fn
+ return
+ fi
+@@ -169,7 +184,7 @@ parse_symbol() {
+ if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then
+ local code=${cache[$module,$address]}
+ else
+- local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
++ local code=$(${ADDR2LINE} -i -e "$objfile" "$address" 2>/dev/null)
+ if [[ $aarray_support == true ]]; then
+ cache[$module,$address]=$code
+ fi
+--
+2.43.0
+
--- /dev/null
+From 073cdb347279ca86cf043c0fe12fd47856b09c9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 29 Apr 2022 14:37:57 -0700
+Subject: scripts/decode_stacktrace.sh: support old bash version
+
+From: Schspa Shi <schspa@gmail.com>
+
+[ Upstream commit 3af8acf6aff2a98731522b52927429760f0b8006 ]
+
+Old bash version don't support associative array variables. Avoid to use
+associative array variables to avoid error.
+
+Without this, old bash version will report error as fellowing
+[ 15.954042] Kernel panic - not syncing: sysrq triggered crash
+[ 15.955252] CPU: 1 PID: 167 Comm: sh Not tainted 5.18.0-rc1-00208-gb7d075db2fd5 #4
+[ 15.956472] Hardware name: Hobot J5 Virtual development board (DT)
+[ 15.957856] Call trace:
+./scripts/decode_stacktrace.sh: line 128: ,dump_backtrace: syntax error: operand expected (error token is ",dump_backtrace")
+
+Link: https://lkml.kernel.org/r/20220409180331.24047-1-schspa@gmail.com
+Signed-off-by: Schspa Shi <schspa@gmail.com>
+Cc: Stephen Boyd <swboyd@chromium.org>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Stable-dep-of: efbd63983533 ("scripts/decode_stacktrace.sh: optionally use LLVM utilities")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ scripts/decode_stacktrace.sh | 27 +++++++++++++++++++--------
+ 1 file changed, 19 insertions(+), 8 deletions(-)
+
+diff --git a/scripts/decode_stacktrace.sh b/scripts/decode_stacktrace.sh
+index 5fbad61fe490..7075e26ab2c4 100755
+--- a/scripts/decode_stacktrace.sh
++++ b/scripts/decode_stacktrace.sh
+@@ -45,8 +45,13 @@ else
+ fi
+ fi
+
+-declare -A cache
+-declare -A modcache
++declare aarray_support=true
++declare -A cache 2>/dev/null
++if [[ $? != 0 ]]; then
++ aarray_support=false
++else
++ declare -A modcache
++fi
+
+ find_module() {
+ if [[ -n $debuginfod ]] ; then
+@@ -97,7 +102,7 @@ parse_symbol() {
+
+ if [[ $module == "" ]] ; then
+ local objfile=$vmlinux
+- elif [[ "${modcache[$module]+isset}" == "isset" ]]; then
++ elif [[ $aarray_support == true && "${modcache[$module]+isset}" == "isset" ]]; then
+ local objfile=${modcache[$module]}
+ else
+ local objfile=$(find_module)
+@@ -105,7 +110,9 @@ parse_symbol() {
+ echo "WARNING! Modules path isn't set, but is needed to parse this symbol" >&2
+ return
+ fi
+- modcache[$module]=$objfile
++ if [[ $aarray_support == true ]]; then
++ modcache[$module]=$objfile
++ fi
+ fi
+
+ # Remove the englobing parenthesis
+@@ -125,7 +132,7 @@ parse_symbol() {
+ # Use 'nm vmlinux' to figure out the base address of said symbol.
+ # It's actually faster to call it every time than to load it
+ # all into bash.
+- if [[ "${cache[$module,$name]+isset}" == "isset" ]]; then
++ if [[ $aarray_support == true && "${cache[$module,$name]+isset}" == "isset" ]]; then
+ local base_addr=${cache[$module,$name]}
+ else
+ local base_addr=$(nm "$objfile" 2>/dev/null | awk '$3 == "'$name'" && ($2 == "t" || $2 == "T") {print $1; exit}')
+@@ -133,7 +140,9 @@ parse_symbol() {
+ # address not found
+ return
+ fi
+- cache[$module,$name]="$base_addr"
++ if [[ $aarray_support == true ]]; then
++ cache[$module,$name]="$base_addr"
++ fi
+ fi
+ # Let's start doing the math to get the exact address into the
+ # symbol. First, strip out the symbol total length.
+@@ -149,11 +158,13 @@ parse_symbol() {
+
+ # Pass it to addr2line to get filename and line number
+ # Could get more than one result
+- if [[ "${cache[$module,$address]+isset}" == "isset" ]]; then
++ if [[ $aarray_support == true && "${cache[$module,$address]+isset}" == "isset" ]]; then
+ local code=${cache[$module,$address]}
+ else
+ local code=$(${CROSS_COMPILE}addr2line -i -e "$objfile" "$address" 2>/dev/null)
+- cache[$module,$address]=$code
++ if [[ $aarray_support == true ]]; then
++ cache[$module,$address]=$code
++ fi
+ fi
+
+ # addr2line doesn't return a proper error code if it fails, so
+--
+2.43.0
+
--- /dev/null
+From 56ebed83bab688a588edb8ea276033b3db38ed98 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Jun 2022 13:04:04 +0300
+Subject: serial: 8250_exar: Fill in rs485_supported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 59c221f8e1269278161313048c71929c9950b2c4 ]
+
+Add information on supported serial_rs485 features.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20220606100433.13793-8-ilpo.jarvinen@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 0c2a5f471ce5 ("serial: 8250_exar: Set missing rs485_supported flag")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/8250/8250_exar.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
+index 0b1976ceb01f..24878d5a6a05 100644
+--- a/drivers/tty/serial/8250/8250_exar.c
++++ b/drivers/tty/serial/8250/8250_exar.c
+@@ -123,6 +123,7 @@ struct exar8250;
+
+ struct exar8250_platform {
+ int (*rs485_config)(struct uart_port *, struct serial_rs485 *);
++ const struct serial_rs485 *rs485_supported;
+ int (*register_gpio)(struct pci_dev *, struct uart_8250_port *);
+ void (*unregister_gpio)(struct uart_8250_port *);
+ };
+@@ -441,10 +442,15 @@ static int generic_rs485_config(struct uart_port *port,
+ return 0;
+ }
+
++static const struct serial_rs485 generic_rs485_supported = {
++ .flags = SER_RS485_ENABLED,
++};
++
+ static const struct exar8250_platform exar8250_default_platform = {
+ .register_gpio = xr17v35x_register_gpio,
+ .unregister_gpio = xr17v35x_unregister_gpio,
+ .rs485_config = generic_rs485_config,
++ .rs485_supported = &generic_rs485_supported,
+ };
+
+ static int iot2040_rs485_config(struct uart_port *port,
+@@ -480,6 +486,10 @@ static int iot2040_rs485_config(struct uart_port *port,
+ return generic_rs485_config(port, rs485);
+ }
+
++static const struct serial_rs485 iot2040_rs485_supported = {
++ .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS,
++};
++
+ static const struct property_entry iot2040_gpio_properties[] = {
+ PROPERTY_ENTRY_U32("exar,first-pin", 10),
+ PROPERTY_ENTRY_U32("ngpios", 1),
+@@ -508,6 +518,7 @@ static int iot2040_register_gpio(struct pci_dev *pcidev,
+
+ static const struct exar8250_platform iot2040_platform = {
+ .rs485_config = iot2040_rs485_config,
++ .rs485_supported = &iot2040_rs485_supported,
+ .register_gpio = iot2040_register_gpio,
+ .unregister_gpio = xr17v35x_unregister_gpio,
+ };
+@@ -550,6 +561,7 @@ pci_xr17v35x_setup(struct exar8250 *priv, struct pci_dev *pcidev,
+
+ port->port.uartclk = baud * 16;
+ port->port.rs485_config = platform->rs485_config;
++ port->port.rs485_supported = platform->rs485_supported;
+
+ /*
+ * Setup the UART clock for the devices on expansion slot to
+--
+2.43.0
+
--- /dev/null
+From fd145d84ec6dbe6802f1ccef24360a2325eb115a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jan 2024 07:18:18 +0100
+Subject: serial: 8250_exar: Set missing rs485_supported flag
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Lino Sanfilippo <l.sanfilippo@kunbus.com>
+
+[ Upstream commit 0c2a5f471ce58bca8f8ab5fcb911aff91eaaa5eb ]
+
+The UART supports an auto-RTS mode in which the RTS pin is automatically
+activated during transmission. So mark this mode as being supported even
+if RTS is not controlled by the driver but the UART.
+
+Also the serial core expects now at least one of both modes rts-on-send or
+rts-after-send to be supported. This is since during sanitization
+unsupported flags are deleted from a RS485 configuration set by userspace.
+However if the configuration ends up with both flags unset, the core prints
+a warning since it considers such a configuration invalid (see
+uart_sanitize_serial_rs485()).
+
+Cc: <stable@vger.kernel.org>
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Lino Sanfilippo <l.sanfilippo@kunbus.com>
+Link: https://lore.kernel.org/r/20240103061818.564-8-l.sanfilippo@kunbus.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/8250/8250_exar.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c
+index 24878d5a6a05..ada9666f5988 100644
+--- a/drivers/tty/serial/8250/8250_exar.c
++++ b/drivers/tty/serial/8250/8250_exar.c
+@@ -443,7 +443,7 @@ static int generic_rs485_config(struct uart_port *port,
+ }
+
+ static const struct serial_rs485 generic_rs485_supported = {
+- .flags = SER_RS485_ENABLED,
++ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND,
+ };
+
+ static const struct exar8250_platform exar8250_default_platform = {
+@@ -487,7 +487,8 @@ static int iot2040_rs485_config(struct uart_port *port,
+ }
+
+ static const struct serial_rs485 iot2040_rs485_supported = {
+- .flags = SER_RS485_ENABLED | SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS,
++ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND |
++ SER_RS485_RX_DURING_TX | SER_RS485_TERMINATE_BUS,
+ };
+
+ static const struct property_entry iot2040_gpio_properties[] = {
+--
+2.43.0
+
scripts-decode_stacktrace.sh-support-old-bash-versio.patch
scripts-decode_stacktrace-demangle-rust-symbols.patch
scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch
+dma-buf-add-dma_fence_timestamp-helper.patch-23638
+mwifiex-select-firmware-based-on-strapping.patch-10303
+wifi-mwifiex-support-sd8978-chipset.patch-17202
+wifi-mwifiex-add-extra-delay-for-firmware-ready.patch-15130
+bus-moxtet-add-spi-device-table.patch-12211
+wifi-mwifiex-fix-uninitialized-firmware_stat.patch-14283
+crypto-lib-mpi-fix-unexpected-pointer-access-in-mpi_.patch-5277
+usb-dwc3-gadget-wait-for-ep0-xfers-to-complete-durin.patch-16924
+usb-dwc3-ep0-don-t-prepare-beyond-setup-stage.patch-27319
+usb-dwc3-gadget-only-end-transfer-for-ep0-data-phase.patch-25635
+usb-dwc3-gadget-delay-issuing-end-transfer.patch-16020
+usb-dwc3-fix-ep0-handling-when-getting-reset-while-d.patch-19133
+usb-dwc3-gadget-force-sending-delayed-status-during-.patch-25697
+usb-dwc3-gadget-submit-endxfer-command-if-delayed-du.patch-28020
+usb-dwc3-gadget-stall-and-restart-ep0-if-host-is-unr.patch-18649
+usb-dwc3-gadget-refactor-ep0-forced-stall-restart-in.patch-17868
+usb-dwc3-gadget-handle-ep0-request-dequeuing-properl.patch-14688
+usb-dwc3-gadget-queue-pm-runtime-idle-on-disconnect-.patch-19688
+serial-8250_exar-fill-in-rs485_supported.patch-28198
+serial-8250_exar-set-missing-rs485_supported-flag.patch-19915
+fbdev-defio-early-out-if-page-is-already-enlisted.patch-31235
+fbdev-don-t-sort-deferred-i-o-pages-by-default.patch-6824
+fbdev-defio-fix-the-pagelist-corruption.patch-6707
+fbdev-track-deferred-i-o-pages-in-pageref-struct.patch-4580
+fbdev-rename-pagelist-to-pagereflist-for-deferred-i-.patch-15544
+fbdev-fix-invalid-page-access-after-closing-deferred.patch-12976
+fbdev-fix-incorrect-page-mapping-clearance-at-fb_def.patch-30601
+fbdev-flush-deferred-io-before-closing.patch-17074
+scripts-decode_stacktrace.sh-support-old-bash-versio.patch-6443
+scripts-decode_stacktrace-demangle-rust-symbols.patch-30753
+scripts-decode_stacktrace.sh-optionally-use-llvm-uti.patch-24681
--- /dev/null
+From b99690d4610a25983636c8d393b94a2fe845fd0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Apr 2022 19:22:50 -0700
+Subject: usb: dwc3: ep0: Don't prepare beyond Setup stage
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit c96683798e272366866a5c0ce3073c0b5a256db7 ]
+
+Since we can't guarantee that the host won't send new Setup packet
+before going through the device-initiated disconnect, don't prepare
+beyond the Setup stage and keep the device in EP0_SETUP_PHASE. This
+ensures that the device-initated disconnect sequence can go through
+gracefully. Note that the controller won't service the End Transfer
+command if it can't DMA out the Setup packet.
+
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/6bacec56ecabb2c6e49a09cedfcac281fdc97de0.1650593829.git.Thinh.Nguyen@synopsys.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/ep0.c | 2 +-
+ drivers/usb/dwc3/gadget.c | 29 +++++++++++++++++------------
+ 2 files changed, 18 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
+index 34cb8662e129..624de23782b5 100644
+--- a/drivers/usb/dwc3/ep0.c
++++ b/drivers/usb/dwc3/ep0.c
+@@ -816,7 +816,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
+ int ret = -EINVAL;
+ u32 len;
+
+- if (!dwc->gadget_driver)
++ if (!dwc->gadget_driver || !dwc->connected)
+ goto out;
+
+ trace_dwc3_ctrl_req(ctrl);
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 3a663d71d791..2afe6784f1df 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2472,6 +2472,23 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->connected = false;
+
++ /*
++ * Per databook, when we want to stop the gadget, if a control transfer
++ * is still in process, complete it and get the core into setup phase.
++ */
++ if (dwc->ep0state != EP0_SETUP_PHASE) {
++ int ret;
++
++ reinit_completion(&dwc->ep0_in_setup);
++
++ spin_unlock_irqrestore(&dwc->lock, flags);
++ ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
++ msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
++ spin_lock_irqsave(&dwc->lock, flags);
++ if (ret == 0)
++ dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
++ }
++
+ /*
+ * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 4.1.8 Table 4-7, it states that for a device-initiated
+@@ -2516,18 +2533,6 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+ is_on = !!is_on;
+
+ dwc->softconnect = is_on;
+- /*
+- * Per databook, when we want to stop the gadget, if a control transfer
+- * is still in process, complete it and get the core into setup phase.
+- */
+- if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+- reinit_completion(&dwc->ep0_in_setup);
+-
+- ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+- msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+- if (ret == 0)
+- dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
+- }
+
+ /*
+ * Avoid issuing a runtime resume if the device is already in the
+--
+2.43.0
+
--- /dev/null
+From 8f73ee63dc2cc5c4e2dd666f8aaa8451604fdac4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 4 May 2022 12:36:41 -0700
+Subject: usb: dwc3: Fix ep0 handling when getting reset while doing control
+ transfer
+
+From: Mayank Rana <quic_mrana@quicinc.com>
+
+[ Upstream commit 9d778f0c5f95ca5aa2ff628ea281978697e8d89b ]
+
+According to the databook ep0 should be in setup phase during reset.
+If host issues reset between control transfers, ep0 will be in an
+invalid state. Fix this by issuing stall and restart on ep0 if it
+is not in setup phase.
+
+Also SW needs to complete pending control transfer and setup core for
+next setup stage as per data book. Hence check ep0 state during reset
+interrupt handling and make sure active transfers on ep0 out/in
+endpoint are stopped by queuing ENDXFER command for that endpoint and
+restart ep0 out again to receive next setup packet.
+
+Signed-off-by: Mayank Rana <quic_mrana@quicinc.com>
+Link: https://lore.kernel.org/r/1651693001-29891-1-git-send-email-quic_mrana@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/ep0.c | 11 ++++++++---
+ drivers/usb/dwc3/gadget.c | 27 +++++++++++++++++++++++++--
+ drivers/usb/dwc3/gadget.h | 2 ++
+ 3 files changed, 35 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
+index 624de23782b5..f402c66039af 100644
+--- a/drivers/usb/dwc3/ep0.c
++++ b/drivers/usb/dwc3/ep0.c
+@@ -218,7 +218,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+ return ret;
+ }
+
+-static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
++void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+ {
+ struct dwc3_ep *dep;
+
+@@ -1090,13 +1090,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
+ }
+
+-static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
++void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+ {
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+
+- if (!dep->resource_index)
++ /*
++ * For status/DATA OUT stage, TRB will be queued on ep0 out
++ * endpoint for which resource index is zero. Hence allow
++ * queuing ENDXFER command for ep0 out endpoint.
++ */
++ if (!dep->resource_index && dep->number)
+ return;
+
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 5cf2fb165a56..b978e42ace75 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -869,12 +869,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
+ reg |= DWC3_DALEPENA_EP(dep->number);
+ dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
+
++ dep->trb_dequeue = 0;
++ dep->trb_enqueue = 0;
++
+ if (usb_endpoint_xfer_control(desc))
+ goto out;
+
+ /* Initialize the TRB ring */
+- dep->trb_dequeue = 0;
+- dep->trb_enqueue = 0;
+ memset(dep->trb_pool, 0,
+ sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
+
+@@ -2702,6 +2703,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
+
+ /* begin to receive SETUP packets */
+ dwc->ep0state = EP0_SETUP_PHASE;
++ dwc->ep0_bounced = false;
+ dwc->link_state = DWC3_LINK_STATE_SS_DIS;
+ dwc->delayed_status = false;
+ dwc3_ep0_out_start(dwc);
+@@ -3790,6 +3792,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+ }
+
+ dwc3_reset_gadget(dwc);
++
++ /*
++ * From SNPS databook section 8.1.2, the EP0 should be in setup
++ * phase. So ensure that EP0 is in setup phase by issuing a stall
++ * and restart if EP0 is not in setup phase.
++ */
++ if (dwc->ep0state != EP0_SETUP_PHASE) {
++ unsigned int dir;
++
++ dir = !!dwc->ep0_expect_in;
++ if (dwc->ep0state == EP0_DATA_PHASE)
++ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++ else
++ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++
++ dwc->eps[0]->trb_enqueue = 0;
++ dwc->eps[1]->trb_enqueue = 0;
++
++ dwc3_ep0_stall_and_restart(dwc);
++ }
++
+ /*
+ * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+ * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
+diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
+index f763380e672e..55a56cf67d73 100644
+--- a/drivers/usb/dwc3/gadget.h
++++ b/drivers/usb/dwc3/gadget.h
+@@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
+ void dwc3_ep0_interrupt(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event);
+ void dwc3_ep0_out_start(struct dwc3 *dwc);
++void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
++void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
+ int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
+ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
+ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+--
+2.43.0
+
--- /dev/null
+From adce0573fd1d8834fc982a989489eabca25c066c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Apr 2022 19:23:03 -0700
+Subject: usb: dwc3: gadget: Delay issuing End Transfer
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit f66eef8fb8989a7193cafc3870f7c7b2b97f16cb ]
+
+If the controller hasn't DMA'ed the Setup data from its fifo, it won't
+process the End Transfer command. Polling for the command completion may
+block the driver from servicing the Setup phase and cause a timeout.
+Previously we only check and delay issuing End Transfer in the case of
+endpoint dequeue. Let's do that for all End Transfer scenarios.
+
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/2fcf3b5d90068d549589a57a27a79f76c6769b04.1650593829.git.Thinh.Nguyen@synopsys.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 22 ++++++++++++----------
+ 1 file changed, 12 insertions(+), 10 deletions(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 4d830ba7d824..5cf2fb165a56 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2029,16 +2029,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+ if (r == req) {
+ struct dwc3_request *t;
+
+- /*
+- * If a Setup packet is received but yet to DMA out, the controller will
+- * not process the End Transfer command of any endpoint. Polling of its
+- * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+- * timeout. Delay issuing the End Transfer command until the Setup TRB is
+- * prepared.
+- */
+- if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
+- dep->flags |= DWC3_EP_DELAY_STOP;
+-
+ /* wait until it is processed */
+ dwc3_stop_active_transfer(dep, true, true);
+
+@@ -3663,6 +3653,18 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ return;
+
++ /*
++ * If a Setup packet is received but yet to DMA out, the controller will
++ * not process the End Transfer command of any endpoint. Polling of its
++ * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
++ * timeout. Delay issuing the End Transfer command until the Setup TRB is
++ * prepared.
++ */
++ if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
++ dep->flags |= DWC3_EP_DELAY_STOP;
++ return;
++ }
++
+ /*
+ * NOTICE: We are violating what the Databook says about the
+ * EndTransfer command. Ideally we would _always_ wait for the
+--
+2.43.0
+
--- /dev/null
+From c0f4a91e640fa3781409bd37104482ccc3924a88 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 17 Aug 2022 11:23:52 -0700
+Subject: usb: dwc3: gadget: Force sending delayed status during soft
+ disconnect
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit e1ee843488d58099a89979627ef85d5bd6c5cacd ]
+
+If any function drivers request for a delayed status phase, this leads to a
+SETUP transfer timeout error, since the function may take longer to process
+the DATA stage. This eventually results in end transfer timeouts, as there
+is a pending SETUP transaction.
+
+In addition, allow the DWC3_EP_DELAY_STOP to be set for if there is a
+delayed status requested. Ocasionally, a host may abort the current SETUP
+transaction, by issuing a subsequent SETUP token. In those situations, it
+would result in an endxfer timeout as well.
+
+Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20220817182359.13550-3-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index b978e42ace75..ba1b2e70b351 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2470,6 +2470,9 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ int ret;
+
++ if (dwc->delayed_status)
++ dwc3_ep0_send_delayed_status(dwc);
++
+ reinit_completion(&dwc->ep0_in_setup);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+@@ -3662,7 +3665,7 @@ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ * timeout. Delay issuing the End Transfer command until the Setup TRB is
+ * prepared.
+ */
+- if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
++ if (dwc->ep0state != EP0_SETUP_PHASE) {
+ dep->flags |= DWC3_EP_DELAY_STOP;
+ return;
+ }
+--
+2.43.0
+
--- /dev/null
+From 0b4e713f7dab29713dfeb420e9db0223b89d849f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 6 Dec 2023 12:18:14 -0800
+Subject: usb: dwc3: gadget: Handle EP0 request dequeuing properly
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 730e12fbec53ab59dd807d981a204258a4cfb29a ]
+
+Current EP0 dequeue path will share the same as other EPs. However, there
+are some special considerations that need to be made for EP0 transfers:
+
+ - EP0 transfers never transition into the started_list
+ - EP0 only has one active request at a time
+
+In case there is a vendor specific control message for a function over USB
+FFS, then there is no guarantee on the timeline which the DATA/STATUS stage
+is responded to. While this occurs, any attempt to end transfers on
+non-control EPs will end up having the DWC3_EP_DELAY_STOP flag set, and
+defer issuing of the end transfer command. If the USB FFS application
+decides to timeout the control transfer, or if USB FFS AIO path exits, the
+USB FFS driver will issue a call to usb_ep_dequeue() for the ep0 request.
+
+In case of the AIO exit path, the AIO FS blocks until all pending USB
+requests utilizing the AIO path is completed. However, since the dequeue
+of ep0 req does not happen properly, all non-control EPs with the
+DWC3_EP_DELAY_STOP flag set will not be handled, and the AIO exit path will
+be stuck waiting for the USB FFS data endpoints to receive a completion
+callback.
+
+Fix is to utilize dwc3_ep0_reset_state() in the dequeue API to ensure EP0
+is brought back to the SETUP state, and ensures that any deferred end
+transfer commands are handled. This also will end any active transfers
+on EP0, compared to the previous implementation which directly called
+giveback only.
+
+Fixes: fcd2def66392 ("usb: dwc3: gadget: Refactor dwc3_gadget_ep_dequeue")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/20231206201814.32664-1-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 12 +++++++++++-
+ 1 file changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 7704e2444b4b..d472dab16889 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2039,7 +2039,17 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+
+ list_for_each_entry(r, &dep->pending_list, list) {
+ if (r == req) {
+- dwc3_gadget_giveback(dep, req, -ECONNRESET);
++ /*
++ * Explicitly check for EP0/1 as dequeue for those
++ * EPs need to be handled differently. Control EP
++ * only deals with one USB req, and giveback will
++ * occur during dwc3_ep0_stall_and_restart(). EP0
++ * requests are never added to started_list.
++ */
++ if (dep->number > 1)
++ dwc3_gadget_giveback(dep, req, -ECONNRESET);
++ else
++ dwc3_ep0_reset_state(dwc);
+ goto out;
+ }
+ }
+--
+2.43.0
+
--- /dev/null
+From b54260b8b5b4a85bbd1a4bc2846273d02f85522c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Apr 2022 19:22:57 -0700
+Subject: usb: dwc3: gadget: Only End Transfer for ep0 data phase
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit ace17b6ee4f92ab0375d12a1b42494f8590a96b6 ]
+
+The driver shouldn't be able to issue End Transfer to the control
+endpoint at anytime. Typically we should only do so in error cases such
+as invalid/unexpected direction of Data Phase as described in the
+control transfer flow of the programming guide. It _may_ end started
+data phase during controller deinitialization from soft disconnect or
+driver removal. However, that should not happen because the driver
+should be maintained in EP0_SETUP_PHASE during driver tear-down. On
+soft-connect, the controller should be reset from a soft-reset and there
+should be no issue starting the control endpoint.
+
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Link: https://lore.kernel.org/r/3c6643678863a26702e4115e9e19d7d94a30d49c.1650593829.git.Thinh.Nguyen@synopsys.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 2afe6784f1df..4d830ba7d824 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -3647,6 +3647,17 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
+ void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt)
+ {
++ struct dwc3 *dwc = dep->dwc;
++
++ /*
++ * Only issue End Transfer command to the control endpoint of a started
++ * Data Phase. Typically we should only do so in error cases such as
++ * invalid/unexpected direction as described in the control transfer
++ * flow of the programming guide.
++ */
++ if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE)
++ return;
++
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
+ (dep->flags & DWC3_EP_DELAY_STOP) ||
+ (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+--
+2.43.0
+
--- /dev/null
+From cd27b42ef09f2d914b0dd9d43f36e03f8681b88c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Jan 2024 13:49:46 -0800
+Subject: usb: dwc3: gadget: Queue PM runtime idle on disconnect event
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 3c7af52c7616c3aa6dacd2336ec748d4a65df8f4 ]
+
+There is a scenario where DWC3 runtime suspend is blocked due to the
+dwc->connected flag still being true while PM usage_count is zero after
+DWC3 giveback is completed and the USB gadget session is being terminated.
+This leads to a case where nothing schedules a PM runtime idle for the
+device.
+
+The exact condition is seen with the following sequence:
+ 1. USB bus reset is issued by the host
+ 2. Shortly after, or concurrently, a USB PD DR SWAP request is received
+ (sink->source)
+ 3. USB bus reset event handler runs and issues
+ dwc3_stop_active_transfers(), and pending transfer are stopped
+ 4. DWC3 usage_count decremented to 0, and runtime idle occurs while
+ dwc->connected == true, returns -EBUSY
+ 5. DWC3 disconnect event seen, dwc->connected set to false due to DR
+ swap handling
+ 6. No runtime idle after this point
+
+Address this by issuing an asynchronous PM runtime idle call after the
+disconnect event is completed, as it modifies the dwc->connected flag,
+which is what blocks the initial runtime idle.
+
+Fixes: fc8bb91bc83e ("usb: dwc3: implement runtime PM")
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20240103214946.2596-1-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index d472dab16889..8bd063f4eff2 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -3784,6 +3784,13 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+
+ dwc3_ep0_reset_state(dwc);
++
++ /*
++ * Request PM idle to address condition where usage count is
++ * already decremented to zero, but waiting for the disconnect
++ * interrupt to set dwc->connected to FALSE.
++ */
++ pm_request_idle(dwc->dev);
+ }
+
+ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+--
+2.43.0
+
--- /dev/null
+From 92460099926c88af8a8237322a3e18180a4bf039 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 20 Apr 2023 14:27:59 -0700
+Subject: usb: dwc3: gadget: Refactor EP0 forced stall/restart into a separate
+ API
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 8f40fc0808137c157dd408d2632e63bfca2aecdb ]
+
+Several sequences utilize the same routine for forcing the control endpoint
+back into the SETUP phase. This is required, because those operations need
+to ensure that EP0 is back in the default state.
+
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20230420212759.29429-3-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 53 ++++++++++++++++-----------------------
+ 1 file changed, 21 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index a74ac44c6cd8..7704e2444b4b 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -139,6 +139,24 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
+ return -ETIMEDOUT;
+ }
+
++static void dwc3_ep0_reset_state(struct dwc3 *dwc)
++{
++ unsigned int dir;
++
++ if (dwc->ep0state != EP0_SETUP_PHASE) {
++ dir = !!dwc->ep0_expect_in;
++ if (dwc->ep0state == EP0_DATA_PHASE)
++ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++ else
++ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++
++ dwc->eps[0]->trb_enqueue = 0;
++ dwc->eps[1]->trb_enqueue = 0;
++
++ dwc3_ep0_stall_and_restart(dwc);
++ }
++}
++
+ /**
+ * dwc3_ep_inc_trb - increment a trb index.
+ * @index: Pointer to the TRB index to increment.
+@@ -2495,16 +2513,9 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+ ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+ msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+ if (ret == 0) {
+- unsigned int dir;
+-
+ dev_warn(dwc->dev, "wait for SETUP phase timed out\n");
+ spin_lock_irqsave(&dwc->lock, flags);
+- dir = !!dwc->ep0_expect_in;
+- if (dwc->ep0state == EP0_DATA_PHASE)
+- dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+- else
+- dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+- dwc3_ep0_stall_and_restart(dwc);
++ dwc3_ep0_reset_state(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ }
+ }
+@@ -3762,16 +3773,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+ dwc->setup_packet_pending = false;
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+
+- if (dwc->ep0state != EP0_SETUP_PHASE) {
+- unsigned int dir;
+-
+- dir = !!dwc->ep0_expect_in;
+- if (dwc->ep0state == EP0_DATA_PHASE)
+- dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+- else
+- dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+- dwc3_ep0_stall_and_restart(dwc);
+- }
++ dwc3_ep0_reset_state(dwc);
+ }
+
+ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+@@ -3827,20 +3829,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+ * phase. So ensure that EP0 is in setup phase by issuing a stall
+ * and restart if EP0 is not in setup phase.
+ */
+- if (dwc->ep0state != EP0_SETUP_PHASE) {
+- unsigned int dir;
+-
+- dir = !!dwc->ep0_expect_in;
+- if (dwc->ep0state == EP0_DATA_PHASE)
+- dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+- else
+- dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+-
+- dwc->eps[0]->trb_enqueue = 0;
+- dwc->eps[1]->trb_enqueue = 0;
+-
+- dwc3_ep0_stall_and_restart(dwc);
+- }
++ dwc3_ep0_reset_state(dwc);
+
+ /*
+ * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
+--
+2.43.0
+
--- /dev/null
+From 620813673299aa87ddc65381506f42fb9fe1505b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 13 Apr 2023 12:57:40 -0700
+Subject: usb: dwc3: gadget: Stall and restart EP0 if host is unresponsive
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 02435a739b81ae24aff5d6e930efef9458e2af3c ]
+
+It was observed that there are hosts that may complete pending SETUP
+transactions before the stop active transfers and controller halt occurs,
+leading to lingering endxfer commands on DEPs on subsequent pullup/gadget
+start iterations.
+
+ dwc3_gadget_ep_disable name=ep8in flags=0x3009 direction=1
+ dwc3_gadget_ep_disable name=ep4in flags=1 direction=1
+ dwc3_gadget_ep_disable name=ep3out flags=1 direction=0
+ usb_gadget_disconnect deactivated=0 connected=0 ret=0
+
+The sequence shows that the USB gadget disconnect (dwc3_gadget_pullup(0))
+routine completed successfully, allowing for the USB gadget to proceed with
+a USB gadget connect. However, if this occurs the system runs into an
+issue where:
+
+ BUG: spinlock already unlocked on CPU
+ spin_bug+0x0
+ dwc3_remove_requests+0x278
+ dwc3_ep0_out_start+0xb0
+ __dwc3_gadget_start+0x25c
+
+This is due to the pending endxfers, leading to gadget start (w/o lock
+held) to execute the remove requests, which will unlock the dwc3
+spinlock as part of giveback.
+
+To mitigate this, resolve the pending endxfers on the pullup disable
+path by re-locating the SETUP phase check after stop active transfers, since
+that is where the DWC3_EP_DELAY_STOP is potentially set. This also allows
+for handling of a host that may be unresponsive by using the completion
+timeout to trigger the stall and restart for EP0.
+
+Fixes: c96683798e27 ("usb: dwc3: ep0: Don't prepare beyond Setup stage")
+Cc: stable@vger.kernel.org
+Acked-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20230413195742.11821-2-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 49 +++++++++++++++++++++++++--------------
+ 1 file changed, 32 insertions(+), 17 deletions(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index c61b6f49701a..a74ac44c6cd8 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -2459,29 +2459,17 @@ static int __dwc3_gadget_start(struct dwc3 *dwc);
+ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+ {
+ unsigned long flags;
++ int ret;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->connected = false;
+
+ /*
+- * Per databook, when we want to stop the gadget, if a control transfer
+- * is still in process, complete it and get the core into setup phase.
++ * Attempt to end pending SETUP status phase, and not wait for the
++ * function to do so.
+ */
+- if (dwc->ep0state != EP0_SETUP_PHASE) {
+- int ret;
+-
+- if (dwc->delayed_status)
+- dwc3_ep0_send_delayed_status(dwc);
+-
+- reinit_completion(&dwc->ep0_in_setup);
+-
+- spin_unlock_irqrestore(&dwc->lock, flags);
+- ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
+- msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+- spin_lock_irqsave(&dwc->lock, flags);
+- if (ret == 0)
+- dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
+- }
++ if (dwc->delayed_status)
++ dwc3_ep0_send_delayed_status(dwc);
+
+ /*
+ * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
+@@ -2494,6 +2482,33 @@ static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
+ __dwc3_gadget_stop(dwc);
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
++ /*
++ * Per databook, when we want to stop the gadget, if a control transfer
++ * is still in process, complete it and get the core into setup phase.
++ * In case the host is unresponsive to a SETUP transaction, forcefully
++ * stall the transfer, and move back to the SETUP phase, so that any
++ * pending endxfers can be executed.
++ */
++ if (dwc->ep0state != EP0_SETUP_PHASE) {
++ reinit_completion(&dwc->ep0_in_setup);
++
++ ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
++ msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
++ if (ret == 0) {
++ unsigned int dir;
++
++ dev_warn(dwc->dev, "wait for SETUP phase timed out\n");
++ spin_lock_irqsave(&dwc->lock, flags);
++ dir = !!dwc->ep0_expect_in;
++ if (dwc->ep0state == EP0_DATA_PHASE)
++ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++ else
++ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++ dwc3_ep0_stall_and_restart(dwc);
++ spin_unlock_irqrestore(&dwc->lock, flags);
++ }
++ }
++
+ /*
+ * Note: if the GEVNTCOUNT indicates events in the event buffer, the
+ * driver needs to acknowledge them before the controller can halt.
+--
+2.43.0
+
--- /dev/null
+From bdce6e89daa1411beecbe033786d22487f003c9a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 1 Sep 2022 12:36:25 -0700
+Subject: usb: dwc3: gadget: Submit endxfer command if delayed during
+ disconnect
+
+From: Wesley Cheng <quic_wcheng@quicinc.com>
+
+[ Upstream commit 8422b769fa46bd429dc0f324012629a4691f0dd9 ]
+
+During a cable disconnect sequence, if ep0state is not in the SETUP phase,
+then nothing will trigger any pending end transfer commands. Force
+stopping of any pending SETUP transaction, and move back to the SETUP
+phase.
+
+Reviewed-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20220901193625.8727-6-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/gadget.c | 13 ++++++++++++-
+ 1 file changed, 12 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index ba1b2e70b351..c61b6f49701a 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -3739,13 +3739,24 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
+ reg &= ~DWC3_DCTL_INITU2ENA;
+ dwc3_gadget_dctl_write_safe(dwc, reg);
+
++ dwc->connected = false;
++
+ dwc3_disconnect_gadget(dwc);
+
+ dwc->gadget->speed = USB_SPEED_UNKNOWN;
+ dwc->setup_packet_pending = false;
+ usb_gadget_set_state(dwc->gadget, USB_STATE_NOTATTACHED);
+
+- dwc->connected = false;
++ if (dwc->ep0state != EP0_SETUP_PHASE) {
++ unsigned int dir;
++
++ dir = !!dwc->ep0_expect_in;
++ if (dwc->ep0state == EP0_DATA_PHASE)
++ dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
++ else
++ dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
++ dwc3_ep0_stall_and_restart(dwc);
++ }
+ }
+
+ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
+--
+2.43.0
+
--- /dev/null
+From d5523dbba024bb8a98d2a06c0553fc92f6e4a90f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 9 Mar 2022 12:54:02 -0800
+Subject: usb: dwc3: gadget: Wait for ep0 xfers to complete during dequeue
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+
+[ Upstream commit e4cf6580ac740f766dae26203bd6311d353dcd42 ]
+
+If a Setup packet is received but yet to DMA out, the controller will
+not process the End Transfer command of any endpoint. Polling of its
+DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+command timeout.
+
+This may occur if the driver doesn’t service the completion interrupt of
+the control status stage yet due to system latency, then it won’t
+prepare TRB and start the transfer for the next Setup Stage. To the host
+side, the control transfer had completed, and the host can send a new
+Setup packet at this point.
+
+In the meanwhile, if the driver receives an async call to dequeue a
+request (triggering End Transfer) to any endpoint, then the driver will
+service that End transfer first, blocking the control status stage
+completion handler. Since no TRB is available for the Setup stage, the
+Setup packet can’t be DMA’ed out and the End Transfer gets hung.
+
+The driver must not block setting up of the Setup stage. So track and
+only issue the End Transfer command only when there’s Setup TRB prepared
+so that the controller can DMA out the Setup packet. Delay the End
+transfer command if there's no Setup TRB available. This is applicable to
+all DWC_usb3x IPs.
+
+Co-developed-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Signed-off-by: Thinh Nguyen <Thinh.Nguyen@synopsys.com>
+Signed-off-by: Wesley Cheng <quic_wcheng@quicinc.com>
+Link: https://lore.kernel.org/r/20220309205402.4467-1-quic_wcheng@quicinc.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 730e12fbec53 ("usb: dwc3: gadget: Handle EP0 request dequeuing properly")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/usb/dwc3/core.h | 1 +
+ drivers/usb/dwc3/ep0.c | 14 ++++++++++++++
+ drivers/usb/dwc3/gadget.c | 20 +++++++++++++++-----
+ drivers/usb/dwc3/gadget.h | 1 +
+ 4 files changed, 31 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
+index 3dcb5b744f7c..d64f7edc70c1 100644
+--- a/drivers/usb/dwc3/core.h
++++ b/drivers/usb/dwc3/core.h
+@@ -722,6 +722,7 @@ struct dwc3_ep {
+ #define DWC3_EP_FIRST_STREAM_PRIMED BIT(10)
+ #define DWC3_EP_PENDING_CLEAR_STALL BIT(11)
+ #define DWC3_EP_TXFIFO_RESIZED BIT(12)
++#define DWC3_EP_DELAY_STOP BIT(13)
+
+ /* This last one is specific to EP0 */
+ #define DWC3_EP0_DIR_IN BIT(31)
+diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
+index 52f2bfae46bc..34cb8662e129 100644
+--- a/drivers/usb/dwc3/ep0.c
++++ b/drivers/usb/dwc3/ep0.c
+@@ -274,6 +274,7 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
+ {
+ struct dwc3_ep *dep;
+ int ret;
++ int i;
+
+ complete(&dwc->ep0_in_setup);
+
+@@ -282,6 +283,19 @@ void dwc3_ep0_out_start(struct dwc3 *dwc)
+ DWC3_TRBCTL_CONTROL_SETUP, false);
+ ret = dwc3_ep0_start_trans(dep);
+ WARN_ON(ret < 0);
++ for (i = 2; i < DWC3_ENDPOINTS_NUM; i++) {
++ struct dwc3_ep *dwc3_ep;
++
++ dwc3_ep = dwc->eps[i];
++ if (!dwc3_ep)
++ continue;
++
++ if (!(dwc3_ep->flags & DWC3_EP_DELAY_STOP))
++ continue;
++
++ dwc3_ep->flags &= ~DWC3_EP_DELAY_STOP;
++ dwc3_stop_active_transfer(dwc3_ep, true, true);
++ }
+ }
+
+ static struct dwc3_ep *dwc3_wIndex_to_dep(struct dwc3 *dwc, __le16 wIndex_le)
+diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
+index 6188193e5ef4..3a663d71d791 100644
+--- a/drivers/usb/dwc3/gadget.c
++++ b/drivers/usb/dwc3/gadget.c
+@@ -641,9 +641,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
+ return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms);
+ }
+
+-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+- bool interrupt);
+-
+ /**
+ * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
+ * @dwc: pointer to the DWC3 context
+@@ -1891,6 +1888,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
+ */
+ if ((dep->flags & DWC3_EP_END_TRANSFER_PENDING) ||
+ (dep->flags & DWC3_EP_WEDGE) ||
++ (dep->flags & DWC3_EP_DELAY_STOP) ||
+ (dep->flags & DWC3_EP_STALL)) {
+ dep->flags |= DWC3_EP_DELAY_START;
+ return 0;
+@@ -2031,6 +2029,16 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
+ if (r == req) {
+ struct dwc3_request *t;
+
++ /*
++ * If a Setup packet is received but yet to DMA out, the controller will
++ * not process the End Transfer command of any endpoint. Polling of its
++ * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
++ * timeout. Delay issuing the End Transfer command until the Setup TRB is
++ * prepared.
++ */
++ if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
++ dep->flags |= DWC3_EP_DELAY_STOP;
++
+ /* wait until it is processed */
+ dwc3_stop_active_transfer(dep, true, true);
+
+@@ -2114,7 +2122,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
+ list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+ dwc3_gadget_move_cancelled_request(req, DWC3_REQUEST_STATUS_STALLED);
+
+- if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
++ if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
++ (dep->flags & DWC3_EP_DELAY_STOP)) {
+ dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
+ return 0;
+ }
+@@ -3630,10 +3639,11 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
+ }
+ }
+
+-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
++void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+ bool interrupt)
+ {
+ if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
++ (dep->flags & DWC3_EP_DELAY_STOP) ||
+ (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+ return;
+
+diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h
+index 77df4b6d6c13..f763380e672e 100644
+--- a/drivers/usb/dwc3/gadget.h
++++ b/drivers/usb/dwc3/gadget.h
+@@ -116,6 +116,7 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
+ gfp_t gfp_flags);
+ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol);
+ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc);
++void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force, bool interrupt);
+
+ /**
+ * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
+--
+2.43.0
+
--- /dev/null
+From 307f5bd1d45e51f1e0899ef19771b7ecb1b59f9c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 9 Dec 2023 07:40:29 +0800
+Subject: wifi: mwifiex: add extra delay for firmware ready
+
+From: David Lin <yu-hao.lin@nxp.com>
+
+[ Upstream commit 1c5d463c0770c6fa2037511a24fb17966fd07d97 ]
+
+For SDIO IW416, due to a bug, FW may return ready before complete full
+initialization. Command timeout may occur at driver load after reboot.
+Workaround by adding 100ms delay at checking FW status.
+
+Signed-off-by: David Lin <yu-hao.lin@nxp.com>
+Cc: stable@vger.kernel.org
+Reviewed-by: Francesco Dolcini <francesco.dolcini@toradex.com>
+Acked-by: Brian Norris <briannorris@chromium.org>
+Tested-by: Marcel Ziswiler <marcel.ziswiler@toradex.com> # Verdin AM62 (IW416)
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://msgid.link/20231208234029.2197-1-yu-hao.lin@nxp.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sdio.c | 19 +++++++++++++++++++
+ drivers/net/wireless/marvell/mwifiex/sdio.h | 2 ++
+ 2 files changed, 21 insertions(+)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index 919f1bae61dc..dd4bfb7d71ee 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -343,6 +343,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8786 = {
+ .can_dump_fw = false,
+ .can_auto_tdls = false,
+ .can_ext_scan = false,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
+@@ -358,6 +359,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8787 = {
+ .can_dump_fw = false,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
+@@ -373,6 +375,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8797 = {
+ .can_dump_fw = false,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+@@ -388,6 +391,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8897 = {
+ .can_dump_fw = true,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
+@@ -404,6 +408,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
+ .fw_dump_enh = true,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8978 = {
+@@ -420,6 +425,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8978 = {
+ .fw_dump_enh = true,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = true,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
+@@ -436,6 +442,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
+ .fw_dump_enh = true,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
+@@ -451,6 +458,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
+ .can_dump_fw = false,
+ .can_auto_tdls = true,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = {
+@@ -467,6 +475,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = {
+ .fw_dump_enh = true,
+ .can_auto_tdls = true,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = {
+@@ -482,6 +491,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8801 = {
+ .can_dump_fw = false,
+ .can_auto_tdls = false,
+ .can_ext_scan = true,
++ .fw_ready_extra_delay = false,
+ };
+
+ static struct memory_type_mapping generic_mem_type_map[] = {
+@@ -574,6 +584,7 @@ mwifiex_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
+ card->fw_dump_enh = data->fw_dump_enh;
+ card->can_auto_tdls = data->can_auto_tdls;
+ card->can_ext_scan = data->can_ext_scan;
++ card->fw_ready_extra_delay = data->fw_ready_extra_delay;
+ INIT_WORK(&card->work, mwifiex_sdio_work);
+ }
+
+@@ -777,6 +788,7 @@ mwifiex_sdio_read_fw_status(struct mwifiex_adapter *adapter, u16 *dat)
+ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
+ u32 poll_num)
+ {
++ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+ u16 firmware_stat;
+ u32 tries;
+@@ -794,6 +806,13 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
+ ret = -1;
+ }
+
++ if (card->fw_ready_extra_delay &&
++ firmware_stat == FIRMWARE_READY_SDIO)
++ /* firmware might pretend to be ready, when it's not.
++ * Wait a little bit more as a workaround.
++ */
++ msleep(100);
++
+ return ret;
+ }
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
+index e9a9de566cc8..8f11fed8eae9 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
+@@ -269,6 +269,7 @@ struct sdio_mmc_card {
+ bool fw_dump_enh;
+ bool can_auto_tdls;
+ bool can_ext_scan;
++ bool fw_ready_extra_delay;
+
+ struct mwifiex_sdio_mpa_tx mpa_tx;
+ struct mwifiex_sdio_mpa_rx mpa_rx;
+@@ -292,6 +293,7 @@ struct mwifiex_sdio_device {
+ bool fw_dump_enh;
+ bool can_auto_tdls;
+ bool can_ext_scan;
++ bool fw_ready_extra_delay;
+ };
+
+ /*
+--
+2.43.0
+
--- /dev/null
+From e8342bec9354a4d2492c1f8dca1c564065906a33 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 21 Dec 2023 09:55:11 +0800
+Subject: wifi: mwifiex: fix uninitialized firmware_stat
+
+From: David Lin <yu-hao.lin@nxp.com>
+
+[ Upstream commit 3df95e265924ac898c1a38a0c01846dd0bd3b354 ]
+
+Variable firmware_stat is possible to be used without initialization.
+
+Signed-off-by: David Lin <yu-hao.lin@nxp.com>
+Fixes: 1c5d463c0770 ("wifi: mwifiex: add extra delay for firmware ready")
+Cc: stable@vger.kernel.org
+Reported-by: kernel test robot <lkp@intel.com>
+Reported-by: Dan Carpenter <error27@gmail.com>
+Closes: https://lore.kernel.org/r/202312192236.ZflaWYCw-lkp@intel.com/
+Acked-by: Brian Norris <briannorris@chromium.org>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://msgid.link/20231221015511.1032128-1-yu-hao.lin@nxp.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/marvell/mwifiex/sdio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index dd4bfb7d71ee..45f46a445a6c 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -790,7 +790,7 @@ static int mwifiex_check_fw_status(struct mwifiex_adapter *adapter,
+ {
+ struct sdio_mmc_card *card = adapter->card;
+ int ret = 0;
+- u16 firmware_stat;
++ u16 firmware_stat = 0;
+ u32 tries;
+
+ for (tries = 0; tries < poll_num; tries++) {
+--
+2.43.0
+
--- /dev/null
+From 444a1f89cee2117c4524c52fb57d2a70f40d89bc Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 Jan 2023 15:02:00 +0100
+Subject: wifi: mwifiex: Support SD8978 chipset
+
+From: Lukas Wunner <lukas@wunner.de>
+
+[ Upstream commit bba047f15851c8b053221f1b276eb7682d59f755 ]
+
+The Marvell SD8978 (aka NXP IW416) uses identical registers as SD8987,
+so reuse the existing mwifiex_reg_sd8987 definition.
+
+Note that mwifiex_reg_sd8977 and mwifiex_reg_sd8997 are likewise
+identical, save for the fw_dump_ctrl register: They define it as 0xf0
+whereas mwifiex_reg_sd8987 defines it as 0xf9. I've verified that
+0xf9 is the correct value on SD8978. NXP's out-of-tree driver uses
+0xf9 for all of them, so there's a chance that 0xf0 is not correct
+in the mwifiex_reg_sd8977 and mwifiex_reg_sd8997 definitions. I cannot
+test that for lack of hardware, hence am leaving it as is.
+
+NXP has only released a firmware which runs Bluetooth over UART.
+Perhaps Bluetooth over SDIO is unsupported by this chipset.
+Consequently, only an "sdiouart" firmware image is referenced, not an
+alternative "sdsd" image.
+
+Signed-off-by: Lukas Wunner <lukas@wunner.de>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/536b4f17a72ca460ad1b07045757043fb0778988.1674827105.git.lukas@wunner.de
+Stable-dep-of: 1c5d463c0770 ("wifi: mwifiex: add extra delay for firmware ready")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../bindings/net/wireless/marvell-8xxx.txt | 4 ++-
+ drivers/net/wireless/marvell/mwifiex/Kconfig | 5 ++--
+ drivers/net/wireless/marvell/mwifiex/sdio.c | 25 +++++++++++++++++--
+ drivers/net/wireless/marvell/mwifiex/sdio.h | 1 +
+ include/linux/mmc/sdio_ids.h | 1 +
+ 5 files changed, 31 insertions(+), 5 deletions(-)
+
+diff --git a/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt b/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt
+index 9bf9bbac16e2..cdc303caf5f4 100644
+--- a/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt
++++ b/Documentation/devicetree/bindings/net/wireless/marvell-8xxx.txt
+@@ -1,4 +1,4 @@
+-Marvell 8787/8897/8997 (sd8787/sd8897/sd8997/pcie8997) SDIO/PCIE devices
++Marvell 8787/8897/8978/8997 (sd8787/sd8897/sd8978/sd8997/pcie8997) SDIO/PCIE devices
+ ------
+
+ This node provides properties for controlling the Marvell SDIO/PCIE wireless device.
+@@ -10,7 +10,9 @@ Required properties:
+ - compatible : should be one of the following:
+ * "marvell,sd8787"
+ * "marvell,sd8897"
++ * "marvell,sd8978"
+ * "marvell,sd8997"
++ * "nxp,iw416"
+ * "pci11ab,2b42"
+ * "pci1b4b,2b42"
+
+diff --git a/drivers/net/wireless/marvell/mwifiex/Kconfig b/drivers/net/wireless/marvell/mwifiex/Kconfig
+index 2b4ff2b78a7e..b182f7155d66 100644
+--- a/drivers/net/wireless/marvell/mwifiex/Kconfig
++++ b/drivers/net/wireless/marvell/mwifiex/Kconfig
+@@ -10,13 +10,14 @@ config MWIFIEX
+ mwifiex.
+
+ config MWIFIEX_SDIO
+- tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8977/SD8987/SD8997"
++ tristate "Marvell WiFi-Ex Driver for SD8786/SD8787/SD8797/SD8887/SD8897/SD8977/SD8978/SD8987/SD8997"
+ depends on MWIFIEX && MMC
+ select FW_LOADER
+ select WANT_DEV_COREDUMP
+ help
+ This adds support for wireless adapters based on Marvell
+- 8786/8787/8797/8887/8897/8977/8987/8997 chipsets with SDIO interface.
++ 8786/8787/8797/8887/8897/8977/8978/8987/8997 chipsets with
++ SDIO interface. SD8978 is also known as NXP IW416.
+
+ If you choose to build it as a module, it will be called
+ mwifiex_sdio.
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
+index 016065a56e6c..919f1bae61dc 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
+@@ -275,7 +275,7 @@ static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8887 = {
+ 0x68, 0x69, 0x6a},
+ };
+
+-static const struct mwifiex_sdio_card_reg mwifiex_reg_sd8987 = {
++static const struct mwifiex_sdio_card_reg mwifiex_reg_sd89xx = {
+ .start_rd_port = 0,
+ .start_wr_port = 0,
+ .base_0_reg = 0xF8,
+@@ -406,6 +406,22 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8977 = {
+ .can_ext_scan = true,
+ };
+
++static const struct mwifiex_sdio_device mwifiex_sdio_sd8978 = {
++ .firmware_sdiouart = SD8978_SDIOUART_FW_NAME,
++ .reg = &mwifiex_reg_sd89xx,
++ .max_ports = 32,
++ .mp_agg_pkt_limit = 16,
++ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_4K,
++ .mp_tx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
++ .mp_rx_agg_buf_size = MWIFIEX_MP_AGGR_BUF_SIZE_MAX,
++ .supports_sdio_new_mode = true,
++ .has_control_mask = false,
++ .can_dump_fw = true,
++ .fw_dump_enh = true,
++ .can_auto_tdls = false,
++ .can_ext_scan = true,
++};
++
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8997 = {
+ .firmware = SD8997_DEFAULT_FW_NAME,
+ .reg = &mwifiex_reg_sd8997,
+@@ -439,7 +455,7 @@ static const struct mwifiex_sdio_device mwifiex_sdio_sd8887 = {
+
+ static const struct mwifiex_sdio_device mwifiex_sdio_sd8987 = {
+ .firmware = SD8987_DEFAULT_FW_NAME,
+- .reg = &mwifiex_reg_sd8987,
++ .reg = &mwifiex_reg_sd89xx,
+ .max_ports = 32,
+ .mp_agg_pkt_limit = 16,
+ .tx_buf_size = MWIFIEX_TX_DATA_BUF_SIZE_2K,
+@@ -493,7 +509,9 @@ static struct memory_type_mapping mem_type_mapping_tbl[] = {
+ static const struct of_device_id mwifiex_sdio_of_match_table[] __maybe_unused = {
+ { .compatible = "marvell,sd8787" },
+ { .compatible = "marvell,sd8897" },
++ { .compatible = "marvell,sd8978" },
+ { .compatible = "marvell,sd8997" },
++ { .compatible = "nxp,iw416" },
+ { }
+ };
+
+@@ -931,6 +949,8 @@ static const struct sdio_device_id mwifiex_ids[] = {
+ .driver_data = (unsigned long)&mwifiex_sdio_sd8801},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8977_WLAN),
+ .driver_data = (unsigned long)&mwifiex_sdio_sd8977},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8978_WLAN),
++ .driver_data = (unsigned long)&mwifiex_sdio_sd8978},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8987_WLAN),
+ .driver_data = (unsigned long)&mwifiex_sdio_sd8987},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8997_WLAN),
+@@ -3175,5 +3195,6 @@ MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8887_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8977_DEFAULT_FW_NAME);
++MODULE_FIRMWARE(SD8978_SDIOUART_FW_NAME);
+ MODULE_FIRMWARE(SD8987_DEFAULT_FW_NAME);
+ MODULE_FIRMWARE(SD8997_DEFAULT_FW_NAME);
+diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h
+index ad2c28cbb630..e9a9de566cc8 100644
+--- a/drivers/net/wireless/marvell/mwifiex/sdio.h
++++ b/drivers/net/wireless/marvell/mwifiex/sdio.h
+@@ -37,6 +37,7 @@
+ #define SD8887_DEFAULT_FW_NAME "mrvl/sd8887_uapsta.bin"
+ #define SD8801_DEFAULT_FW_NAME "mrvl/sd8801_uapsta.bin"
+ #define SD8977_DEFAULT_FW_NAME "mrvl/sdsd8977_combo_v2.bin"
++#define SD8978_SDIOUART_FW_NAME "mrvl/sdiouartiw416_combo_v0.bin"
+ #define SD8987_DEFAULT_FW_NAME "mrvl/sd8987_uapsta.bin"
+ #define SD8997_DEFAULT_FW_NAME "mrvl/sdsd8997_combo_v4.bin"
+
+diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
+index a85c9f0bd470..d1524c5e49a1 100644
+--- a/include/linux/mmc/sdio_ids.h
++++ b/include/linux/mmc/sdio_ids.h
+@@ -101,6 +101,7 @@
+ #define SDIO_DEVICE_ID_MARVELL_8977_BT 0x9146
+ #define SDIO_DEVICE_ID_MARVELL_8987_WLAN 0x9149
+ #define SDIO_DEVICE_ID_MARVELL_8987_BT 0x914a
++#define SDIO_DEVICE_ID_MARVELL_8978_WLAN 0x9159
+
+ #define SDIO_VENDOR_ID_MEDIATEK 0x037a
+ #define SDIO_DEVICE_ID_MEDIATEK_MT7663 0x7663
+--
+2.43.0
+