Merge tag 'driver-core-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core updates from Greg KH: "Here is the "big" set of driver core changes for 5.7-rc1. Nothing huge in here, just lots of little firmware core changes and use of new apis, a libfs fix, a debugfs api change, and some driver core deferred probe rework. All of these have been in linux-next for a while with no reported issues" * tag 'driver-core-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (44 commits) Revert "driver core: Set fw_devlink to "permissive" behavior by default" driver core: Set fw_devlink to "permissive" behavior by default driver core: Replace open-coded list_last_entry() driver core: Read atomic counter once in driver_probe_done() libfs: fix infoleak in simple_attr_read() driver core: Add device links from fwnode only for the primary device platform/x86: touchscreen_dmi: Add info for the Chuwi Vi8 Plus tablet platform/x86: touchscreen_dmi: Add EFI embedded firmware info support Input: icn8505 - Switch to firmware_request_platform for retreiving the fw Input: silead - Switch to firmware_request_platform for retreiving the fw selftests: firmware: Add firmware_request_platform tests test_firmware: add support for firmware_request_platform firmware: Add new platform fallback mechanism and firmware_request_platform() Revert "drivers: base: power: wakeup.c: Use built-in RCU list checking" drivers: base: power: wakeup.c: Use built-in RCU list checking component: allow missing unbind callback debugfs: remove return value of debugfs_create_file_size() debugfs: Check module state before warning in {full/open}_proxy_open() firmware: fix a double abort case with fw_load_sysfs_fallback arch_topology: Fix putting invalid cpu clk ...
This commit is contained in:
@@ -94,7 +94,7 @@ static void update_topology_flags_workfn(struct work_struct *work)
|
||||
update_topology = 0;
|
||||
}
|
||||
|
||||
static u32 capacity_scale;
|
||||
static DEFINE_PER_CPU(u32, freq_factor) = 1;
|
||||
static u32 *raw_capacity;
|
||||
|
||||
static int free_raw_capacity(void)
|
||||
@@ -108,17 +108,23 @@ static int free_raw_capacity(void)
|
||||
void topology_normalize_cpu_scale(void)
|
||||
{
|
||||
u64 capacity;
|
||||
u64 capacity_scale;
|
||||
int cpu;
|
||||
|
||||
if (!raw_capacity)
|
||||
return;
|
||||
|
||||
pr_debug("cpu_capacity: capacity_scale=%u\n", capacity_scale);
|
||||
capacity_scale = 1;
|
||||
for_each_possible_cpu(cpu) {
|
||||
pr_debug("cpu_capacity: cpu=%d raw_capacity=%u\n",
|
||||
cpu, raw_capacity[cpu]);
|
||||
capacity = (raw_capacity[cpu] << SCHED_CAPACITY_SHIFT)
|
||||
/ capacity_scale;
|
||||
capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
|
||||
capacity_scale = max(capacity, capacity_scale);
|
||||
}
|
||||
|
||||
pr_debug("cpu_capacity: capacity_scale=%llu\n", capacity_scale);
|
||||
for_each_possible_cpu(cpu) {
|
||||
capacity = raw_capacity[cpu] * per_cpu(freq_factor, cpu);
|
||||
capacity = div64_u64(capacity << SCHED_CAPACITY_SHIFT,
|
||||
capacity_scale);
|
||||
topology_set_cpu_scale(cpu, capacity);
|
||||
pr_debug("cpu_capacity: CPU%d cpu_capacity=%lu\n",
|
||||
cpu, topology_get_cpu_scale(cpu));
|
||||
@@ -127,6 +133,7 @@ void topology_normalize_cpu_scale(void)
|
||||
|
||||
bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
|
||||
{
|
||||
struct clk *cpu_clk;
|
||||
static bool cap_parsing_failed;
|
||||
int ret;
|
||||
u32 cpu_capacity;
|
||||
@@ -146,10 +153,22 @@ bool __init topology_parse_cpu_capacity(struct device_node *cpu_node, int cpu)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
capacity_scale = max(cpu_capacity, capacity_scale);
|
||||
raw_capacity[cpu] = cpu_capacity;
|
||||
pr_debug("cpu_capacity: %pOF cpu_capacity=%u (raw)\n",
|
||||
cpu_node, raw_capacity[cpu]);
|
||||
|
||||
/*
|
||||
* Update freq_factor for calculating early boot cpu capacities.
|
||||
* For non-clk CPU DVFS mechanism, there's no way to get the
|
||||
* frequency value now, assuming they are running at the same
|
||||
* frequency (by keeping the initial freq_factor value).
|
||||
*/
|
||||
cpu_clk = of_clk_get(cpu_node, 0);
|
||||
if (!PTR_ERR_OR_ZERO(cpu_clk)) {
|
||||
per_cpu(freq_factor, cpu) =
|
||||
clk_get_rate(cpu_clk) / 1000;
|
||||
clk_put(cpu_clk);
|
||||
}
|
||||
} else {
|
||||
if (raw_capacity) {
|
||||
pr_err("cpu_capacity: missing %pOF raw capacity\n",
|
||||
@@ -188,11 +207,8 @@ init_cpu_capacity_callback(struct notifier_block *nb,
|
||||
|
||||
cpumask_andnot(cpus_to_visit, cpus_to_visit, policy->related_cpus);
|
||||
|
||||
for_each_cpu(cpu, policy->related_cpus) {
|
||||
raw_capacity[cpu] = topology_get_cpu_scale(cpu) *
|
||||
policy->cpuinfo.max_freq / 1000UL;
|
||||
capacity_scale = max(raw_capacity[cpu], capacity_scale);
|
||||
}
|
||||
for_each_cpu(cpu, policy->related_cpus)
|
||||
per_cpu(freq_factor, cpu) = policy->cpuinfo.max_freq / 1000;
|
||||
|
||||
if (cpumask_empty(cpus_to_visit)) {
|
||||
topology_normalize_cpu_scale();
|
||||
@@ -281,7 +297,7 @@ static int __init get_cpu_for_node(struct device_node *node)
|
||||
static int __init parse_core(struct device_node *core, int package_id,
|
||||
int core_id)
|
||||
{
|
||||
char name[10];
|
||||
char name[20];
|
||||
bool leaf = true;
|
||||
int i = 0;
|
||||
int cpu;
|
||||
@@ -327,7 +343,7 @@ static int __init parse_core(struct device_node *core, int package_id,
|
||||
|
||||
static int __init parse_cluster(struct device_node *cluster, int depth)
|
||||
{
|
||||
char name[10];
|
||||
char name[20];
|
||||
bool leaf = true;
|
||||
bool has_cores = false;
|
||||
struct device_node *c;
|
||||
|
@@ -528,7 +528,8 @@ static void component_unbind(struct component *component,
|
||||
{
|
||||
WARN_ON(!component->bound);
|
||||
|
||||
component->ops->unbind(component->dev, master->dev, data);
|
||||
if (component->ops && component->ops->unbind)
|
||||
component->ops->unbind(component->dev, master->dev, data);
|
||||
component->bound = false;
|
||||
|
||||
/* Release all resources claimed in the binding of this component */
|
||||
|
@@ -64,12 +64,12 @@ static inline void device_links_write_unlock(void)
|
||||
mutex_unlock(&device_links_lock);
|
||||
}
|
||||
|
||||
int device_links_read_lock(void)
|
||||
int device_links_read_lock(void) __acquires(&device_links_srcu)
|
||||
{
|
||||
return srcu_read_lock(&device_links_srcu);
|
||||
}
|
||||
|
||||
void device_links_read_unlock(int idx)
|
||||
void device_links_read_unlock(int idx) __releases(&device_links_srcu)
|
||||
{
|
||||
srcu_read_unlock(&device_links_srcu, idx);
|
||||
}
|
||||
@@ -523,9 +523,13 @@ static void device_link_add_missing_supplier_links(void)
|
||||
|
||||
mutex_lock(&wfs_lock);
|
||||
list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
|
||||
links.needs_suppliers)
|
||||
if (!fwnode_call_int_op(dev->fwnode, add_links, dev))
|
||||
links.needs_suppliers) {
|
||||
int ret = fwnode_call_int_op(dev->fwnode, add_links, dev);
|
||||
if (!ret)
|
||||
list_del_init(&dev->links.needs_suppliers);
|
||||
else if (ret != -ENODEV)
|
||||
dev->links.need_for_probe = false;
|
||||
}
|
||||
mutex_unlock(&wfs_lock);
|
||||
}
|
||||
|
||||
@@ -2341,6 +2345,31 @@ static int device_private_init(struct device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 fw_devlink_flags;
|
||||
static int __init fw_devlink_setup(char *arg)
|
||||
{
|
||||
if (!arg)
|
||||
return -EINVAL;
|
||||
|
||||
if (strcmp(arg, "off") == 0) {
|
||||
fw_devlink_flags = 0;
|
||||
} else if (strcmp(arg, "permissive") == 0) {
|
||||
fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY;
|
||||
} else if (strcmp(arg, "on") == 0) {
|
||||
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER;
|
||||
} else if (strcmp(arg, "rpm") == 0) {
|
||||
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER |
|
||||
DL_FLAG_PM_RUNTIME;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
early_param("fw_devlink", fw_devlink_setup);
|
||||
|
||||
u32 fw_devlink_get_flags(void)
|
||||
{
|
||||
return fw_devlink_flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* device_add - add device to device hierarchy.
|
||||
* @dev: device.
|
||||
@@ -2375,6 +2404,7 @@ int device_add(struct device *dev)
|
||||
struct class_interface *class_intf;
|
||||
int error = -EINVAL, fw_ret;
|
||||
struct kobject *glue_dir = NULL;
|
||||
bool is_fwnode_dev = false;
|
||||
|
||||
dev = get_device(dev);
|
||||
if (!dev)
|
||||
@@ -2472,8 +2502,10 @@ int device_add(struct device *dev)
|
||||
|
||||
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
||||
|
||||
if (dev->fwnode && !dev->fwnode->dev)
|
||||
if (dev->fwnode && !dev->fwnode->dev) {
|
||||
dev->fwnode->dev = dev;
|
||||
is_fwnode_dev = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if any of the other devices (consumers) have been waiting for
|
||||
@@ -2489,7 +2521,8 @@ int device_add(struct device *dev)
|
||||
*/
|
||||
device_link_add_missing_supplier_links();
|
||||
|
||||
if (fwnode_has_op(dev->fwnode, add_links)) {
|
||||
if (fw_devlink_flags && is_fwnode_dev &&
|
||||
fwnode_has_op(dev->fwnode, add_links)) {
|
||||
fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev);
|
||||
if (fw_ret == -ENODEV)
|
||||
device_link_wait_for_mandatory_supplier(dev);
|
||||
|
@@ -231,8 +231,7 @@ static struct cpu_attr cpu_attrs[] = {
|
||||
static ssize_t print_cpus_kernel_max(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int n = snprintf(buf, PAGE_SIZE-2, "%d\n", NR_CPUS - 1);
|
||||
return n;
|
||||
return sprintf(buf, "%d\n", NR_CPUS - 1);
|
||||
}
|
||||
static DEVICE_ATTR(kernel_max, 0444, print_cpus_kernel_max, NULL);
|
||||
|
||||
@@ -258,13 +257,13 @@ static ssize_t print_cpus_offline(struct device *dev,
|
||||
buf[n++] = ',';
|
||||
|
||||
if (nr_cpu_ids == total_cpus-1)
|
||||
n += snprintf(&buf[n], len - n, "%u", nr_cpu_ids);
|
||||
n += scnprintf(&buf[n], len - n, "%u", nr_cpu_ids);
|
||||
else
|
||||
n += snprintf(&buf[n], len - n, "%u-%d",
|
||||
n += scnprintf(&buf[n], len - n, "%u-%d",
|
||||
nr_cpu_ids, total_cpus-1);
|
||||
}
|
||||
|
||||
n += snprintf(&buf[n], len - n, "\n");
|
||||
n += scnprintf(&buf[n], len - n, "\n");
|
||||
return n;
|
||||
}
|
||||
static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
|
||||
@@ -272,7 +271,7 @@ static DEVICE_ATTR(offline, 0444, print_cpus_offline, NULL);
|
||||
static ssize_t print_cpus_isolated(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int n = 0, len = PAGE_SIZE-2;
|
||||
int n;
|
||||
cpumask_var_t isolated;
|
||||
|
||||
if (!alloc_cpumask_var(&isolated, GFP_KERNEL))
|
||||
@@ -280,7 +279,7 @@ static ssize_t print_cpus_isolated(struct device *dev,
|
||||
|
||||
cpumask_andnot(isolated, cpu_possible_mask,
|
||||
housekeeping_cpumask(HK_FLAG_DOMAIN));
|
||||
n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(isolated));
|
||||
n = sprintf(buf, "%*pbl\n", cpumask_pr_args(isolated));
|
||||
|
||||
free_cpumask_var(isolated);
|
||||
|
||||
@@ -292,11 +291,7 @@ static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL);
|
||||
static ssize_t print_cpus_nohz_full(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int n = 0, len = PAGE_SIZE-2;
|
||||
|
||||
n = scnprintf(buf, len, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
|
||||
|
||||
return n;
|
||||
return sprintf(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask));
|
||||
}
|
||||
static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL);
|
||||
#endif
|
||||
|
@@ -224,76 +224,52 @@ static int deferred_devs_show(struct seq_file *s, void *data)
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(deferred_devs);
|
||||
|
||||
static int deferred_probe_timeout = -1;
|
||||
#ifdef CONFIG_MODULES
|
||||
/*
|
||||
* In the case of modules, set the default probe timeout to
|
||||
* 30 seconds to give userland some time to load needed modules
|
||||
*/
|
||||
int driver_deferred_probe_timeout = 30;
|
||||
#else
|
||||
/* In the case of !modules, no probe timeout needed */
|
||||
int driver_deferred_probe_timeout = -1;
|
||||
#endif
|
||||
EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout);
|
||||
|
||||
static int __init deferred_probe_timeout_setup(char *str)
|
||||
{
|
||||
int timeout;
|
||||
|
||||
if (!kstrtoint(str, 10, &timeout))
|
||||
deferred_probe_timeout = timeout;
|
||||
driver_deferred_probe_timeout = timeout;
|
||||
return 1;
|
||||
}
|
||||
__setup("deferred_probe_timeout=", deferred_probe_timeout_setup);
|
||||
|
||||
static int __driver_deferred_probe_check_state(struct device *dev)
|
||||
{
|
||||
if (!initcalls_done)
|
||||
return -EPROBE_DEFER;
|
||||
|
||||
if (!deferred_probe_timeout) {
|
||||
dev_WARN(dev, "deferred probe timeout, ignoring dependency");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* driver_deferred_probe_check_state() - Check deferred probe state
|
||||
* @dev: device to check
|
||||
*
|
||||
* Returns -ENODEV if init is done and all built-in drivers have had a chance
|
||||
* to probe (i.e. initcalls are done), -ETIMEDOUT if deferred probe debug
|
||||
* timeout has expired, or -EPROBE_DEFER if none of those conditions are met.
|
||||
* Return:
|
||||
* -ENODEV if initcalls have completed and modules are disabled.
|
||||
* -ETIMEDOUT if the deferred probe timeout was set and has expired
|
||||
* and modules are enabled.
|
||||
* -EPROBE_DEFER in other cases.
|
||||
*
|
||||
* Drivers or subsystems can opt-in to calling this function instead of directly
|
||||
* returning -EPROBE_DEFER.
|
||||
*/
|
||||
int driver_deferred_probe_check_state(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) {
|
||||
dev_warn(dev, "ignoring dependency for device, assuming no driver");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = __driver_deferred_probe_check_state(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_warn(dev, "ignoring dependency for device, assuming no driver");
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* driver_deferred_probe_check_state_continue() - check deferred probe state
|
||||
* @dev: device to check
|
||||
*
|
||||
* Returns -ETIMEDOUT if deferred probe debug timeout has expired, or
|
||||
* -EPROBE_DEFER otherwise.
|
||||
*
|
||||
* Drivers or subsystems can opt-in to calling this function instead of
|
||||
* directly returning -EPROBE_DEFER.
|
||||
*
|
||||
* This is similar to driver_deferred_probe_check_state(), but it allows the
|
||||
* subsystem to keep deferring probe after built-in drivers have had a chance
|
||||
* to probe. One scenario where that is useful is if built-in drivers rely on
|
||||
* resources that are provided by modular drivers.
|
||||
*/
|
||||
int driver_deferred_probe_check_state_continue(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __driver_deferred_probe_check_state(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!driver_deferred_probe_timeout) {
|
||||
dev_WARN(dev, "deferred probe timeout, ignoring dependency");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
@@ -302,7 +278,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
|
||||
{
|
||||
struct device_private *private, *p;
|
||||
|
||||
deferred_probe_timeout = 0;
|
||||
driver_deferred_probe_timeout = 0;
|
||||
driver_deferred_probe_trigger();
|
||||
flush_work(&deferred_probe_work);
|
||||
|
||||
@@ -336,9 +312,9 @@ static int deferred_probe_initcall(void)
|
||||
driver_deferred_probe_trigger();
|
||||
flush_work(&deferred_probe_work);
|
||||
|
||||
if (deferred_probe_timeout > 0) {
|
||||
if (driver_deferred_probe_timeout > 0) {
|
||||
schedule_delayed_work(&deferred_probe_timeout_work,
|
||||
deferred_probe_timeout * HZ);
|
||||
driver_deferred_probe_timeout * HZ);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -668,9 +644,10 @@ static int really_probe_debug(struct device *dev, struct device_driver *drv)
|
||||
*/
|
||||
int driver_probe_done(void)
|
||||
{
|
||||
pr_debug("%s: probe_count = %d\n", __func__,
|
||||
atomic_read(&probe_count));
|
||||
if (atomic_read(&probe_count))
|
||||
int local_probe_count = atomic_read(&probe_count);
|
||||
|
||||
pr_debug("%s: probe_count = %d\n", __func__, local_probe_count);
|
||||
if (local_probe_count)
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
@@ -1222,7 +1199,7 @@ void driver_detach(struct device_driver *drv)
|
||||
spin_unlock(&drv->p->klist_devices.k_lock);
|
||||
break;
|
||||
}
|
||||
dev_prv = list_entry(drv->p->klist_devices.k_list.prev,
|
||||
dev_prv = list_last_entry(&drv->p->klist_devices.k_list,
|
||||
struct device_private,
|
||||
knode_driver.n_node);
|
||||
dev = dev_prv->device;
|
||||
|
@@ -5,5 +5,6 @@ obj-$(CONFIG_FW_LOADER_USER_HELPER) += fallback_table.o
|
||||
obj-$(CONFIG_FW_LOADER) += firmware_class.o
|
||||
firmware_class-objs := main.o
|
||||
firmware_class-$(CONFIG_FW_LOADER_USER_HELPER) += fallback.o
|
||||
firmware_class-$(CONFIG_EFI_EMBEDDED_FIRMWARE) += fallback_platform.o
|
||||
|
||||
obj-y += builtin/
|
||||
|
@@ -525,7 +525,7 @@ static int fw_load_sysfs_fallback(struct fw_sysfs *fw_sysfs,
|
||||
}
|
||||
|
||||
retval = fw_sysfs_wait_timeout(fw_priv, timeout);
|
||||
if (retval < 0) {
|
||||
if (retval < 0 && retval != -ENOENT) {
|
||||
mutex_lock(&fw_lock);
|
||||
fw_load_abort(fw_sysfs);
|
||||
mutex_unlock(&fw_lock);
|
||||
|
@@ -66,4 +66,14 @@ static inline void unregister_sysfs_loader(void)
|
||||
}
|
||||
#endif /* CONFIG_FW_LOADER_USER_HELPER */
|
||||
|
||||
#ifdef CONFIG_EFI_EMBEDDED_FIRMWARE
|
||||
int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags);
|
||||
#else
|
||||
static inline int firmware_fallback_platform(struct fw_priv *fw_priv,
|
||||
enum fw_opt opt_flags)
|
||||
{
|
||||
return -ENOENT;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __FIRMWARE_FALLBACK_H */
|
||||
|
36
drivers/base/firmware_loader/fallback_platform.c
Normal file
36
drivers/base/firmware_loader/fallback_platform.c
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
#include <linux/efi_embedded_fw.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include "fallback.h"
|
||||
#include "firmware.h"
|
||||
|
||||
int firmware_fallback_platform(struct fw_priv *fw_priv, enum fw_opt opt_flags)
|
||||
{
|
||||
const u8 *data;
|
||||
size_t size;
|
||||
int rc;
|
||||
|
||||
if (!(opt_flags & FW_OPT_FALLBACK_PLATFORM))
|
||||
return -ENOENT;
|
||||
|
||||
rc = security_kernel_load_data(LOADING_FIRMWARE_EFI_EMBEDDED);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = efi_get_embedded_fw(fw_priv->fw_name, &data, &size);
|
||||
if (rc)
|
||||
return rc; /* rc == -ENOENT when the fw was not found */
|
||||
|
||||
fw_priv->data = vmalloc(size);
|
||||
if (!fw_priv->data)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(fw_priv->data, data, size);
|
||||
fw_priv->size = size;
|
||||
fw_state_done(fw_priv);
|
||||
return 0;
|
||||
}
|
@@ -29,6 +29,9 @@
|
||||
* firmware caching mechanism.
|
||||
* @FW_OPT_NOFALLBACK_SYSFS: Disable the sysfs fallback mechanism. Takes
|
||||
* precedence over &FW_OPT_UEVENT and &FW_OPT_USERHELPER.
|
||||
* @FW_OPT_FALLBACK_PLATFORM: Enable fallback to device fw copy embedded in
|
||||
* the platform's main firmware. If both this fallback and the sysfs
|
||||
* fallback are enabled, then this fallback will be tried first.
|
||||
*/
|
||||
enum fw_opt {
|
||||
FW_OPT_UEVENT = BIT(0),
|
||||
@@ -37,6 +40,7 @@ enum fw_opt {
|
||||
FW_OPT_NO_WARN = BIT(3),
|
||||
FW_OPT_NOCACHE = BIT(4),
|
||||
FW_OPT_NOFALLBACK_SYSFS = BIT(5),
|
||||
FW_OPT_FALLBACK_PLATFORM = BIT(6),
|
||||
};
|
||||
|
||||
enum fw_status {
|
||||
|
@@ -493,8 +493,10 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
|
||||
}
|
||||
|
||||
fw_priv->size = 0;
|
||||
rc = kernel_read_file_from_path(path, &buffer, &size,
|
||||
msize, id);
|
||||
|
||||
/* load firmware files from the mount namespace of init */
|
||||
rc = kernel_read_file_from_path_initns(path, &buffer,
|
||||
&size, msize, id);
|
||||
if (rc) {
|
||||
if (rc != -ENOENT)
|
||||
dev_warn(device, "loading %s failed with error %d\n",
|
||||
@@ -776,6 +778,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
|
||||
fw_decompress_xz);
|
||||
#endif
|
||||
|
||||
if (ret == -ENOENT)
|
||||
ret = firmware_fallback_platform(fw->priv, opt_flags);
|
||||
|
||||
if (ret) {
|
||||
if (!(opt_flags & FW_OPT_NO_WARN))
|
||||
dev_warn(device,
|
||||
@@ -883,6 +888,30 @@ int request_firmware_direct(const struct firmware **firmware_p,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(request_firmware_direct);
|
||||
|
||||
/**
|
||||
* firmware_request_platform() - request firmware with platform-fw fallback
|
||||
* @firmware: pointer to firmware image
|
||||
* @name: name of firmware file
|
||||
* @device: device for which firmware is being loaded
|
||||
*
|
||||
* This function is similar in behaviour to request_firmware, except that if
|
||||
* direct filesystem lookup fails, it will fallback to looking for a copy of the
|
||||
* requested firmware embedded in the platform's main (e.g. UEFI) firmware.
|
||||
**/
|
||||
int firmware_request_platform(const struct firmware **firmware,
|
||||
const char *name, struct device *device)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Need to pin this module until return */
|
||||
__module_get(THIS_MODULE);
|
||||
ret = _request_firmware(firmware, name, device, NULL, 0,
|
||||
FW_OPT_UEVENT | FW_OPT_FALLBACK_PLATFORM);
|
||||
module_put(THIS_MODULE);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(firmware_request_platform);
|
||||
|
||||
/**
|
||||
* firmware_request_cache() - cache firmware for suspend so resume can use it
|
||||
* @name: name of firmware file
|
||||
|
مرجع در شماره جدید
Block a user