diff --git a/core/linux-sun4i/0001-Backport-firmware-loader.patch b/core/linux-sun4i/0001-Backport-firmware-loader.patch
new file mode 100644
index 000000000..f340ebda7
--- /dev/null
+++ b/core/linux-sun4i/0001-Backport-firmware-loader.patch
@@ -0,0 +1,2620 @@
+From c1715f7de4f2b7c42f432d4a7eb02f3f954d34ca Mon Sep 17 00:00:00 2001
+From: Kevin Mihelich <kevin@archlinuxarm.org>
+Date: Sat, 6 Dec 2014 10:44:46 -0700
+Subject: [PATCH] Backport firmware loader
+
+commit 390ac45ceb37d70308194820e25b1cb885b67260
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Fri Aug 17 22:06:59 2012 +0800
+
+    PM / Sleep: introduce dpm_for_each_dev
+
+    dpm_list and its pm lock provide a good way to iterate all
+    devices in system. Except this way, there is no other easy
+    way to iterate devices in system.
+
+    firmware loader need to cache firmware images for devices
+    before system sleep, so introduce the function to meet its
+    demand.
+
+    Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 1dc17a9704a49c008a8d4cd8bdf03eccc54bcf84
+Author: Kevin Mihelich <kevin@archlinuxarm.org>
+Date:   Wed Nov 26 13:03:08 2014 -0700
+
+    Revert "firmware loader: sync firmware cache by async_synchronize_full_domain"
+
+    This reverts commit d28d3882bd1fdb88ae4e02f11b091a92b0e5068b.
+
+commit 6cf1b1b3016fb279d6a866151e75c94986be032b
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:26 2012 +0800
+
+    driver core: devres: introduce devres_for_each_res
+
+    This patch introduces one devres API of devres_for_each_res
+    so that the device's driver can iterate each resource it has
+    interest in.
+
+    The firmware loader will use the API to get each firmware name
+    from the device instance.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 9373da78351d15876f7508734147c25fa018f512
+Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date:   Thu May 3 18:15:13 2012 +0100
+
+    devres: Add devres_release()
+
+    APIs using devres frequently want to implement a "remove and free the
+    resource" operation so it seems sensible that they should be able to
+    just have devres do the freeing for them since that's a big part of what
+    devres is all about.
+
+    Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 6183261d20682701b6ddd189060da4f8325e775c
+Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Date:   Thu May 3 18:15:12 2012 +0100
+
+    devres: Clarify documentation for devres_destroy()
+
+    It's not massively obvious (at least to me) that removing and freeing a
+    resource does not involve calling the release function for the resource
+    but rather only removes the management of it. Make the documentation more
+    explicit.
+
+    Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit bf4725b4926af17c246faf3b6dda250890b3e1ae
+Author: Takashi Iwai <tiwai@suse.de>
+Date:   Thu Jan 31 11:13:57 2013 +0100
+
+    firmware: Ignore abort check when no user-helper is used
+
+    FW_STATUS_ABORT can be set only during the user-helper invocation,
+    thus we can ignore the check when CONFIG_HW_LOADER_USER_HELPER is
+    disabled.
+
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Takashi Iwai <tiwai@suse.de>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 5d5c8b23919037f575fb6370d87b875c86793834
+Author: Takashi Iwai <tiwai@suse.de>
+Date:   Thu Jan 31 11:13:56 2013 +0100
+
+    firmware: Reduce ifdef CONFIG_FW_LOADER_USER_HELPER
+
+    By shuffling the code, reduce a few ifdefs in firmware_class.c.
+    Also, firmware_buf fmt field is changed to is_pages_buf boolean for
+    simplification.
+
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Takashi Iwai <tiwai@suse.de>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 861d2fc3539af167b5c4c9e81b5bf67300465cef
+Author: Takashi Iwai <tiwai@suse.de>
+Date:   Thu Jan 31 11:13:55 2013 +0100
+
+    firmware: Make user-mode helper optional
+
+    This patch adds a new kconfig, CONFIG_FW_LOADER_USER_HELPER, and
+    guards the user-helper codes in firmware_class.c with ifdefs.
+
+    Yeah, yeah, there are lots of ifdefs in this patch.  The further
+    clean-up with code shuffling follows in the next.
+
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Takashi Iwai <tiwai@suse.de>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit eb2e7c184b7e7bf2ae1968bd32c4354d28c75e69
+Author: Takashi Iwai <tiwai@suse.de>
+Date:   Thu Jan 31 11:13:54 2013 +0100
+
+    firmware: Refactoring for splitting user-mode helper code
+
+    Since 3.7 kernel, the firmware loader can read the firmware files
+    directly, and the traditional user-mode helper is invoked only as a
+    fallback.  This seems working pretty well, and the next step would be
+    to reduce the redundant user-mode helper stuff in future.
+
+    This patch is a preparation for that: refactor the code for splitting
+    user-mode helper stuff more easily.  No functional change.
+
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Takashi Iwai <tiwai@suse.de>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 542061e36122107363fcf7c9aeffa957dee8bffc
+Author: Luciano Coelho <coelho@ti.com>
+Date:   Tue Jan 15 10:43:43 2013 +0200
+
+    firmware: make sure the fw file size is not 0
+
+    If the requested firmware file size is 0 bytes in the filesytem, we
+    will try to vmalloc(0), which causes a warning:
+
+      vmalloc: allocation failure: 0 bytes
+      kworker/1:1: page allocation failure: order:0, mode:0xd2
+        __vmalloc_node_range+0x164/0x208
+        __vmalloc_node+0x4c/0x58
+        vmalloc+0x38/0x44
+        _request_firmware_load+0x220/0x6b0
+        request_firmware+0x64/0xc8
+        wl18xx_setup+0xb4/0x570 [wl18xx]
+        wlcore_nvs_cb+0x64/0x9f8 [wlcore]
+        request_firmware_work_func+0x94/0x100
+        process_one_work+0x1d0/0x750
+        worker_thread+0x184/0x4ac
+        kthread+0xb4/0xc0
+
+    To fix this, check whether the file size is less than or equal to zero
+    in fw_read_file_contents().
+
+    Cc: stable <stable@vger.kernel.org> [3.7]
+    Signed-off-by: Luciano Coelho <coelho@ti.com>
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+
+commit 906aa734d888dfeb855d50ebdaa1abafb4e16880
+Author: Ming Lei <tom.leiming@gmail.com>
+Date:   Sat Nov 3 17:48:16 2012 +0800
+
+    firmware loader: document firmware cache mechanism
+
+    This patch documents the firmware cache mechanism so that
+    users of request_firmware() know that it can be called
+    safely inside device's suspend and resume callback, and
+    the device's firmware needn't be cached any more by individual
+    driver itself to deal with firmware loss during system resume.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 2a2f0be2bb74403c0079a823271e6ff7c2434735
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Nov 3 17:47:58 2012 +0800
+
+    firmware loader: introduce module parameter to customize(v4) fw search path
+
+    This patch introduces one module parameter of 'path' in firmware_class
+    to support customizing firmware image search path, so that people can
+    use its own firmware path if the default built-in paths can't meet their
+    demand[1], and the typical usage is passing the below from kernel command
+    parameter when 'firmware_class' is built in kernel:
+
+    	firmware_class.path=$CUSTOMIZED_PATH
+
+    [1], https://lkml.org/lkml/2012/10/11/337
+
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+    Conflicts:
+    	Documentation/firmware_class/README
+
+commit d6c6925a1c5091163df48d68ba38d2ec2003302e
+Author: Cesar Eduardo Barros <cesarb@cesarb.net>
+Date:   Sat Oct 27 20:37:10 2012 -0200
+
+    firmware: use noinline_for_stack
+
+    The comment above fw_file_size() suggests it is noinline for stack size
+    reasons. Use noinline_for_stack to make this more clear.
+
+    Signed-off-by: Cesar Eduardo Barros <cesarb@cesarb.net>
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit f0d798c22f43f008b0b86ff58f2c68b522f1be90
+Author: Chuansheng Liu <chuansheng.liu@intel.com>
+Date:   Sat Nov 10 01:27:22 2012 +0800
+
+    firmware loader: Fix the concurrent request_firmware() race for kref_get/put
+
+    There is one race that both request_firmware() with the same
+    firmware name.
+
+    The race scenerio is as below:
+    CPU1                                                  CPU2
+    request_firmware() -->
+    _request_firmware_load() return err                   another request_firmware() is coming -->
+    _request_firmware_cleanup is called -->               _request_firmware_prepare -->
+    release_firmware --->                                 fw_lookup_and_allocate_buf -->
+                                                          spin_lock(&fwc->lock)
+    ...                                                   __fw_lookup_buf() return true
+    fw_free_buf() will be called -->                      ...
+    kref_put -->
+    decrease the refcount to 0
+                                                          kref_get(&tmp->ref) ==> it will trigger warning
+                                                                                  due to refcount == 0
+    __fw_free_buf() -->
+    ...                                                   spin_unlock(&fwc->lock)
+    spin_lock(&fwc->lock)
+    list_del(&buf->list)
+    spin_unlock(&fwc->lock)
+    kfree(buf)
+                                                          After that, the freed buf will be used.
+
+    The key race is decreasing refcount to 0 and list_del is not protected together by
+    fwc->lock, and it is possible another thread try to get it between refcount==0
+    and list_del.
+
+    Fix it here to protect it together.
+
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: liu chuansheng <chuansheng.liu@intel.com>
+    Cc: stable <stable@vger.kernel.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 58af61921c9d1f6a8ffa097043bf78a2ff0bc44e
+Author: Chuansheng Liu <chuansheng.liu@intel.com>
+Date:   Thu Nov 8 19:14:40 2012 +0800
+
+    firmware loader: Fix the race FW_STATUS_DONE is followed by class_timeout
+
+    There is a race as below when calling request_firmware():
+    CPU1                                   CPU2
+    write 0 > loading
+    mutex_lock(&fw_lock)
+    ...
+    set_bit FW_STATUS_DONE                 class_timeout is coming
+                                           set_bit FW_STATUS_ABORT
+    complete_all &completion
+    ...
+    mutex_unlock(&fw_lock)
+
+    In this time, the bit FW_STATUS_DONE and FW_STATUS_ABORT are set,
+    and request_firmware() will return failure due to condition in
+    _request_firmware_load():
+    	if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status))
+    		retval = -ENOENT;
+
+    But from the above scenerio, it should be a successful requesting.
+    So we need judge if the bit FW_STATUS_DONE is already set before
+    calling fw_load_abort() in timeout function.
+
+    As Ming's proposal, we need change the timer into sched_work to
+    benefit from using &fw_lock mutex also.
+
+    Signed-off-by: liu chuansheng <chuansheng.liu@intel.com>
+    Acked-by: Ming Lei <ming.lei@canonical.com>
+    Cc: stable <stable@vger.kernel.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit c6a3b74d6c42ca8aead23788768cdd358f672ed3
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Tue Oct 9 12:01:04 2012 +0800
+
+    firmware loader: sync firmware cache by async_synchronize_full_domain
+
+    async.c has provided synchronization mechanism on async_schedule_*,
+    so use async_synchronize_full_domain to sync caching firmware instead
+    of reinventing the wheel.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 9a27bd527d58a887a65bad24fd864122f860ef79
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Tue Oct 9 12:01:03 2012 +0800
+
+    firmware loader: let direct loading back on 'firmware_buf'
+
+    Firstly 'firmware_buf' is introduced to make all loading requests
+    to share one firmware kernel buffer, so firmware_buf should
+    be used in direct loading for saving memory and speedup firmware
+    loading.
+
+    Secondly, the commit below
+
+    	abb139e75c2cdbb955e840d6331cb5863e409d0e(firmware:teach
+    	the kernel to load firmware files directly from the filesystem)
+
+    introduces direct loading for fixing udev regression, but it
+    bypasses the firmware cache meachnism, so this patch enables
+    caching firmware for direct loading case since it is still needed
+    to solve drivers' dependency during system resume.
+
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 451768e107b4bd2beb054f3ec44b666910a8f6ce
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Tue Oct 9 12:01:02 2012 +0800
+
+    firmware loader: fix one reqeust_firmware race
+
+    Several loading requests may be pending on one same
+    firmware buf, and this patch moves fw_map_pages_buf()
+    before complete_all(&fw_buf->completion) and let all
+    requests see the mapped 'buf->data' once the loading
+    is completed.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 31159a04875c46fe2f8634479f5c2c07b3ebd6aa
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Tue Oct 9 12:01:01 2012 +0800
+
+    firmware loader: cancel uncache work before caching firmware
+
+    Under 'Opportunistic sleep' situation, system sleep might be
+    triggered very frequently, so the uncahce work may not be completed
+    before caching firmware during next suspend.
+
+    This patch cancels the uncache work before caching firmware to
+    fix the problem above.
+
+    Also this patch optimizes the cacheing firmware mechanism a bit by
+    only storing one firmware cache entry for one firmware image.
+
+    So if the firmware is still cached during suspend, it doesn't need
+    to be loaded from user space any more.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 4c7559d930c34c76a33b51f413c13f27367e95ff
+Author: Linus Torvalds <torvalds@linux-foundation.org>
+Date:   Thu Oct 4 09:19:02 2012 -0700
+
+    firmware: use 'kernel_read()' to read firmware into kernel buffer
+
+    Fengguang correctly points out that the firmware reading should not use
+    vfs_read(), since the buffer is in kernel space.
+
+    The vfs_read() just happened to work for kernel threads, but sparse
+    warns about the incorrect address spaces, and it's definitely incorrect
+    and could fail for other users of the firmware loading.
+
+    Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+
+commit 2047dd6d95ac6066e518c26cb5133f4787aefb52
+Author: Linus Torvalds <torvalds@linux-foundation.org>
+Date:   Wed Oct 3 15:58:32 2012 -0700
+
+    firmware: teach the kernel to load firmware files directly from the filesystem
+
+    This is a first step in allowing people to by-pass udev for loading
+    device firmware.  Current versions of udev will deadlock (causing us to
+    block for the 30 second timeout) under some circumstances if the
+    firmware is loaded as part of the module initialization path, and this
+    is causing problems for media drivers in particular.
+
+    The current patch hardcodes the firmware path that udev uses by default,
+    and will fall back to the legacy udev mode if the firmware cannot be
+    found there.  We'd like to add support for both configuring the paths
+    and the fallback behaviour, but in the meantime this hopefully fixes the
+    immediate problem, while also giving us a way forward.
+
+    [ v2: Some VFS layer interface cleanups suggested by Al Viro ]
+    [ v3: use the default udev paths suggested by Kay Sievers ]
+
+    Suggested-by: Ivan Kalvachev <ikalvachev@gmail.com>
+    Acked-by: Greg KH <gregkh@linuxfoundation.org>
+    Acked-by: Al Viro <viro@zeniv.linux.org.uk>
+    Cc: Mauro Carvalho Chehab <mchehab@redhat.com>
+    Cc: Kay Sievers <kay@redhat.com>
+    Cc: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+
+commit 7dc9f6ac529be96085a096c440823fe49f95f1cd
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Sep 8 17:32:30 2012 +0800
+
+    firmware loader: fix compile warning when CONFIG_PM=n
+
+    This patch replaces the previous macro of CONFIG_PM with
+    CONFIG_PM_SLEEP becasue firmware cache is only used in
+    system sleep situations.
+
+    Also this patch fixes the below compile warning when
+    CONFIG_PM=n:
+
+    	drivers/base/firmware_class.c:1147: warning: 'device_cache_fw_images'
+    	defined but not used
+    	drivers/base/firmware_class.c:1212: warning:
+    	'device_uncache_fw_images_delay' defined but not used
+
+    Reported-by: Andrew Morton <akpm@linux-foundation.org>
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit d4da2957c44390959fea1003188f449b3f38aba3
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Mon Aug 20 19:04:16 2012 +0800
+
+    firmware loader: let caching firmware piggyback on loading firmware
+
+    After starting caching firmware, there is still some time left
+    before devices are suspended, during the period, request_firmware
+    or its nowait version may still be triggered by the below situations
+    to load firmware images which can't be cached during suspend/resume
+    cycle.
+
+    	- new devices added
+    	- driver bind
+    	- or device open kind of things
+
+    This patch utilizes the piggyback trick to cache firmware for
+    this kind of situation: just increase the firmware buf's reference
+    count and add the fw name entry into cache entry list after starting
+    caching firmware and before syscore_suspend() is called.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 580eb13b0fb703c49e729d42c313dc901145c278
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Mon Aug 20 19:04:15 2012 +0800
+
+    firmware loader: fix firmware -ENOENT situations
+
+    If the requested firmware image doesn't exist, firmware->priv
+    should be set for the later concurrent requests, otherwise
+    warning and oops will be triggered inside firmware_free_data().
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 529f2b00940c82abdf17bdc902e2b02d1904a789
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Fri Aug 17 22:07:00 2012 +0800
+
+    firmware loader: fix build failure if FW_LOADER is m
+
+    device_cache_fw_images need to iterate devices in system,
+    so this patch applies the introduced dpm_for_each_dev to
+    avoid link failure if CONFIG_FW_LOADER is m.
+
+    Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit e51f2ca3d0d22b9cd08b6262d6e8423888a339e6
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Fri Aug 17 22:06:58 2012 +0800
+
+    firmware loader: fix compile failure if !PM
+
+    'return 0' should be added to fw_pm_notify if !PM because
+    return value of the funcion is defined as 'int'.
+
+    Reported-by: Fengguang Wu <fengguang.wu@intel.com>
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 402bbcd0a468c1388fe9fd98a6bd0d4d57f1f5e4
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:29 2012 +0800
+
+    firmware loader: cache devices firmware during suspend/resume cycle
+
+    This patch implements caching devices' firmware automatically
+    during system syspend/resume cycle, so any device drivers can
+    call request_firmware or request_firmware_nowait inside resume
+    path to get the cached firmware if they have loaded firmwares
+    successfully at least once before entering suspend.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 6b5ef252737d7e46cee1471de1f8f778816ed432
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:28 2012 +0800
+
+    firmware loader: use small timeout for cache device firmware
+
+    Because device_cache_fw_images only cache the firmware which has been
+    loaded sucessfully at leat once, using a small loading timeout should
+    be reasonable.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 3a78247eb488608da2dbf2c0b5f312abc7fb36bf
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:27 2012 +0800
+
+    firmware: introduce device_cache/uncache_fw_images
+
+    This patch introduces the three helpers below:
+
+    	void device_cache_fw_images(void)
+    	void device_uncache_fw_images(void)
+    	void device_uncache_fw_images_delay(unsigned long)
+
+    so we can use device_cache_fw_images() to cache firmware for
+    all devices which need firmware to work, and the device driver
+    can get the firmware easily from kernel memory when system isn't
+    ready for completing requests of loading firmware.
+
+    After system is ready for completing firmware loading, driver core
+    will call device_uncache_fw_images() or its delay version to free
+    the cached firmware.
+
+    The above helpers will be used to cache device firmware during
+    system suspend/resume cycle in the following patches.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit fec2fa2ad35e9b5ac3b556b57f2ce0d93d88dad3
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:25 2012 +0800
+
+    firmware loader: store firmware name into devres list
+
+    This patch will store firmware name into devres list of the device
+    which is requesting firmware loading, so that we can implement
+    auto cache and uncache firmware for devices in need.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 82502e82fdb519661d280f9a2ff7888a7e11719d
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:24 2012 +0800
+
+    firmware loader: fix comments on request_firmware_nowait
+
+    request_firmware_nowait is allowed to be called in atomic
+    context now if @gfp is GFP_ATOMIC, so fix the obsolete
+    comments and states which situations are suitable for using
+    it.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 885f2a4d3e79590a5ac56c600ef21c7f33edf1ff
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:23 2012 +0800
+
+    firmware loader: fix device lifetime
+
+    Callers of request_firmware* must hold the reference count of
+    @device, otherwise it is easy to trigger oops since the firmware
+    loader device is the child of @device.
+
+    This patch adds comments about the usage. In fact, most of drivers
+    call request_firmware* in its probe() or open(), so the constraint
+    should be reasonable and can be satisfied.
+
+    Also this patch holds the reference count of @device before
+    schedule_work() in request_firmware_nowait() to avoid that
+    the @device is released after request_firmware_nowait returns
+    and before the worker function is scheduled.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit f8c40c19896e07c5e24596d1c73e0004b83bf564
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:22 2012 +0800
+
+    firmware loader: introduce cache_firmware and uncache_firmware
+
+    This patches introduce two kernel APIs of cache_firmware and
+    uncache_firmware, both of which take the firmware file name
+    as the only parameter.
+
+    So any drivers can call cache_firmware to cache the specified
+    firmware file into kernel memory, and can use the cached firmware
+    in situations which can't request firmware from user space.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 33f7f4606f2add663f0f055d9e22224b2ed927e6
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:21 2012 +0800
+
+    firmware loader: always let firmware_buf own the pages buffer
+
+    This patch always let firmware_buf own the pages buffer allocated
+    inside firmware_data_write, and add all instances of firmware_buf
+    into the firmware cache global list. Also introduce one private field
+    in 'struct firmware', so release_firmware will see the instance of
+    firmware_buf associated with the current firmware instance, then just
+    'free' the instance of firmware_buf.
+
+    The firmware_buf instance represents one pages buffer for one
+    firmware image, so lots of firmware loading requests can share
+    the same firmware_buf instance if they request the same firmware
+    image file.
+
+    This patch will make implementation of the following cache_firmware/
+    uncache_firmware very easy and simple.
+
+    In fact, the patch improves request_formware/release_firmware:
+
+            - only request userspace to write firmware image once if
+    	several devices share one same firmware image and its drivers
+    	call request_firmware concurrently.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 23f444643c50ab3dc66e2cd699ef03d90dbdccc8
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:20 2012 +0800
+
+    firmware loader: introduce firmware_buf
+
+    This patch introduces struct firmware_buf to describe the buffer
+    which holds the firmware data, which will make the following
+    cache_firmware/uncache_firmware implemented easily.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit c341f46309619686830be4b608c9db06b68bafc1
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:19 2012 +0800
+
+    firmware loader: fix creation failure of fw loader device
+
+    If one device driver calls request_firmware_nowait() to request
+    several different firmwares' loading, device_add() will return
+    failure since all firmware loader device use same name of the
+    device who is requesting firmware.
+
+    This patch always use the name of firmware image as the firmware
+    loader device name to fix the problem since the following patches
+    for caching firmware will make sure only one loading for same
+    firmware is alllowd at the same time.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit fca2060e5dac12511c0860878e89e0e5ad8e7a3e
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:18 2012 +0800
+
+    firmware loader: remove unnecessary wmb()
+
+    The wmb() inside fw_load_abort is not necessary, since
+    complete() and wait_on_completion() has implied one pair
+    of memory barrier.
+
+    Also wmb() isn't a correct usage, so just remove it.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit 4eaaa8782ff182e0de3ac050aeb0305879dc89eb
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:17 2012 +0800
+
+    firmware loader: fix races during loading firmware
+
+    This patch fixes two races in loading firmware:
+
+    1, FW_STATUS_DONE should be set before waking up the task waitting
+    on _request_firmware_load, otherwise FW_STATUS_ABORT may be
+    thought as DONE mistakenly.
+
+    2, Inside _request_firmware_load(), there is a small window between
+    wait_for_completion() and mutex_lock(&fw_lock), and 'echo 1 > loading'
+    still may happen during the period, so this patch checks FW_STATUS_DONE
+    to prevent pages' buffer completed from being freed in firmware_loading_store.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit dc3ca115481cc50a7d4aa866f607b470280b1e5e
+Author: Ming Lei <ming.lei@canonical.com>
+Date:   Sat Aug 4 12:01:16 2012 +0800
+
+    firmware loader: simplify pages ownership transfer
+
+    This patch doesn't transfer ownership of pages' buffer to the
+    instance of firmware until the firmware loading is completed,
+    which will simplify firmware_loading_store a lot, so help
+    to introduce the following cache_firmware and uncache_firmware
+    mechanism during system suspend-resume cycle.
+
+    In fact, this patch fixes one bug: if writing data into
+    firmware loader device is bypassed between writting 1 and 0 to
+    'loading', OOPS will be triggered without the patch.
+
+    Also handle the vmap failure case, and add some comments to make
+    code more readable.
+
+    Signed-off-by: Ming Lei <ming.lei@canonical.com>
+    Cc: Linus Torvalds <torvalds@linux-foundation.org>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+commit e728c819e3666bb37e08a49255b24e6933bb72e5
+Author: Lars-Peter Clausen <lars@metafoo.de>
+Date:   Tue Jul 3 18:49:36 2012 +0200
+
+    driver-core: Use kobj_to_dev instead of re-implementing it
+
+    Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
+    Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/firmware_class/README |    7 +
+ drivers/base/Kconfig                |   11 +
+ drivers/base/core.c                 |   17 +-
+ drivers/base/devres.c               |   77 +++
+ drivers/base/firmware_class.c       | 1216 +++++++++++++++++++++++++++++------
+ drivers/base/power/main.c           |   22 +
+ include/linux/device.h              |    6 +
+ include/linux/firmware.h            |   15 +
+ include/linux/pm.h                  |    5 +
+ 9 files changed, 1177 insertions(+), 199 deletions(-)
+
+diff --git a/Documentation/firmware_class/README b/Documentation/firmware_class/README
+index 7eceaff..5fde1ef 100644
+--- a/Documentation/firmware_class/README
++++ b/Documentation/firmware_class/README
+@@ -106,3 +106,10 @@
+    on the setup, so I think that the choice on what firmware to make
+    persistent should be left to userspace.
+ 
++ about firmware cache:
++ --------------------
++ After firmware cache mechanism is introduced during system sleep,
++ request_firmware can be called safely inside device's suspend and
++ resume callback, and callers need't cache the firmware by
++ themselves any more for dealing with firmware loss during system
++ resume.
+diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
+index 7c96a3a..b7af956 100644
+--- a/drivers/base/Kconfig
++++ b/drivers/base/Kconfig
+@@ -145,6 +145,17 @@ config EXTRA_FIRMWARE_DIR
+ 	  this option you can point it elsewhere, such as /lib/firmware/ or
+ 	  some other directory containing the firmware files.
+ 
++config FW_LOADER_USER_HELPER
++	bool "Fallback user-helper invocation for firmware loading"
++	depends on FW_LOADER
++	default y
++	help
++	  This option enables / disables the invocation of user-helper
++	  (e.g. udev) for loading firmware files as a fallback after the
++	  direct file loading in kernel fails.  The user-mode helper is
++	  no longer required unless you have a special firmware file that
++	  resides in a non-standard path.
++
+ config DEBUG_DRIVER
+ 	bool "Driver Core verbose debug messages"
+ 	depends on DEBUG_KERNEL
+diff --git a/drivers/base/core.c b/drivers/base/core.c
+index f428321..c360a22 100644
+--- a/drivers/base/core.c
++++ b/drivers/base/core.c
+@@ -84,14 +84,13 @@ const char *dev_driver_string(const struct device *dev)
+ }
+ EXPORT_SYMBOL(dev_driver_string);
+ 
+-#define to_dev(obj) container_of(obj, struct device, kobj)
+ #define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+ 
+ static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
+ 			     char *buf)
+ {
+ 	struct device_attribute *dev_attr = to_dev_attr(attr);
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 	ssize_t ret = -EIO;
+ 
+ 	if (dev_attr->show)
+@@ -107,7 +106,7 @@ static ssize_t dev_attr_store(struct kobject *kobj, struct attribute *attr,
+ 			      const char *buf, size_t count)
+ {
+ 	struct device_attribute *dev_attr = to_dev_attr(attr);
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 	ssize_t ret = -EIO;
+ 
+ 	if (dev_attr->store)
+@@ -181,7 +180,7 @@ EXPORT_SYMBOL_GPL(device_show_int);
+  */
+ static void device_release(struct kobject *kobj)
+ {
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 	struct device_private *p = dev->p;
+ 
+ 	if (dev->release)
+@@ -199,7 +198,7 @@ static void device_release(struct kobject *kobj)
+ 
+ static const void *device_namespace(struct kobject *kobj)
+ {
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 	const void *ns = NULL;
+ 
+ 	if (dev->class && dev->class->ns_type)
+@@ -220,7 +219,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
+ 	struct kobj_type *ktype = get_ktype(kobj);
+ 
+ 	if (ktype == &device_ktype) {
+-		struct device *dev = to_dev(kobj);
++		struct device *dev = kobj_to_dev(kobj);
+ 		if (dev->bus)
+ 			return 1;
+ 		if (dev->class)
+@@ -231,7 +230,7 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
+ 
+ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
+ {
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 
+ 	if (dev->bus)
+ 		return dev->bus->name;
+@@ -243,7 +242,7 @@ static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
+ static int dev_uevent(struct kset *kset, struct kobject *kobj,
+ 		      struct kobj_uevent_env *env)
+ {
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 	int retval = 0;
+ 
+ 	/* add device node properties if present */
+@@ -1131,7 +1130,7 @@ int device_register(struct device *dev)
+  */
+ struct device *get_device(struct device *dev)
+ {
+-	return dev ? to_dev(kobject_get(&dev->kobj)) : NULL;
++	return dev ? kobj_to_dev(kobject_get(&dev->kobj)) : NULL;
+ }
+ 
+ /**
+diff --git a/drivers/base/devres.c b/drivers/base/devres.c
+index 524bf96..8731979 100644
+--- a/drivers/base/devres.c
++++ b/drivers/base/devres.c
+@@ -144,6 +144,48 @@ EXPORT_SYMBOL_GPL(devres_alloc);
+ #endif
+ 
+ /**
++ * devres_for_each_res - Resource iterator
++ * @dev: Device to iterate resource from
++ * @release: Look for resources associated with this release function
++ * @match: Match function (optional)
++ * @match_data: Data for the match function
++ * @fn: Function to be called for each matched resource.
++ * @data: Data for @fn, the 3rd parameter of @fn
++ *
++ * Call @fn for each devres of @dev which is associated with @release
++ * and for which @match returns 1.
++ *
++ * RETURNS:
++ * 	void
++ */
++void devres_for_each_res(struct device *dev, dr_release_t release,
++			dr_match_t match, void *match_data,
++			void (*fn)(struct device *, void *, void *),
++			void *data)
++{
++	struct devres_node *node;
++	struct devres_node *tmp;
++	unsigned long flags;
++
++	if (!fn)
++		return;
++
++	spin_lock_irqsave(&dev->devres_lock, flags);
++	list_for_each_entry_safe_reverse(node, tmp,
++			&dev->devres_head, entry) {
++		struct devres *dr = container_of(node, struct devres, node);
++
++		if (node->release != release)
++			continue;
++		if (match && !match(dev, dr->data, match_data))
++			continue;
++		fn(dev, dr->data, data);
++	}
++	spin_unlock_irqrestore(&dev->devres_lock, flags);
++}
++EXPORT_SYMBOL_GPL(devres_for_each_res);
++
++/**
+  * devres_free - Free device resource data
+  * @res: Pointer to devres data to free
+  *
+@@ -309,6 +351,10 @@ EXPORT_SYMBOL_GPL(devres_remove);
+  * which @match returns 1.  If @match is NULL, it's considered to
+  * match all.  If found, the resource is removed atomically and freed.
+  *
++ * Note that the release function for the resource will not be called,
++ * only the devres-allocated data will be freed.  The caller becomes
++ * responsible for freeing any other data.
++ *
+  * RETURNS:
+  * 0 if devres is found and freed, -ENOENT if not found.
+  */
+@@ -326,6 +372,37 @@ int devres_destroy(struct device *dev, dr_release_t release,
+ }
+ EXPORT_SYMBOL_GPL(devres_destroy);
+ 
++
++/**
++ * devres_release - Find a device resource and destroy it, calling release
++ * @dev: Device to find resource from
++ * @release: Look for resources associated with this release function
++ * @match: Match function (optional)
++ * @match_data: Data for the match function
++ *
++ * Find the latest devres of @dev associated with @release and for
++ * which @match returns 1.  If @match is NULL, it's considered to
++ * match all.  If found, the resource is removed atomically, the
++ * release function called and the resource freed.
++ *
++ * RETURNS:
++ * 0 if devres is found and freed, -ENOENT if not found.
++ */
++int devres_release(struct device *dev, dr_release_t release,
++		   dr_match_t match, void *match_data)
++{
++	void *res;
++
++	res = devres_remove(dev, release, match, match_data);
++	if (unlikely(!res))
++		return -ENOENT;
++
++	(*release)(dev, res);
++	devres_free(res);
++	return 0;
++}
++EXPORT_SYMBOL_GPL(devres_release);
++
+ static int remove_nodes(struct device *dev,
+ 			struct list_head *first, struct list_head *end,
+ 			struct list_head *todo)
+diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
+index 5401814..15b36b7 100644
+--- a/drivers/base/firmware_class.c
++++ b/drivers/base/firmware_class.c
+@@ -21,8 +21,16 @@
+ #include <linux/firmware.h>
+ #include <linux/slab.h>
+ #include <linux/sched.h>
++#include <linux/file.h>
++#include <linux/list.h>
++#include <linux/async.h>
++#include <linux/pm.h>
++#include <linux/suspend.h>
++#include <linux/syscore_ops.h>
+ 
+-#define to_dev(obj) container_of(obj, struct device, kobj)
++#include <generated/utsrelease.h>
++
++#include "base.h"
+ 
+ MODULE_AUTHOR("Manuel Estrada Sainz");
+ MODULE_DESCRIPTION("Multi purpose firmware loading support");
+@@ -87,23 +95,354 @@ static inline long firmware_loading_timeout(void)
+ 	return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT;
+ }
+ 
+-/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+- * guarding for corner cases a global lock should be OK */
+-static DEFINE_MUTEX(fw_lock);
++struct firmware_cache {
++	/* firmware_buf instance will be added into the below list */
++	spinlock_t lock;
++	struct list_head head;
++	int state;
++
++#ifdef CONFIG_PM_SLEEP
++	/*
++	 * Names of firmware images which have been cached successfully
++	 * will be added into the below list so that device uncache
++	 * helper can trace which firmware images have been cached
++	 * before.
++	 */
++	spinlock_t name_lock;
++	struct list_head fw_names;
++
++	wait_queue_head_t wait_queue;
++	int cnt;
++	struct delayed_work work;
++
++	struct notifier_block   pm_notify;
++#endif
++};
+ 
+-struct firmware_priv {
++struct firmware_buf {
++	struct kref ref;
++	struct list_head list;
+ 	struct completion completion;
+-	struct firmware *fw;
++	struct firmware_cache *fwc;
+ 	unsigned long status;
++	void *data;
++	size_t size;
++#ifdef CONFIG_FW_LOADER_USER_HELPER
++	bool is_paged_buf;
+ 	struct page **pages;
+ 	int nr_pages;
+ 	int page_array_size;
+-	struct timer_list timeout;
+-	struct device dev;
+-	bool nowait;
++#endif
+ 	char fw_id[];
+ };
+ 
++struct fw_cache_entry {
++	struct list_head list;
++	char name[];
++};
++
++struct fw_name_devm {
++	unsigned long magic;
++	char name[];
++};
++
++#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)
++
++#define	FW_LOADER_NO_CACHE	0
++#define	FW_LOADER_START_CACHE	1
++
++static int fw_cache_piggyback_on_request(const char *name);
++
++/* fw_lock could be moved to 'struct firmware_priv' but since it is just
++ * guarding for corner cases a global lock should be OK */
++static DEFINE_MUTEX(fw_lock);
++
++static struct firmware_cache fw_cache;
++
++static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
++					      struct firmware_cache *fwc)
++{
++	struct firmware_buf *buf;
++
++	buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC);
++
++	if (!buf)
++		return buf;
++
++	kref_init(&buf->ref);
++	strcpy(buf->fw_id, fw_name);
++	buf->fwc = fwc;
++	init_completion(&buf->completion);
++
++	pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf);
++
++	return buf;
++}
++
++static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
++{
++	struct firmware_buf *tmp;
++	struct firmware_cache *fwc = &fw_cache;
++
++	list_for_each_entry(tmp, &fwc->head, list)
++		if (!strcmp(tmp->fw_id, fw_name))
++			return tmp;
++	return NULL;
++}
++
++static int fw_lookup_and_allocate_buf(const char *fw_name,
++				      struct firmware_cache *fwc,
++				      struct firmware_buf **buf)
++{
++	struct firmware_buf *tmp;
++
++	spin_lock(&fwc->lock);
++	tmp = __fw_lookup_buf(fw_name);
++	if (tmp) {
++		kref_get(&tmp->ref);
++		spin_unlock(&fwc->lock);
++		*buf = tmp;
++		return 1;
++	}
++	tmp = __allocate_fw_buf(fw_name, fwc);
++	if (tmp)
++		list_add(&tmp->list, &fwc->head);
++	spin_unlock(&fwc->lock);
++
++	*buf = tmp;
++
++	return tmp ? 0 : -ENOMEM;
++}
++
++static struct firmware_buf *fw_lookup_buf(const char *fw_name)
++{
++	struct firmware_buf *tmp;
++	struct firmware_cache *fwc = &fw_cache;
++
++	spin_lock(&fwc->lock);
++	tmp = __fw_lookup_buf(fw_name);
++	spin_unlock(&fwc->lock);
++
++	return tmp;
++}
++
++static void __fw_free_buf(struct kref *ref)
++{
++	struct firmware_buf *buf = to_fwbuf(ref);
++	struct firmware_cache *fwc = buf->fwc;
++
++	pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
++		 __func__, buf->fw_id, buf, buf->data,
++		 (unsigned int)buf->size);
++
++	list_del(&buf->list);
++	spin_unlock(&fwc->lock);
++
++#ifdef CONFIG_FW_LOADER_USER_HELPER
++	if (buf->is_paged_buf) {
++		int i;
++		vunmap(buf->data);
++		for (i = 0; i < buf->nr_pages; i++)
++			__free_page(buf->pages[i]);
++		kfree(buf->pages);
++	} else
++#endif
++		vfree(buf->data);
++	kfree(buf);
++}
++
++static void fw_free_buf(struct firmware_buf *buf)
++{
++	struct firmware_cache *fwc = buf->fwc;
++	spin_lock(&fwc->lock);
++	if (!kref_put(&buf->ref, __fw_free_buf))
++		spin_unlock(&fwc->lock);
++}
++
++/* direct firmware loading support */
++static char fw_path_para[256];
++static const char * const fw_path[] = {
++	fw_path_para,
++	"/lib/firmware/updates/" UTS_RELEASE,
++	"/lib/firmware/updates",
++	"/lib/firmware/" UTS_RELEASE,
++	"/lib/firmware"
++};
++
++/*
++ * Typical usage is that passing 'firmware_class.path=$CUSTOMIZED_PATH'
++ * from kernel command line because firmware_class is generally built in
++ * kernel instead of module.
++ */
++module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644);
++MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path");
++
++/* Don't inline this: 'struct kstat' is biggish */
++static noinline_for_stack long fw_file_size(struct file *file)
++{
++	struct kstat st;
++	if (vfs_getattr(file->f_path.mnt, file->f_path.dentry, &st))
++		return -1;
++	if (!S_ISREG(st.mode))
++		return -1;
++	if (st.size != (long)st.size)
++		return -1;
++	return st.size;
++}
++
++static bool fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf)
++{
++	long size;
++	char *buf;
++
++	size = fw_file_size(file);
++	if (size <= 0)
++		return false;
++	buf = vmalloc(size);
++	if (!buf)
++		return false;
++	if (kernel_read(file, 0, buf, size) != size) {
++		vfree(buf);
++		return false;
++	}
++	fw_buf->data = buf;
++	fw_buf->size = size;
++	return true;
++}
++
++static bool fw_get_filesystem_firmware(struct device *device,
++				       struct firmware_buf *buf)
++{
++	int i;
++	bool success = false;
++	char *path = __getname();
++
++	for (i = 0; i < ARRAY_SIZE(fw_path); i++) {
++		struct file *file;
++
++		/* skip the unset customized path */
++		if (!fw_path[i][0])
++			continue;
++
++		snprintf(path, PATH_MAX, "%s/%s", fw_path[i], buf->fw_id);
++
++		file = filp_open(path, O_RDONLY, 0);
++		if (IS_ERR(file))
++			continue;
++		success = fw_read_file_contents(file, buf);
++		fput(file);
++		if (success)
++			break;
++	}
++	__putname(path);
++
++	if (success) {
++		dev_dbg(device, "firmware: direct-loading firmware %s\n",
++			buf->fw_id);
++		mutex_lock(&fw_lock);
++		set_bit(FW_STATUS_DONE, &buf->status);
++		complete_all(&buf->completion);
++		mutex_unlock(&fw_lock);
++	}
++
++	return success;
++}
++
++/* firmware holds the ownership of pages */
++static void firmware_free_data(const struct firmware *fw)
++{
++	/* Loaded directly? */
++	if (!fw->priv) {
++		vfree(fw->data);
++		return;
++	}
++	fw_free_buf(fw->priv);
++}
++
++/* store the pages buffer info firmware from buf */
++static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
++{
++	fw->priv = buf;
++#ifdef CONFIG_FW_LOADER_USER_HELPER
++	fw->pages = buf->pages;
++#endif
++	fw->size = buf->size;
++	fw->data = buf->data;
++
++	pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
++		 __func__, buf->fw_id, buf, buf->data,
++		 (unsigned int)buf->size);
++}
++
++#ifdef CONFIG_PM_SLEEP
++static void fw_name_devm_release(struct device *dev, void *res)
++{
++	struct fw_name_devm *fwn = res;
++
++	if (fwn->magic == (unsigned long)&fw_cache)
++		pr_debug("%s: fw_name-%s devm-%p released\n",
++				__func__, fwn->name, res);
++}
++
++static int fw_devm_match(struct device *dev, void *res,
++		void *match_data)
++{
++	struct fw_name_devm *fwn = res;
++
++	return (fwn->magic == (unsigned long)&fw_cache) &&
++		!strcmp(fwn->name, match_data);
++}
++
++static struct fw_name_devm *fw_find_devm_name(struct device *dev,
++		const char *name)
++{
++	struct fw_name_devm *fwn;
++
++	fwn = devres_find(dev, fw_name_devm_release,
++			  fw_devm_match, (void *)name);
++	return fwn;
++}
++
++/* add firmware name into devres list */
++static int fw_add_devm_name(struct device *dev, const char *name)
++{
++	struct fw_name_devm *fwn;
++
++	fwn = fw_find_devm_name(dev, name);
++	if (fwn)
++		return 1;
++
++	fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) +
++			   strlen(name) + 1, GFP_KERNEL);
++	if (!fwn)
++		return -ENOMEM;
++
++	fwn->magic = (unsigned long)&fw_cache;
++	strcpy(fwn->name, name);
++	devres_add(dev, fwn);
++
++	return 0;
++}
++#else
++static int fw_add_devm_name(struct device *dev, const char *name)
++{
++	return 0;
++}
++#endif
++
++
++/*
++ * user-mode helper code
++ */
++#ifdef CONFIG_FW_LOADER_USER_HELPER
++struct firmware_priv {
++	struct delayed_work timeout_work;
++	bool nowait;
++	struct device dev;
++	struct firmware_buf *buf;
++	struct firmware *fw;
++};
++
+ static struct firmware_priv *to_firmware_priv(struct device *dev)
+ {
+ 	return container_of(dev, struct firmware_priv, dev);
+@@ -111,11 +450,15 @@ static struct firmware_priv *to_firmware_priv(struct device *dev)
+ 
+ static void fw_load_abort(struct firmware_priv *fw_priv)
+ {
+-	set_bit(FW_STATUS_ABORT, &fw_priv->status);
+-	wmb();
+-	complete(&fw_priv->completion);
++	struct firmware_buf *buf = fw_priv->buf;
++
++	set_bit(FW_STATUS_ABORT, &buf->status);
++	complete_all(&buf->completion);
+ }
+ 
++#define is_fw_load_aborted(buf)	\
++	test_bit(FW_STATUS_ABORT, &(buf)->status)
++
+ static ssize_t firmware_timeout_show(struct class *class,
+ 				     struct class_attribute *attr,
+ 				     char *buf)
+@@ -156,11 +499,7 @@ static struct class_attribute firmware_class_attrs[] = {
+ static void fw_dev_release(struct device *dev)
+ {
+ 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+-	int i;
+ 
+-	for (i = 0; i < fw_priv->nr_pages; i++)
+-		__free_page(fw_priv->pages[i]);
+-	kfree(fw_priv->pages);
+ 	kfree(fw_priv);
+ 
+ 	module_put(THIS_MODULE);
+@@ -170,7 +509,7 @@ static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
+ {
+ 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ 
+-	if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
++	if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->buf->fw_id))
+ 		return -ENOMEM;
+ 	if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
+ 		return -ENOMEM;
+@@ -191,26 +530,30 @@ static ssize_t firmware_loading_show(struct device *dev,
+ 				     struct device_attribute *attr, char *buf)
+ {
+ 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+-	int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
++	int loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
+ 
+ 	return sprintf(buf, "%d\n", loading);
+ }
+ 
+-static void firmware_free_data(const struct firmware *fw)
+-{
+-	int i;
+-	vunmap(fw->data);
+-	if (fw->pages) {
+-		for (i = 0; i < PFN_UP(fw->size); i++)
+-			__free_page(fw->pages[i]);
+-		kfree(fw->pages);
+-	}
+-}
+-
+ /* Some architectures don't have PAGE_KERNEL_RO */
+ #ifndef PAGE_KERNEL_RO
+ #define PAGE_KERNEL_RO PAGE_KERNEL
+ #endif
++
++/* one pages buffer should be mapped/unmapped only once */
++static int fw_map_pages_buf(struct firmware_buf *buf)
++{
++	if (!buf->is_paged_buf)
++		return 0;
++
++	if (buf->data)
++		vunmap(buf->data);
++	buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
++	if (!buf->data)
++		return -ENOMEM;
++	return 0;
++}
++
+ /**
+  * firmware_loading_store - set value in the 'loading' control file
+  * @dev: device pointer
+@@ -229,45 +572,41 @@ static ssize_t firmware_loading_store(struct device *dev,
+ 				      const char *buf, size_t count)
+ {
+ 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
++	struct firmware_buf *fw_buf = fw_priv->buf;
+ 	int loading = simple_strtol(buf, NULL, 10);
+ 	int i;
+ 
+ 	mutex_lock(&fw_lock);
+ 
+-	if (!fw_priv->fw)
++	if (!fw_buf)
+ 		goto out;
+ 
+ 	switch (loading) {
+ 	case 1:
+-		firmware_free_data(fw_priv->fw);
+-		memset(fw_priv->fw, 0, sizeof(struct firmware));
+-		/* If the pages are not owned by 'struct firmware' */
+-		for (i = 0; i < fw_priv->nr_pages; i++)
+-			__free_page(fw_priv->pages[i]);
+-		kfree(fw_priv->pages);
+-		fw_priv->pages = NULL;
+-		fw_priv->page_array_size = 0;
+-		fw_priv->nr_pages = 0;
+-		set_bit(FW_STATUS_LOADING, &fw_priv->status);
++		/* discarding any previous partial load */
++		if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
++			for (i = 0; i < fw_buf->nr_pages; i++)
++				__free_page(fw_buf->pages[i]);
++			kfree(fw_buf->pages);
++			fw_buf->pages = NULL;
++			fw_buf->page_array_size = 0;
++			fw_buf->nr_pages = 0;
++			set_bit(FW_STATUS_LOADING, &fw_buf->status);
++		}
+ 		break;
+ 	case 0:
+-		if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
+-			vunmap(fw_priv->fw->data);
+-			fw_priv->fw->data = vmap(fw_priv->pages,
+-						 fw_priv->nr_pages,
+-						 0, PAGE_KERNEL_RO);
+-			if (!fw_priv->fw->data) {
+-				dev_err(dev, "%s: vmap() failed\n", __func__);
+-				goto err;
+-			}
+-			/* Pages are now owned by 'struct firmware' */
+-			fw_priv->fw->pages = fw_priv->pages;
+-			fw_priv->pages = NULL;
+-
+-			fw_priv->page_array_size = 0;
+-			fw_priv->nr_pages = 0;
+-			complete(&fw_priv->completion);
+-			clear_bit(FW_STATUS_LOADING, &fw_priv->status);
++		if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
++			set_bit(FW_STATUS_DONE, &fw_buf->status);
++			clear_bit(FW_STATUS_LOADING, &fw_buf->status);
++
++			/*
++			 * Several loading requests may be pending on
++			 * one same firmware buf, so let all requests
++			 * see the mapped 'buf->data' once the loading
++			 * is completed.
++			 * */
++			fw_map_pages_buf(fw_buf);
++			complete_all(&fw_buf->completion);
+ 			break;
+ 		}
+ 		/* fallthrough */
+@@ -275,7 +614,6 @@ static ssize_t firmware_loading_store(struct device *dev,
+ 		dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
+ 		/* fallthrough */
+ 	case -1:
+-	err:
+ 		fw_load_abort(fw_priv);
+ 		break;
+ 	}
+@@ -290,23 +628,23 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
+ 				  struct bin_attribute *bin_attr,
+ 				  char *buffer, loff_t offset, size_t count)
+ {
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+-	struct firmware *fw;
++	struct firmware_buf *buf;
+ 	ssize_t ret_count;
+ 
+ 	mutex_lock(&fw_lock);
+-	fw = fw_priv->fw;
+-	if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
++	buf = fw_priv->buf;
++	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
+ 		ret_count = -ENODEV;
+ 		goto out;
+ 	}
+-	if (offset > fw->size) {
++	if (offset > buf->size) {
+ 		ret_count = 0;
+ 		goto out;
+ 	}
+-	if (count > fw->size - offset)
+-		count = fw->size - offset;
++	if (count > buf->size - offset)
++		count = buf->size - offset;
+ 
+ 	ret_count = count;
+ 
+@@ -316,11 +654,11 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
+ 		int page_ofs = offset & (PAGE_SIZE-1);
+ 		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+ 
+-		page_data = kmap(fw_priv->pages[page_nr]);
++		page_data = kmap(buf->pages[page_nr]);
+ 
+ 		memcpy(buffer, page_data + page_ofs, page_cnt);
+ 
+-		kunmap(fw_priv->pages[page_nr]);
++		kunmap(buf->pages[page_nr]);
+ 		buffer += page_cnt;
+ 		offset += page_cnt;
+ 		count -= page_cnt;
+@@ -332,12 +670,13 @@ out:
+ 
+ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+ {
++	struct firmware_buf *buf = fw_priv->buf;
+ 	int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
+ 
+ 	/* If the array of pages is too small, grow it... */
+-	if (fw_priv->page_array_size < pages_needed) {
++	if (buf->page_array_size < pages_needed) {
+ 		int new_array_size = max(pages_needed,
+-					 fw_priv->page_array_size * 2);
++					 buf->page_array_size * 2);
+ 		struct page **new_pages;
+ 
+ 		new_pages = kmalloc(new_array_size * sizeof(void *),
+@@ -346,24 +685,24 @@ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+ 			fw_load_abort(fw_priv);
+ 			return -ENOMEM;
+ 		}
+-		memcpy(new_pages, fw_priv->pages,
+-		       fw_priv->page_array_size * sizeof(void *));
+-		memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
+-		       (new_array_size - fw_priv->page_array_size));
+-		kfree(fw_priv->pages);
+-		fw_priv->pages = new_pages;
+-		fw_priv->page_array_size = new_array_size;
++		memcpy(new_pages, buf->pages,
++		       buf->page_array_size * sizeof(void *));
++		memset(&new_pages[buf->page_array_size], 0, sizeof(void *) *
++		       (new_array_size - buf->page_array_size));
++		kfree(buf->pages);
++		buf->pages = new_pages;
++		buf->page_array_size = new_array_size;
+ 	}
+ 
+-	while (fw_priv->nr_pages < pages_needed) {
+-		fw_priv->pages[fw_priv->nr_pages] =
++	while (buf->nr_pages < pages_needed) {
++		buf->pages[buf->nr_pages] =
+ 			alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
+ 
+-		if (!fw_priv->pages[fw_priv->nr_pages]) {
++		if (!buf->pages[buf->nr_pages]) {
+ 			fw_load_abort(fw_priv);
+ 			return -ENOMEM;
+ 		}
+-		fw_priv->nr_pages++;
++		buf->nr_pages++;
+ 	}
+ 	return 0;
+ }
+@@ -384,20 +723,21 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
+ 				   struct bin_attribute *bin_attr,
+ 				   char *buffer, loff_t offset, size_t count)
+ {
+-	struct device *dev = to_dev(kobj);
++	struct device *dev = kobj_to_dev(kobj);
+ 	struct firmware_priv *fw_priv = to_firmware_priv(dev);
+-	struct firmware *fw;
++	struct firmware_buf *buf;
+ 	ssize_t retval;
+ 
+ 	if (!capable(CAP_SYS_RAWIO))
+ 		return -EPERM;
+ 
+ 	mutex_lock(&fw_lock);
+-	fw = fw_priv->fw;
+-	if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
++	buf = fw_priv->buf;
++	if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
+ 		retval = -ENODEV;
+ 		goto out;
+ 	}
++
+ 	retval = fw_realloc_buffer(fw_priv, offset + count);
+ 	if (retval)
+ 		goto out;
+@@ -410,17 +750,17 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
+ 		int page_ofs = offset & (PAGE_SIZE - 1);
+ 		int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
+ 
+-		page_data = kmap(fw_priv->pages[page_nr]);
++		page_data = kmap(buf->pages[page_nr]);
+ 
+ 		memcpy(page_data + page_ofs, buffer, page_cnt);
+ 
+-		kunmap(fw_priv->pages[page_nr]);
++		kunmap(buf->pages[page_nr]);
+ 		buffer += page_cnt;
+ 		offset += page_cnt;
+ 		count -= page_cnt;
+ 	}
+ 
+-	fw->size = max_t(size_t, offset, fw->size);
++	buf->size = max_t(size_t, offset, buf->size);
+ out:
+ 	mutex_unlock(&fw_lock);
+ 	return retval;
+@@ -433,11 +773,18 @@ static struct bin_attribute firmware_attr_data = {
+ 	.write = firmware_data_write,
+ };
+ 
+-static void firmware_class_timeout(u_long data)
++static void firmware_class_timeout_work(struct work_struct *work)
+ {
+-	struct firmware_priv *fw_priv = (struct firmware_priv *) data;
++	struct firmware_priv *fw_priv = container_of(work,
++			struct firmware_priv, timeout_work.work);
+ 
++	mutex_lock(&fw_lock);
++	if (test_bit(FW_STATUS_DONE, &(fw_priv->buf->status))) {
++		mutex_unlock(&fw_lock);
++		return;
++	}
+ 	fw_load_abort(fw_priv);
++	mutex_unlock(&fw_lock);
+ }
+ 
+ static struct firmware_priv *
+@@ -447,70 +794,38 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
+ 	struct firmware_priv *fw_priv;
+ 	struct device *f_dev;
+ 
+-	fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
++	fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
+ 	if (!fw_priv) {
+ 		dev_err(device, "%s: kmalloc failed\n", __func__);
+-		return ERR_PTR(-ENOMEM);
++		fw_priv = ERR_PTR(-ENOMEM);
++		goto exit;
+ 	}
+ 
+-	fw_priv->fw = firmware;
+ 	fw_priv->nowait = nowait;
+-	strcpy(fw_priv->fw_id, fw_name);
+-	init_completion(&fw_priv->completion);
+-	setup_timer(&fw_priv->timeout,
+-		    firmware_class_timeout, (u_long) fw_priv);
++	fw_priv->fw = firmware;
++	INIT_DELAYED_WORK(&fw_priv->timeout_work,
++		firmware_class_timeout_work);
+ 
+ 	f_dev = &fw_priv->dev;
+ 
+ 	device_initialize(f_dev);
+-	dev_set_name(f_dev, "%s", dev_name(device));
++	dev_set_name(f_dev, "%s", fw_name);
+ 	f_dev->parent = device;
+ 	f_dev->class = &firmware_class;
+-
++exit:
+ 	return fw_priv;
+ }
+ 
+-static struct firmware_priv *
+-_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
+-			  struct device *device, bool uevent, bool nowait)
+-{
+-	struct firmware *firmware;
+-	struct firmware_priv *fw_priv;
+-
+-	if (!firmware_p)
+-		return ERR_PTR(-EINVAL);
+-
+-	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
+-	if (!firmware) {
+-		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
+-			__func__);
+-		return ERR_PTR(-ENOMEM);
+-	}
+-
+-	if (fw_get_builtin_firmware(firmware, name)) {
+-		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
+-		return NULL;
+-	}
+-
+-	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
+-	if (IS_ERR(fw_priv)) {
+-		release_firmware(firmware);
+-		*firmware_p = NULL;
+-	}
+-	return fw_priv;
+-}
+-
+-static void _request_firmware_cleanup(const struct firmware **firmware_p)
+-{
+-	release_firmware(*firmware_p);
+-	*firmware_p = NULL;
+-}
+-
++/* load a firmware via user helper */
+ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
+ 				  long timeout)
+ {
+ 	int retval = 0;
+ 	struct device *f_dev = &fw_priv->dev;
++	struct firmware_buf *buf = fw_priv->buf;
++
++	/* fall back on userspace loading */
++	buf->is_paged_buf = true;
+ 
+ 	dev_set_uevent_suppress(f_dev, true);
+ 
+@@ -537,24 +852,18 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
+ 
+ 	if (uevent) {
+ 		dev_set_uevent_suppress(f_dev, false);
+-		dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
++		dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
+ 		if (timeout != MAX_SCHEDULE_TIMEOUT)
+-			mod_timer(&fw_priv->timeout,
+-				  round_jiffies_up(jiffies + timeout));
++			schedule_delayed_work(&fw_priv->timeout_work, timeout);
+ 
+ 		kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+ 	}
+ 
+-	wait_for_completion(&fw_priv->completion);
++	wait_for_completion(&buf->completion);
+ 
+-	set_bit(FW_STATUS_DONE, &fw_priv->status);
+-	del_timer_sync(&fw_priv->timeout);
++	cancel_delayed_work_sync(&fw_priv->timeout_work);
+ 
+-	mutex_lock(&fw_lock);
+-	if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
+-		retval = -ENOENT;
+-	fw_priv->fw = NULL;
+-	mutex_unlock(&fw_lock);
++	fw_priv->buf = NULL;
+ 
+ 	device_remove_file(f_dev, &dev_attr_loading);
+ err_del_bin_attr:
+@@ -566,6 +875,186 @@ err_put_dev:
+ 	return retval;
+ }
+ 
++static int fw_load_from_user_helper(struct firmware *firmware,
++				    const char *name, struct device *device,
++				    bool uevent, bool nowait, long timeout)
++{
++	struct firmware_priv *fw_priv;
++
++	fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
++	if (IS_ERR(fw_priv))
++		return PTR_ERR(fw_priv);
++
++	fw_priv->buf = firmware->priv;
++	return _request_firmware_load(fw_priv, uevent, timeout);
++}
++#else /* CONFIG_FW_LOADER_USER_HELPER */
++static inline int
++fw_load_from_user_helper(struct firmware *firmware, const char *name,
++			 struct device *device, bool uevent, bool nowait,
++			 long timeout)
++{
++	return -ENOENT;
++}
++
++/* No abort during direct loading */
++#define is_fw_load_aborted(buf) false
++
++#endif /* CONFIG_FW_LOADER_USER_HELPER */
++
++
++/* wait until the shared firmware_buf becomes ready (or error) */
++static int sync_cached_firmware_buf(struct firmware_buf *buf)
++{
++	int ret = 0;
++
++	mutex_lock(&fw_lock);
++	while (!test_bit(FW_STATUS_DONE, &buf->status)) {
++		if (is_fw_load_aborted(buf)) {
++			ret = -ENOENT;
++			break;
++		}
++		mutex_unlock(&fw_lock);
++		wait_for_completion(&buf->completion);
++		mutex_lock(&fw_lock);
++	}
++	mutex_unlock(&fw_lock);
++	return ret;
++}
++
++/* prepare firmware and firmware_buf structs;
++ * return 0 if a firmware is already assigned, 1 if need to load one,
++ * or a negative error code
++ */
++static int
++_request_firmware_prepare(struct firmware **firmware_p, const char *name,
++			  struct device *device)
++{
++	struct firmware *firmware;
++	struct firmware_buf *buf;
++	int ret;
++
++	*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
++	if (!firmware) {
++		dev_err(device, "%s: kmalloc(struct firmware) failed\n",
++			__func__);
++		return -ENOMEM;
++	}
++
++	if (fw_get_builtin_firmware(firmware, name)) {
++		dev_dbg(device, "firmware: using built-in firmware %s\n", name);
++		return 0; /* assigned */
++	}
++
++	ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
++
++	/*
++	 * bind with 'buf' now to avoid warning in failure path
++	 * of requesting firmware.
++	 */
++	firmware->priv = buf;
++
++	if (ret > 0) {
++		ret = sync_cached_firmware_buf(buf);
++		if (!ret) {
++			fw_set_page_data(buf, firmware);
++			return 0; /* assigned */
++		}
++	}
++
++	if (ret < 0)
++		return ret;
++	return 1; /* need to load */
++}
++
++static int assign_firmware_buf(struct firmware *fw, struct device *device)
++{
++	struct firmware_buf *buf = fw->priv;
++
++	mutex_lock(&fw_lock);
++	if (!buf->size || is_fw_load_aborted(buf)) {
++		mutex_unlock(&fw_lock);
++		return -ENOENT;
++	}
++
++	/*
++	 * add firmware name into devres list so that we can auto cache
++	 * and uncache firmware for device.
++	 *
++	 * device may has been deleted already, but the problem
++	 * should be fixed in devres or driver core.
++	 */
++	if (device)
++		fw_add_devm_name(device, buf->fw_id);
++
++	/*
++	 * After caching firmware image is started, let it piggyback
++	 * on request firmware.
++	 */
++	if (buf->fwc->state == FW_LOADER_START_CACHE) {
++		if (fw_cache_piggyback_on_request(buf->fw_id))
++			kref_get(&buf->ref);
++	}
++
++	/* pass the pages buffer to driver at the last minute */
++	fw_set_page_data(buf, fw);
++	mutex_unlock(&fw_lock);
++	return 0;
++}
++
++/* called from request_firmware() and request_firmware_work_func() */
++static int
++_request_firmware(const struct firmware **firmware_p, const char *name,
++		  struct device *device, bool uevent, bool nowait)
++{
++	struct firmware *fw;
++	long timeout;
++	int ret;
++
++	if (!firmware_p)
++		return -EINVAL;
++
++	ret = _request_firmware_prepare(&fw, name, device);
++	if (ret <= 0) /* error or already assigned */
++		goto out;
++
++	ret = 0;
++	timeout = firmware_loading_timeout();
++	if (nowait) {
++		timeout = usermodehelper_read_lock_wait(timeout);
++		if (!timeout) {
++			dev_dbg(device, "firmware: %s loading timed out\n",
++				name);
++			ret = -EBUSY;
++			goto out;
++		}
++	} else {
++		ret = usermodehelper_read_trylock();
++		if (WARN_ON(ret)) {
++			dev_err(device, "firmware: %s will not be loaded\n",
++				name);
++			goto out;
++		}
++	}
++
++	if (!fw_get_filesystem_firmware(device, fw->priv))
++		ret = fw_load_from_user_helper(fw, name, device,
++					       uevent, nowait, timeout);
++	if (!ret)
++		ret = assign_firmware_buf(fw, device);
++
++	usermodehelper_read_unlock();
++
++ out:
++	if (ret < 0) {
++		release_firmware(fw);
++		fw = NULL;
++	}
++
++	*firmware_p = fw;
++	return ret;
++}
++
+ /**
+  * request_firmware: - send firmware request and wait for it
+  * @firmware_p: pointer to firmware image
+@@ -580,31 +1069,17 @@ err_put_dev:
+  *      @name will be used as $FIRMWARE in the uevent environment and
+  *      should be distinctive enough not to be confused with any other
+  *      firmware image for this or any other device.
++ *
++ *	Caller must hold the reference count of @device.
++ *
++ *	The function can be called safely inside device's suspend and
++ *	resume callback.
+  **/
+ int
+ request_firmware(const struct firmware **firmware_p, const char *name,
+                  struct device *device)
+ {
+-	struct firmware_priv *fw_priv;
+-	int ret;
+-
+-	fw_priv = _request_firmware_prepare(firmware_p, name, device, true,
+-					    false);
+-	if (IS_ERR_OR_NULL(fw_priv))
+-		return PTR_RET(fw_priv);
+-
+-	ret = usermodehelper_read_trylock();
+-	if (WARN_ON(ret)) {
+-		dev_err(device, "firmware: %s will not be loaded\n", name);
+-	} else {
+-		ret = _request_firmware_load(fw_priv, true,
+-					firmware_loading_timeout());
+-		usermodehelper_read_unlock();
+-	}
+-	if (ret)
+-		_request_firmware_cleanup(firmware_p);
+-
+-	return ret;
++	return _request_firmware(firmware_p, name, device, true, false);
+ }
+ 
+ /**
+@@ -635,32 +1110,13 @@ static void request_firmware_work_func(struct work_struct *work)
+ {
+ 	struct firmware_work *fw_work;
+ 	const struct firmware *fw;
+-	struct firmware_priv *fw_priv;
+-	long timeout;
+-	int ret;
+ 
+ 	fw_work = container_of(work, struct firmware_work, work);
+-	fw_priv = _request_firmware_prepare(&fw, fw_work->name, fw_work->device,
+-			fw_work->uevent, true);
+-	if (IS_ERR_OR_NULL(fw_priv)) {
+-		ret = PTR_RET(fw_priv);
+-		goto out;
+-	}
+-
+-	timeout = usermodehelper_read_lock_wait(firmware_loading_timeout());
+-	if (timeout) {
+-		ret = _request_firmware_load(fw_priv, fw_work->uevent, timeout);
+-		usermodehelper_read_unlock();
+-	} else {
+-		dev_dbg(fw_work->device, "firmware: %s loading timed out\n",
+-			fw_work->name);
+-		ret = -EAGAIN;
+-	}
+-	if (ret)
+-		_request_firmware_cleanup(&fw);
+ 
+- out:
++	_request_firmware(&fw, fw_work->name, fw_work->device,
++			  fw_work->uevent, true);
+ 	fw_work->cont(fw, fw_work->context);
++	put_device(fw_work->device); /* taken in request_firmware_nowait() */
+ 
+ 	module_put(fw_work->module);
+ 	kfree(fw_work);
+@@ -679,9 +1135,15 @@ static void request_firmware_work_func(struct work_struct *work)
+  * @cont: function will be called asynchronously when the firmware
+  *	request is over.
+  *
+- *	Asynchronous variant of request_firmware() for user contexts where
+- *	it is not possible to sleep for long time. It can't be called
+- *	in atomic contexts.
++ *	Caller must hold the reference count of @device.
++ *
++ *	Asynchronous variant of request_firmware() for user contexts:
++ *		- sleep for as small periods as possible since it may
++ *		increase kernel boot time of built-in device drivers
++ *		requesting firmware in their ->probe() methods, if
++ *		@gfp is GFP_KERNEL.
++ *
++ *		- can't sleep at all if @gfp is GFP_ATOMIC.
+  **/
+ int
+ request_firmware_nowait(
+@@ -707,19 +1169,391 @@ request_firmware_nowait(
+ 		return -EFAULT;
+ 	}
+ 
++	get_device(fw_work->device);
+ 	INIT_WORK(&fw_work->work, request_firmware_work_func);
+ 	schedule_work(&fw_work->work);
+ 	return 0;
+ }
+ 
++/**
++ * cache_firmware - cache one firmware image in kernel memory space
++ * @fw_name: the firmware image name
++ *
++ * Cache firmware in kernel memory so that drivers can use it when
++ * system isn't ready for them to request firmware image from userspace.
++ * Once it returns successfully, driver can use request_firmware or its
++ * nowait version to get the cached firmware without any interacting
++ * with userspace
++ *
++ * Return 0 if the firmware image has been cached successfully
++ * Return !0 otherwise
++ *
++ */
++int cache_firmware(const char *fw_name)
++{
++	int ret;
++	const struct firmware *fw;
++
++	pr_debug("%s: %s\n", __func__, fw_name);
++
++	ret = request_firmware(&fw, fw_name, NULL);
++	if (!ret)
++		kfree(fw);
++
++	pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
++
++	return ret;
++}
++
++/**
++ * uncache_firmware - remove one cached firmware image
++ * @fw_name: the firmware image name
++ *
++ * Uncache one firmware image which has been cached successfully
++ * before.
++ *
++ * Return 0 if the firmware cache has been removed successfully
++ * Return !0 otherwise
++ *
++ */
++int uncache_firmware(const char *fw_name)
++{
++	struct firmware_buf *buf;
++	struct firmware fw;
++
++	pr_debug("%s: %s\n", __func__, fw_name);
++
++	if (fw_get_builtin_firmware(&fw, fw_name))
++		return 0;
++
++	buf = fw_lookup_buf(fw_name);
++	if (buf) {
++		fw_free_buf(buf);
++		return 0;
++	}
++
++	return -EINVAL;
++}
++
++#ifdef CONFIG_PM_SLEEP
++static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
++{
++	struct fw_cache_entry *fce;
++
++	fce = kzalloc(sizeof(*fce) + strlen(name) + 1, GFP_ATOMIC);
++	if (!fce)
++		goto exit;
++
++	strcpy(fce->name, name);
++exit:
++	return fce;
++}
++
++static int __fw_entry_found(const char *name)
++{
++	struct firmware_cache *fwc = &fw_cache;
++	struct fw_cache_entry *fce;
++
++	list_for_each_entry(fce, &fwc->fw_names, list) {
++		if (!strcmp(fce->name, name))
++			return 1;
++	}
++	return 0;
++}
++
++static int fw_cache_piggyback_on_request(const char *name)
++{
++	struct firmware_cache *fwc = &fw_cache;
++	struct fw_cache_entry *fce;
++	int ret = 0;
++
++	spin_lock(&fwc->name_lock);
++	if (__fw_entry_found(name))
++		goto found;
++
++	fce = alloc_fw_cache_entry(name);
++	if (fce) {
++		ret = 1;
++		list_add(&fce->list, &fwc->fw_names);
++		pr_debug("%s: fw: %s\n", __func__, name);
++	}
++found:
++	spin_unlock(&fwc->name_lock);
++	return ret;
++}
++
++static void free_fw_cache_entry(struct fw_cache_entry *fce)
++{
++	kfree(fce);
++}
++
++static void __async_dev_cache_fw_image(void *fw_entry,
++				       async_cookie_t cookie)
++{
++	struct fw_cache_entry *fce = fw_entry;
++	struct firmware_cache *fwc = &fw_cache;
++	int ret;
++
++	ret = cache_firmware(fce->name);
++	if (ret) {
++		spin_lock(&fwc->name_lock);
++		list_del(&fce->list);
++		spin_unlock(&fwc->name_lock);
++
++		free_fw_cache_entry(fce);
++	}
++
++	spin_lock(&fwc->name_lock);
++	fwc->cnt--;
++	spin_unlock(&fwc->name_lock);
++
++	wake_up(&fwc->wait_queue);
++}
++
++/* called with dev->devres_lock held */
++static void dev_create_fw_entry(struct device *dev, void *res,
++				void *data)
++{
++	struct fw_name_devm *fwn = res;
++	const char *fw_name = fwn->name;
++	struct list_head *head = data;
++	struct fw_cache_entry *fce;
++
++	fce = alloc_fw_cache_entry(fw_name);
++	if (fce)
++		list_add(&fce->list, head);
++}
++
++static int devm_name_match(struct device *dev, void *res,
++			   void *match_data)
++{
++	struct fw_name_devm *fwn = res;
++	return (fwn->magic == (unsigned long)match_data);
++}
++
++static void dev_cache_fw_image(struct device *dev, void *data)
++{
++	LIST_HEAD(todo);
++	struct fw_cache_entry *fce;
++	struct fw_cache_entry *fce_next;
++	struct firmware_cache *fwc = &fw_cache;
++
++	devres_for_each_res(dev, fw_name_devm_release,
++			    devm_name_match, &fw_cache,
++			    dev_create_fw_entry, &todo);
++
++	list_for_each_entry_safe(fce, fce_next, &todo, list) {
++		list_del(&fce->list);
++
++		spin_lock(&fwc->name_lock);
++		/* only one cache entry for one firmware */
++		if (!__fw_entry_found(fce->name)) {
++			fwc->cnt++;
++			list_add(&fce->list, &fwc->fw_names);
++		} else {
++			free_fw_cache_entry(fce);
++			fce = NULL;
++		}
++		spin_unlock(&fwc->name_lock);
++
++		if (fce)
++			async_schedule(__async_dev_cache_fw_image,
++				       (void *)fce);
++	}
++}
++
++static void __device_uncache_fw_images(void)
++{
++	struct firmware_cache *fwc = &fw_cache;
++	struct fw_cache_entry *fce;
++
++	spin_lock(&fwc->name_lock);
++	while (!list_empty(&fwc->fw_names)) {
++		fce = list_entry(fwc->fw_names.next,
++				struct fw_cache_entry, list);
++		list_del(&fce->list);
++		spin_unlock(&fwc->name_lock);
++
++		uncache_firmware(fce->name);
++		free_fw_cache_entry(fce);
++
++		spin_lock(&fwc->name_lock);
++	}
++	spin_unlock(&fwc->name_lock);
++}
++
++/**
++ * device_cache_fw_images - cache devices' firmware
++ *
++ * If one device called request_firmware or its nowait version
++ * successfully before, the firmware names are recored into the
++ * device's devres link list, so device_cache_fw_images can call
++ * cache_firmware() to cache these firmwares for the device,
++ * then the device driver can load its firmwares easily at
++ * time when system is not ready to complete loading firmware.
++ */
++static void device_cache_fw_images(void)
++{
++	struct firmware_cache *fwc = &fw_cache;
++	int old_timeout;
++	DEFINE_WAIT(wait);
++
++	pr_debug("%s\n", __func__);
++
++	/* cancel uncache work */
++	cancel_delayed_work_sync(&fwc->work);
++
++	/*
++	 * use small loading timeout for caching devices' firmware
++	 * because all these firmware images have been loaded
++	 * successfully at lease once, also system is ready for
++	 * completing firmware loading now. The maximum size of
++	 * firmware in current distributions is about 2M bytes,
++	 * so 10 secs should be enough.
++	 */
++	old_timeout = loading_timeout;
++	loading_timeout = 10;
++
++	mutex_lock(&fw_lock);
++	fwc->state = FW_LOADER_START_CACHE;
++	dpm_for_each_dev(NULL, dev_cache_fw_image);
++	mutex_unlock(&fw_lock);
++
++	/* wait for completion of caching firmware for all devices */
++	spin_lock(&fwc->name_lock);
++	for (;;) {
++		prepare_to_wait(&fwc->wait_queue, &wait,
++				TASK_UNINTERRUPTIBLE);
++		if (!fwc->cnt)
++			break;
++
++		spin_unlock(&fwc->name_lock);
++
++		schedule();
++
++		spin_lock(&fwc->name_lock);
++	}
++	spin_unlock(&fwc->name_lock);
++	finish_wait(&fwc->wait_queue, &wait);
++
++	loading_timeout = old_timeout;
++}
++
++/**
++ * device_uncache_fw_images - uncache devices' firmware
++ *
++ * uncache all firmwares which have been cached successfully
++ * by device_uncache_fw_images earlier
++ */
++static void device_uncache_fw_images(void)
++{
++	pr_debug("%s\n", __func__);
++	__device_uncache_fw_images();
++}
++
++static void device_uncache_fw_images_work(struct work_struct *work)
++{
++	device_uncache_fw_images();
++}
++
++/**
++ * device_uncache_fw_images_delay - uncache devices firmwares
++ * @delay: number of milliseconds to delay uncache device firmwares
++ *
++ * uncache all devices's firmwares which has been cached successfully
++ * by device_cache_fw_images after @delay milliseconds.
++ */
++static void device_uncache_fw_images_delay(unsigned long delay)
++{
++	schedule_delayed_work(&fw_cache.work,
++			msecs_to_jiffies(delay));
++}
++
++static int fw_pm_notify(struct notifier_block *notify_block,
++			unsigned long mode, void *unused)
++{
++	switch (mode) {
++	case PM_HIBERNATION_PREPARE:
++	case PM_SUSPEND_PREPARE:
++		device_cache_fw_images();
++		break;
++
++	case PM_POST_SUSPEND:
++	case PM_POST_HIBERNATION:
++	case PM_POST_RESTORE:
++		/*
++		 * In case that system sleep failed and syscore_suspend is
++		 * not called.
++		 */
++		mutex_lock(&fw_lock);
++		fw_cache.state = FW_LOADER_NO_CACHE;
++		mutex_unlock(&fw_lock);
++
++		device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
++		break;
++	}
++
++	return 0;
++}
++
++/* stop caching firmware once syscore_suspend is reached */
++static int fw_suspend(void)
++{
++	fw_cache.state = FW_LOADER_NO_CACHE;
++	return 0;
++}
++
++static struct syscore_ops fw_syscore_ops = {
++	.suspend = fw_suspend,
++};
++#else
++static int fw_cache_piggyback_on_request(const char *name)
++{
++	return 0;
++}
++#endif
++
++static void __init fw_cache_init(void)
++{
++	spin_lock_init(&fw_cache.lock);
++	INIT_LIST_HEAD(&fw_cache.head);
++	fw_cache.state = FW_LOADER_NO_CACHE;
++
++#ifdef CONFIG_PM_SLEEP
++	spin_lock_init(&fw_cache.name_lock);
++	INIT_LIST_HEAD(&fw_cache.fw_names);
++	fw_cache.cnt = 0;
++
++	init_waitqueue_head(&fw_cache.wait_queue);
++	INIT_DELAYED_WORK(&fw_cache.work,
++			  device_uncache_fw_images_work);
++
++	fw_cache.pm_notify.notifier_call = fw_pm_notify;
++	register_pm_notifier(&fw_cache.pm_notify);
++
++	register_syscore_ops(&fw_syscore_ops);
++#endif
++}
++
+ static int __init firmware_class_init(void)
+ {
++	fw_cache_init();
++#ifdef CONFIG_FW_LOADER_USER_HELPER
+ 	return class_register(&firmware_class);
++#else
++	return 0;
++#endif
+ }
+ 
+ static void __exit firmware_class_exit(void)
+ {
++#ifdef CONFIG_PM_SLEEP
++	unregister_syscore_ops(&fw_syscore_ops);
++	unregister_pm_notifier(&fw_cache.pm_notify);
++#endif
++#ifdef CONFIG_FW_LOADER_USER_HELPER
+ 	class_unregister(&firmware_class);
++#endif
+ }
+ 
+ fs_initcall(firmware_class_init);
+@@ -728,3 +1562,5 @@ module_exit(firmware_class_exit);
+ EXPORT_SYMBOL(release_firmware);
+ EXPORT_SYMBOL(request_firmware);
+ EXPORT_SYMBOL(request_firmware_nowait);
++EXPORT_SYMBOL_GPL(cache_firmware);
++EXPORT_SYMBOL_GPL(uncache_firmware);
+diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
+index 77e3b97..8bde48f 100644
+--- a/drivers/base/power/main.c
++++ b/drivers/base/power/main.c
+@@ -1387,3 +1387,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
+ 	return async_error;
+ }
+ EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
++
++/**
++ * dpm_for_each_dev - device iterator.
++ * @data: data for the callback.
++ * @fn: function to be called for each device.
++ *
++ * Iterate over devices in dpm_list, and call @fn for each device,
++ * passing it @data.
++ */
++void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
++{
++	struct device *dev;
++
++	if (!fn)
++		return;
++
++	device_pm_lock();
++	list_for_each_entry(dev, &dpm_list, power.entry)
++		fn(dev, data);
++	device_pm_unlock();
++}
++EXPORT_SYMBOL_GPL(dpm_for_each_dev);
+diff --git a/include/linux/device.h b/include/linux/device.h
+index 5ad17cc..ad8904d 100644
+--- a/include/linux/device.h
++++ b/include/linux/device.h
+@@ -531,6 +531,10 @@ extern void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
+ #else
+ extern void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
+ #endif
++extern void devres_for_each_res(struct device *dev, dr_release_t release,
++				dr_match_t match, void *match_data,
++				void (*fn)(struct device *, void *, void *),
++				void *data);
+ extern void devres_free(void *res);
+ extern void devres_add(struct device *dev, void *res);
+ extern void *devres_find(struct device *dev, dr_release_t release,
+@@ -541,6 +545,8 @@ extern void *devres_remove(struct device *dev, dr_release_t release,
+ 			   dr_match_t match, void *match_data);
+ extern int devres_destroy(struct device *dev, dr_release_t release,
+ 			  dr_match_t match, void *match_data);
++extern int devres_release(struct device *dev, dr_release_t release,
++			  dr_match_t match, void *match_data);
+ 
+ /* devres group */
+ extern void * __must_check devres_open_group(struct device *dev, void *id,
+diff --git a/include/linux/firmware.h b/include/linux/firmware.h
+index 1e7c011..e4279fe 100644
+--- a/include/linux/firmware.h
++++ b/include/linux/firmware.h
+@@ -12,6 +12,9 @@ struct firmware {
+ 	size_t size;
+ 	const u8 *data;
+ 	struct page **pages;
++
++	/* firmware loader private fields */
++	void *priv;
+ };
+ 
+ struct module;
+@@ -44,6 +47,8 @@ int request_firmware_nowait(
+ 	void (*cont)(const struct firmware *fw, void *context));
+ 
+ void release_firmware(const struct firmware *fw);
++int cache_firmware(const char *name);
++int uncache_firmware(const char *name);
+ #else
+ static inline int request_firmware(const struct firmware **fw,
+ 				   const char *name,
+@@ -62,6 +67,16 @@ static inline int request_firmware_nowait(
+ static inline void release_firmware(const struct firmware *fw)
+ {
+ }
++
++static inline int cache_firmware(const char *name)
++{
++	return -ENOENT;
++}
++
++static inline int uncache_firmware(const char *name)
++{
++	return -EINVAL;
++}
+ #endif
+ 
+ #endif
+diff --git a/include/linux/pm.h b/include/linux/pm.h
+index f067e60..88f034a 100644
+--- a/include/linux/pm.h
++++ b/include/linux/pm.h
+@@ -638,6 +638,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
+ 	} while (0)
+ 
+ extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
++extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *));
+ 
+ extern int pm_generic_prepare(struct device *dev);
+ extern int pm_generic_suspend_late(struct device *dev);
+@@ -677,6 +678,10 @@ static inline int device_pm_wait_for_dev(struct device *a, struct device *b)
+ 	return 0;
+ }
+ 
++static inline void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
++{
++}
++
+ #define pm_generic_prepare	NULL
+ #define pm_generic_suspend	NULL
+ #define pm_generic_resume	NULL
+-- 
+2.1.3
+
diff --git a/core/linux-sun4i/PKGBUILD b/core/linux-sun4i/PKGBUILD
index 8e676474a..1cfe5cf26 100644
--- a/core/linux-sun4i/PKGBUILD
+++ b/core/linux-sun4i/PKGBUILD
@@ -4,12 +4,12 @@
 buildarch=4
 
 pkgbase=linux-sun4i
-_commit=e37d760b363888f3a65cd6455c99a75cac70a7b8
+_commit=9a1cd034181af628d4145202289e1993c1687db6
 _srcname=linux-sunxi-${_commit}
 _kernelname=${pkgname#linux}
 _desc="AllWinner A10"
-pkgver=3.4.90
-pkgrel=4
+pkgver=3.4.103
+pkgrel=1
 arch=('armv7h')
 url="http://www.kernel.org/"
 license=('GPL2')
@@ -17,10 +17,12 @@ makedepends=('xmlto' 'docbook-xsl' 'uboot-mkimage' 'bc' 'kmod' 'inetutils' 'git'
 options=('!strip')
 source=("https://github.com/linux-sunxi/linux-sunxi/archive/${_commit}.tar.gz"
         '0001-Backport-BFP-XOR-operation.patch'
+        '0001-Backport-firmware-loader.patch'
         'config')
-md5sums=('dbf73f4cb6b1399e73449f4e91bf5694'
+md5sums=('6cdd44f9131a854cdbf5ddad86187c57'
          'ca5b466850782493b99e824d6efbea4d'
-         '079150dcdc1cb81f02d0b31730c333e6')
+         '520bde63dd6803f3332e9b26cb34cff9'
+         '4592842f7b5702452815d5601f43ab7a')
 
 prepare() {
   cd "${srcdir}/${_srcname}"
@@ -28,6 +30,7 @@ prepare() {
   cat "${srcdir}/config" > ./.config
 
   git apply ../0001-Backport-BFP-XOR-operation.patch
+  git apply ../0001-Backport-firmware-loader.patch
 
   # add pkgrel to extraversion
   sed -ri "s|^(EXTRAVERSION =)(.*)|\1 \2-${pkgrel}|" Makefile
diff --git a/core/linux-sun4i/config b/core/linux-sun4i/config
index 7bcd0b9c2..cdbd6cf86 100644
--- a/core/linux-sun4i/config
+++ b/core/linux-sun4i/config
@@ -1,6 +1,6 @@
 #
 # Automatically generated file; DO NOT EDIT.
-# Linux/arm 3.4.90-4 Kernel Configuration
+# Linux/arm 3.4.90-5 Kernel Configuration
 #
 CONFIG_ARM=y
 CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -996,6 +996,7 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y
 CONFIG_FW_LOADER=y
 CONFIG_FIRMWARE_IN_KERNEL=y
 CONFIG_EXTRA_FIRMWARE=""
+# CONFIG_FW_LOADER_USER_HELPER is not set
 # CONFIG_DEBUG_DRIVER is not set
 # CONFIG_DEBUG_DEVRES is not set
 # CONFIG_SYS_HYPERVISOR is not set