summaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Kconfig28
-rw-r--r--drivers/base/arch_topology.c8
-rw-r--r--drivers/base/attribute_container.c3
-rw-r--r--drivers/base/bus.c6
-rw-r--r--drivers/base/cacheinfo.c13
-rw-r--r--drivers/base/class.c4
-rw-r--r--drivers/base/component.c83
-rw-r--r--drivers/base/container.c5
-rw-r--r--drivers/base/core.c11
-rw-r--r--drivers/base/cpu.c1
-rw-r--r--drivers/base/dd.c43
-rw-r--r--drivers/base/devcoredump.c17
-rw-r--r--drivers/base/devres.c3
-rw-r--r--drivers/base/dma-contiguous.c6
-rw-r--r--drivers/base/dma-mapping.c3
-rw-r--r--drivers/base/driver.c6
-rw-r--r--drivers/base/firmware.c3
-rw-r--r--drivers/base/firmware_class.c859
-rw-r--r--drivers/base/hypervisor.c3
-rw-r--r--drivers/base/init.c3
-rw-r--r--drivers/base/isa.c1
-rw-r--r--drivers/base/map.c2
-rw-r--r--drivers/base/memory.c2
-rw-r--r--drivers/base/module.c4
-rw-r--r--drivers/base/pinctrl.c3
-rw-r--r--drivers/base/platform-msi.c13
-rw-r--r--drivers/base/platform.c3
-rw-r--r--drivers/base/power/domain.c69
-rw-r--r--drivers/base/power/main.c415
-rw-r--r--drivers/base/power/power.h11
-rw-r--r--drivers/base/power/runtime.c84
-rw-r--r--drivers/base/power/sysfs.c182
-rw-r--r--drivers/base/power/wakeirq.c8
-rw-r--r--drivers/base/power/wakeup.c26
-rw-r--r--drivers/base/property.c184
-rw-r--r--drivers/base/regmap/Kconfig10
-rw-r--r--drivers/base/regmap/Makefile2
-rw-r--r--drivers/base/regmap/internal.h8
-rw-r--r--drivers/base/regmap/regcache-flat.c15
-rw-r--r--drivers/base/regmap/regmap-debugfs.c12
-rw-r--r--drivers/base/regmap/regmap-sdw.c88
-rw-r--r--drivers/base/regmap/regmap-slimbus.c80
-rw-r--r--drivers/base/regmap/regmap.c47
-rw-r--r--drivers/base/soc.c2
-rw-r--r--drivers/base/syscore.c3
-rw-r--r--drivers/base/test/test_async_driver_probe.c10
-rw-r--r--drivers/base/topology.c17
-rw-r--r--drivers/base/transport_class.c3
48 files changed, 1497 insertions, 915 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 49fd50fccd48..29b0eb452b3a 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -86,10 +86,9 @@ config FW_LOADER
require userspace firmware loading support, but a module built
out-of-tree does.
-config FIRMWARE_IN_KERNEL
- bool "Include in-kernel firmware blobs in kernel binary"
+config EXTRA_FIRMWARE
+ string "External firmware blobs to build into the kernel binary"
depends on FW_LOADER
- default y
help
Various drivers in the kernel source tree may require firmware,
which is generally available in your distribution's linux-firmware
@@ -99,23 +98,6 @@ config FIRMWARE_IN_KERNEL
/lib/firmware/ on your system, so they can be loaded by userspace
helpers on request.
- Enabling this option will build each required firmware blob
- specified by EXTRA_FIRMWARE into the kernel directly, where
- request_firmware() will find them without having to call out to
- userspace. This may be useful if your root file system requires a
- device that uses such firmware and you do not wish to use an
- initrd.
-
- This single option controls the inclusion of firmware for
- every driver that uses request_firmware(), which avoids a
- proliferation of 'Include firmware for xxx device' options.
-
- Say 'N' and let firmware be loaded from userspace.
-
-config EXTRA_FIRMWARE
- string "External firmware blobs to build into the kernel binary"
- depends on FW_LOADER
- help
This option allows firmware to be built into the kernel for the case
where the user either cannot or doesn't want to provide it from
userspace at runtime (for example, when the firmware in question is
@@ -126,11 +108,11 @@ config EXTRA_FIRMWARE
firmware files -- the same names that appear in MODULE_FIRMWARE()
and request_firmware() in the source. These files should exist under
the directory specified by the EXTRA_FIRMWARE_DIR option, which is
- by default the firmware subdirectory of the kernel source tree.
+ /lib/firmware by default.
For example, you might set CONFIG_EXTRA_FIRMWARE="usb8388.bin", copy
- the usb8388.bin file into the firmware directory, and build the kernel.
- Then any request_firmware("usb8388.bin") will be satisfied internally
+ the usb8388.bin file into /lib/firmware, and build the kernel. Then
+ any request_firmware("usb8388.bin") will be satisfied internally
without needing to call out to userspace.
WARNING: If you include additional firmware files into your binary
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 4de87b0b53c8..52ec5174bcb1 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -1,15 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Arch specific cpu topology information
*
* Copyright (C) 2016, ARM Ltd.
* Written by: Juri Lelli, ARM Ltd.
- *
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Released under the GPLv2 only.
- * SPDX-License-Identifier: GPL-2.0
*/
#include <linux/acpi.h>
diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c
index 95e3ef82f3b7..20736aaa0e69 100644
--- a/drivers/base/attribute_container.c
+++ b/drivers/base/attribute_container.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* attribute_container.c - implementation of a simple container for classes
*
* Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
*
- * This file is licensed under GPLv2
- *
* The basic idea here is to enable a device to be attached to an
* aritrary numer of classes without having to allocate storage for them.
* Instead, the contained classes select the devices they need to attach
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 22a64fd3309b..ef6183306b40 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* bus.c - bus driver management
*
@@ -5,9 +6,6 @@
* Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2007 Novell Inc.
- *
- * This file is released under the GPLv2
- *
*/
#include <linux/async.h>
@@ -309,7 +307,7 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start,
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
- while ((dev = next_device(&i)) && !error)
+ while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
return error;
diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c
index 07532d83be0b..edf726267282 100644
--- a/drivers/base/cacheinfo.c
+++ b/drivers/base/cacheinfo.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* cacheinfo support - processor cache information via sysfs
*
* Based on arch/x86/kernel/cpu/intel_cacheinfo.c
* Author: Sudeep Holla <sudeep.holla@arm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether express or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/base/class.c b/drivers/base/class.c
index 52eb8e644acd..54def4e02f00 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* class.c - basic device class management
*
@@ -5,9 +6,6 @@
* Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2003-2004 Greg Kroah-Hartman
* Copyright (c) 2003-2004 IBM Corp.
- *
- * This file is released under the GPLv2
- *
*/
#include <linux/device.h>
diff --git a/drivers/base/component.c b/drivers/base/component.c
index 89b032f2ffd2..8946dfee4768 100644
--- a/drivers/base/component.c
+++ b/drivers/base/component.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Componentized device handling.
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
* This is work in progress. We gather up the component devices into a list,
* and bind them when instructed. At the moment, we're specific to the DRM
* subsystem, and only handles one master device, but this doesn't have to be
@@ -17,6 +14,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
+#include <linux/debugfs.h>
struct component;
@@ -41,6 +39,7 @@ struct master {
const struct component_master_ops *ops;
struct device *dev;
struct component_match *match;
+ struct dentry *dentry;
};
struct component {
@@ -56,6 +55,80 @@ static DEFINE_MUTEX(component_mutex);
static LIST_HEAD(component_list);
static LIST_HEAD(masters);
+#ifdef CONFIG_DEBUG_FS
+
+static struct dentry *component_debugfs_dir;
+
+static int component_devices_show(struct seq_file *s, void *data)
+{
+ struct master *m = s->private;
+ struct component_match *match = m->match;
+ size_t i;
+
+ mutex_lock(&component_mutex);
+ seq_printf(s, "%-40s %20s\n", "master name", "status");
+ seq_puts(s, "-------------------------------------------------------------\n");
+ seq_printf(s, "%-40s %20s\n\n",
+ dev_name(m->dev), m->bound ? "bound" : "not bound");
+
+ seq_printf(s, "%-40s %20s\n", "device name", "status");
+ seq_puts(s, "-------------------------------------------------------------\n");
+ for (i = 0; i < match->num; i++) {
+ struct device *d = (struct device *)match->compare[i].data;
+
+ seq_printf(s, "%-40s %20s\n", dev_name(d),
+ match->compare[i].component ?
+ "registered" : "not registered");
+ }
+ mutex_unlock(&component_mutex);
+
+ return 0;
+}
+
+static int component_devices_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, component_devices_show, inode->i_private);
+}
+
+static const struct file_operations component_devices_fops = {
+ .open = component_devices_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init component_debug_init(void)
+{
+ component_debugfs_dir = debugfs_create_dir("device_component", NULL);
+
+ return 0;
+}
+
+core_initcall(component_debug_init);
+
+static void component_master_debugfs_add(struct master *m)
+{
+ m->dentry = debugfs_create_file(dev_name(m->dev), 0444,
+ component_debugfs_dir,
+ m, &component_devices_fops);
+}
+
+static void component_master_debugfs_del(struct master *m)
+{
+ debugfs_remove(m->dentry);
+ m->dentry = NULL;
+}
+
+#else
+
+static void component_master_debugfs_add(struct master *m)
+{ }
+
+static void component_master_debugfs_del(struct master *m)
+{ }
+
+#endif
+
static struct master *__master_find(struct device *dev,
const struct component_master_ops *ops)
{
@@ -290,6 +363,7 @@ static void free_master(struct master *master)
struct component_match *match = master->match;
int i;
+ component_master_debugfs_del(master);
list_del(&master->node);
if (match) {
@@ -323,6 +397,7 @@ int component_master_add_with_match(struct device *dev,
master->ops = ops;
master->match = match;
+ component_master_debugfs_add(master);
/* Add to the list of available masters. */
mutex_lock(&component_mutex);
list_add(&master->node, &masters);
diff --git a/drivers/base/container.c b/drivers/base/container.c
index ecbfbe2e908f..1ba42d2d3532 100644
--- a/drivers/base/container.c
+++ b/drivers/base/container.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* System bus type for containers.
*
* Copyright (C) 2013, Intel Corporation
* Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/container.h>
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 110230d86527..b2261f92f2f1 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* drivers/base/core.c - core driver model code (device registration, etc)
*
@@ -5,9 +6,6 @@
* Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2006 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2006 Novell, Inc.
- *
- * This file is released under the GPLv2
- *
*/
#include <linux/device.h>
@@ -22,7 +20,6 @@
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/genhd.h>
-#include <linux/kallsyms.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/netdevice.h>
@@ -687,8 +684,8 @@ static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
if (dev_attr->show)
ret = dev_attr->show(dev, dev_attr, buf);
if (ret >= (ssize_t)PAGE_SIZE) {
- print_symbol("dev_attr_show: %s returned bad count\n",
- (unsigned long)dev_attr->show);
+ printk("dev_attr_show: %pS returned bad count\n",
+ dev_attr->show);
}
return ret;
}
@@ -2116,7 +2113,7 @@ int device_for_each_child(struct device *parent, void *data,
return 0;
klist_iter_init(&parent->p->klist_children, &i);
- while ((child = next_device(&i)) && !error)
+ while (!error && (child = next_device(&i)))
error = fn(child, data);
klist_iter_exit(&i);
return error;
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index d99038487a0d..d21a2d913107 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* CPU subsystem support
*/
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 2c964f56dafe..de6fd092bf2f 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* drivers/base/dd.c - The core device/driver interactions.
*
@@ -13,8 +14,6 @@
* Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2007-2009 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2007-2009 Novell Inc.
- *
- * This file is released under the GPLv2
*/
#include <linux/device.h>
@@ -289,6 +288,18 @@ static void driver_bound(struct device *dev)
kobject_uevent(&dev->kobj, KOBJ_BIND);
}
+static ssize_t coredump_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ device_lock(dev);
+ if (dev->driver->coredump)
+ dev->driver->coredump(dev);
+ device_unlock(dev);
+
+ return count;
+}
+static DEVICE_ATTR_WO(coredump);
+
static int driver_sysfs_add(struct device *dev)
{
int ret;
@@ -298,14 +309,26 @@ static int driver_sysfs_add(struct device *dev)
BUS_NOTIFY_BIND_DRIVER, dev);
ret = sysfs_create_link(&dev->driver->p->kobj, &dev->kobj,
+ kobject_name(&dev->kobj));
+ if (ret)
+ goto fail;
+
+ ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
+ "driver");
+ if (ret)
+ goto rm_dev;
+
+ if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump ||
+ !device_create_file(dev, &dev_attr_coredump))
+ return 0;
+
+ sysfs_remove_link(&dev->kobj, "driver");
+
+rm_dev:
+ sysfs_remove_link(&dev->driver->p->kobj,
kobject_name(&dev->kobj));
- if (ret == 0) {
- ret = sysfs_create_link(&dev->kobj, &dev->driver->p->kobj,
- "driver");
- if (ret)
- sysfs_remove_link(&dev->driver->p->kobj,
- kobject_name(&dev->kobj));
- }
+
+fail:
return ret;
}
@@ -314,6 +337,8 @@ static void driver_sysfs_remove(struct device *dev)
struct device_driver *drv = dev->driver;
if (drv) {
+ if (drv->coredump)
+ device_remove_file(dev, &dev_attr_coredump);
sysfs_remove_link(&drv->p->kobj, kobject_name(&dev->kobj));
sysfs_remove_link(&dev->kobj, "driver");
}
diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c
index 7be310f7db73..f1a3353f3494 100644
--- a/drivers/base/devcoredump.c
+++ b/drivers/base/devcoredump.c
@@ -1,23 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * This file is provided under the GPLv2 license.
- *
- * GPL LICENSE SUMMARY
- *
* Copyright(c) 2014 Intel Mobile Communications GmbH
* Copyright(c) 2015 Intel Deutschland GmbH
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of version 2 of the GNU General Public License as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * The full GNU General Public License is included in this distribution
- * in the file called COPYING.
- *
* Contact Information:
* Intel Linux Wireless <ilw@linux.intel.com>
* Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 71d577025285..95b67281cd2a 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* drivers/base/devres.c - device resource management
*
* Copyright (c) 2006 SUSE Linux Products GmbH
* Copyright (c) 2006 Tejun Heo <teheo@suse.de>
- *
- * This file is released under the GPLv2.
*/
#include <linux/device.h>
diff --git a/drivers/base/dma-contiguous.c b/drivers/base/dma-contiguous.c
index ea9726e71468..d987dcd1bd56 100644
--- a/drivers/base/dma-contiguous.c
+++ b/drivers/base/dma-contiguous.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Contiguous Memory Allocator for DMA mapping framework
* Copyright (c) 2010-2011 by Samsung Electronics.
* Written by:
* Marek Szyprowski <m.szyprowski@samsung.com>
* Michal Nazarewicz <mina86@mina86.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License or (at your optional) any later version of the license.
*/
#define pr_fmt(fmt) "cma: " fmt
diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c
index e584eddef0a7..3b118353ea17 100644
--- a/drivers/base/dma-mapping.c
+++ b/drivers/base/dma-mapping.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* drivers/base/dma-mapping.c - arch-independent dma-mapping routines
*
* Copyright (c) 2006 SUSE Linux Products GmbH
* Copyright (c) 2006 Tejun Heo <teheo@suse.de>
- *
- * This file is released under the GPLv2.
*/
#include <linux/acpi.h>
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 4eabfe28d2b3..ba912558a510 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* driver.c - centralized device driver management
*
@@ -5,9 +6,6 @@
* Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2007 Novell Inc.
- *
- * This file is released under the GPLv2
- *
*/
#include <linux/device.h>
@@ -52,7 +50,7 @@ int driver_for_each_device(struct device_driver *drv, struct device *start,
klist_iter_init_node(&drv->p->klist_devices, &i,
start ? &start->p->knode_driver : NULL);
- while ((dev = next_device(&i)) && !error)
+ while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
return error;
diff --git a/drivers/base/firmware.c b/drivers/base/firmware.c
index 113815556809..8dff940e0db9 100644
--- a/drivers/base/firmware.c
+++ b/drivers/base/firmware.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* firmware.c - firmware subsystem hoohaw.
*
@@ -5,8 +6,6 @@
* Copyright (c) 2002-3 Open Source Development Labs
* Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (c) 2007 Novell Inc.
- *
- * This file is released under the GPLv2
*/
#include <linux/kobject.h>
#include <linux/module.h>
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 4b57cf5bc81d..7dd36ace6152 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* firmware_class.c - Multi purpose firmware loading support
*
@@ -41,6 +42,96 @@ MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
MODULE_LICENSE("GPL");
+enum fw_status {
+ FW_STATUS_UNKNOWN,
+ FW_STATUS_LOADING,
+ FW_STATUS_DONE,
+ FW_STATUS_ABORTED,
+};
+
+/*
+ * Concurrent request_firmware() for the same firmware need to be
+ * serialized. struct fw_state is simple state machine which hold the
+ * state of the firmware loading.
+ */
+struct fw_state {
+ struct completion completion;
+ enum fw_status status;
+};
+
+/* firmware behavior options */
+#define FW_OPT_UEVENT (1U << 0)
+#define FW_OPT_NOWAIT (1U << 1)
+#define FW_OPT_USERHELPER (1U << 2)
+#define FW_OPT_NO_WARN (1U << 3)
+#define FW_OPT_NOCACHE (1U << 4)
+#define FW_OPT_NOFALLBACK (1U << 5)
+
+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;
+
+ struct delayed_work work;
+
+ struct notifier_block pm_notify;
+#endif
+};
+
+struct fw_priv {
+ struct kref ref;
+ struct list_head list;
+ struct firmware_cache *fwc;
+ struct fw_state fw_st;
+ void *data;
+ size_t size;
+ size_t allocated_size;
+#ifdef CONFIG_FW_LOADER_USER_HELPER
+ bool is_paged_buf;
+ bool need_uevent;
+ struct page **pages;
+ int nr_pages;
+ int page_array_size;
+ struct list_head pending_list;
+#endif
+ const char *fw_name;
+};
+
+struct fw_cache_entry {
+ struct list_head list;
+ const char *name;
+};
+
+struct fw_name_devm {
+ unsigned long magic;
+ const char *name;
+};
+
+static inline struct fw_priv *to_fw_priv(struct kref *ref)
+{
+ return container_of(ref, struct fw_priv, ref);
+}
+
+#define FW_LOADER_NO_CACHE 0
+#define FW_LOADER_START_CACHE 1
+
+/* fw_lock could be moved to 'struct fw_sysfs' 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;
+
/* Builtin firmware support */
#ifdef CONFIG_FW_LOADER
@@ -48,6 +139,14 @@ MODULE_LICENSE("GPL");
extern struct builtin_fw __start_builtin_fw[];
extern struct builtin_fw __end_builtin_fw[];
+static void fw_copy_to_prealloc_buf(struct firmware *fw,
+ void *buf, size_t size)
+{
+ if (!buf || size < fw->size)
+ return;
+ memcpy(buf, fw->data, fw->size);
+}
+
static bool fw_get_builtin_firmware(struct firmware *fw, const char *name,
void *buf, size_t size)
{
@@ -57,9 +156,8 @@ static bool fw_get_builtin_firmware(struct firmware *fw, const char *name,
if (strcmp(name, b_fw->name) == 0) {
fw->size = b_fw->size;
fw->data = b_fw->data;
+ fw_copy_to_prealloc_buf(fw, buf, size);
- if (buf && fw->size <= size)
- memcpy(buf, fw->data, fw->size);
return true;
}
}
@@ -93,13 +191,6 @@ static inline bool fw_is_builtin_firmware(const struct firmware *fw)
}
#endif
-enum fw_status {
- FW_STATUS_UNKNOWN,
- FW_STATUS_LOADING,
- FW_STATUS_DONE,
- FW_STATUS_ABORTED,
-};
-
static int loading_timeout = 60; /* In seconds */
static inline long firmware_loading_timeout(void)
@@ -107,29 +198,17 @@ static inline long firmware_loading_timeout(void)
return loading_timeout > 0 ? loading_timeout * HZ : MAX_JIFFY_OFFSET;
}
-/*
- * Concurrent request_firmware() for the same firmware need to be
- * serialized. struct fw_state is simple state machine which hold the
- * state of the firmware loading.
- */
-struct fw_state {
- struct completion completion;
- enum fw_status status;
-};
-
-static void fw_state_init(struct fw_state *fw_st)
+static void fw_state_init(struct fw_priv *fw_priv)
{
+ struct fw_state *fw_st = &fw_priv->fw_st;
+
init_completion(&fw_st->completion);
fw_st->status = FW_STATUS_UNKNOWN;
}
-static inline bool __fw_state_is_done(enum fw_status status)
-{
- return status == FW_STATUS_DONE || status == FW_STATUS_ABORTED;
-}
-
-static int __fw_state_wait_common(struct fw_state *fw_st, long timeout)
+static int __fw_state_wait_common(struct fw_priv *fw_priv, long timeout)
{
+ struct fw_state *fw_st = &fw_priv->fw_st;
long ret;
ret = wait_for_completion_killable_timeout(&fw_st->completion, timeout);
@@ -141,226 +220,172 @@ static int __fw_state_wait_common(struct fw_state *fw_st, long timeout)
return ret < 0 ? ret : 0;
}
-static void __fw_state_set(struct fw_state *fw_st,
+static void __fw_state_set(struct fw_priv *fw_priv,
enum fw_status status)
{
+ struct fw_state *fw_st = &fw_priv->fw_st;
+
WRITE_ONCE(fw_st->status, status);
if (status == FW_STATUS_DONE || status == FW_STATUS_ABORTED)
complete_all(&fw_st->completion);
}
-#define fw_state_start(fw_st) \
- __fw_state_set(fw_st, FW_STATUS_LOADING)
-#define fw_state_done(fw_st) \
- __fw_state_set(fw_st, FW_STATUS_DONE)
-#define fw_state_aborted(fw_st) \
- __fw_state_set(fw_st, FW_STATUS_ABORTED)
-#define fw_state_wait(fw_st) \
- __fw_state_wait_common(fw_st, MAX_SCHEDULE_TIMEOUT)
-
-static int __fw_state_check(struct fw_state *fw_st, enum fw_status status)
+static inline void fw_state_start(struct fw_priv *fw_priv)
{
- return fw_st->status == status;
+ __fw_state_set(fw_priv, FW_STATUS_LOADING);
}
-#define fw_state_is_aborted(fw_st) \
- __fw_state_check(fw_st, FW_STATUS_ABORTED)
-
-#ifdef CONFIG_FW_LOADER_USER_HELPER
-
-#define fw_state_aborted(fw_st) \
- __fw_state_set(fw_st, FW_STATUS_ABORTED)
-#define fw_state_is_done(fw_st) \
- __fw_state_check(fw_st, FW_STATUS_DONE)
-#define fw_state_is_loading(fw_st) \
- __fw_state_check(fw_st, FW_STATUS_LOADING)
-#define fw_state_wait_timeout(fw_st, timeout) \
- __fw_state_wait_common(fw_st, timeout)
-
-#endif /* CONFIG_FW_LOADER_USER_HELPER */
+static inline void fw_state_done(struct fw_priv *fw_priv)
+{
+ __fw_state_set(fw_priv, FW_STATUS_DONE);
+}
-/* firmware behavior options */
-#define FW_OPT_UEVENT (1U << 0)
-#define FW_OPT_NOWAIT (1U << 1)
-#ifdef CONFIG_FW_LOADER_USER_HELPER
-#define FW_OPT_USERHELPER (1U << 2)
-#else
-#define FW_OPT_USERHELPER 0
-#endif
-#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK
-#define FW_OPT_FALLBACK FW_OPT_USERHELPER
-#else
-#define FW_OPT_FALLBACK 0
-#endif
-#define FW_OPT_NO_WARN (1U << 3)
-#define FW_OPT_NOCACHE (1U << 4)
+static inline void fw_state_aborted(struct fw_priv *fw_priv)
+{
+ __fw_state_set(fw_priv, FW_STATUS_ABORTED);
+}
-struct firmware_cache {
- /* firmware_buf instance will be added into the below list */
- spinlock_t lock;
- struct list_head head;
- int state;
+static inline int fw_state_wait(struct fw_priv *fw_priv)
+{
+ return __fw_state_wait_common(fw_priv, MAX_SCHEDULE_TIMEOUT);
+}
-#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;
+static bool __fw_state_check(struct fw_priv *fw_priv,
+ enum fw_status status)
+{
+ struct fw_state *fw_st = &fw_priv->fw_st;
- struct delayed_work work;
+ return fw_st->status == status;
+}
- struct notifier_block pm_notify;
-#endif
-};
+static inline bool fw_state_is_aborted(struct fw_priv *fw_priv)
+{
+ return __fw_state_check(fw_priv, FW_STATUS_ABORTED);
+}
-struct firmware_buf {
- struct kref ref;
- struct list_head list;
- struct firmware_cache *fwc;
- struct fw_state fw_st;
- void *data;
- size_t size;
- size_t allocated_size;
#ifdef CONFIG_FW_LOADER_USER_HELPER
- bool is_paged_buf;
- bool need_uevent;
- struct page **pages;
- int nr_pages;
- int page_array_size;
- struct list_head pending_list;
-#endif
- const char *fw_id;
-};
-struct fw_cache_entry {
- struct list_head list;
- const char *name;
-};
+static inline bool fw_sysfs_done(struct fw_priv *fw_priv)
+{
+ return __fw_state_check(fw_priv, FW_STATUS_DONE);
+}
-struct fw_name_devm {
- unsigned long magic;
- const char *name;
-};
+static inline bool fw_sysfs_loading(struct fw_priv *fw_priv)
+{
+ return __fw_state_check(fw_priv, FW_STATUS_LOADING);
+}
-#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)
+static inline int fw_sysfs_wait_timeout(struct fw_priv *fw_priv, long timeout)
+{
+ return __fw_state_wait_common(fw_priv, timeout);
+}
-#define FW_LOADER_NO_CACHE 0
-#define FW_LOADER_START_CACHE 1
+#endif /* CONFIG_FW_LOADER_USER_HELPER */
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,
- void *dbuf, size_t size)
+static struct fw_priv *__allocate_fw_priv(const char *fw_name,
+ struct firmware_cache *fwc,
+ void *dbuf, size_t size)
{
- struct firmware_buf *buf;
+ struct fw_priv *fw_priv;
- buf = kzalloc(sizeof(*buf), GFP_ATOMIC);
- if (!buf)
+ fw_priv = kzalloc(sizeof(*fw_priv), GFP_ATOMIC);
+ if (!fw_priv)
return NULL;
- buf->fw_id = kstrdup_const(fw_name, GFP_ATOMIC);
- if (!buf->fw_id) {
- kfree(buf);
+ fw_priv->fw_name = kstrdup_const(fw_name, GFP_ATOMIC);
+ if (!fw_priv->fw_name) {
+ kfree(fw_priv);
return NULL;
}
- kref_init(&buf->ref);
- buf->fwc = fwc;
- buf->data = dbuf;
- buf->allocated_size = size;
- fw_state_init(&buf->fw_st);
+ kref_init(&fw_priv->ref);
+ fw_priv->fwc = fwc;
+ fw_priv->data = dbuf;
+ fw_priv->allocated_size = size;
+ fw_state_init(fw_priv);
#ifdef CONFIG_FW_LOADER_USER_HELPER
- INIT_LIST_HEAD(&buf->pending_list);
+ INIT_LIST_HEAD(&fw_priv->pending_list);
#endif
- pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf);
+ pr_debug("%s: fw-%s fw_priv=%p\n", __func__, fw_name, fw_priv);
- return buf;
+ return fw_priv;
}
-static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
+static struct fw_priv *__lookup_fw_priv(const char *fw_name)
{
- struct firmware_buf *tmp;
+ struct fw_priv *tmp;
struct firmware_cache *fwc = &fw_cache;
list_for_each_entry(tmp, &fwc->head, list)
- if (!strcmp(tmp->fw_id, fw_name))
+ if (!strcmp(tmp->fw_name, fw_name))
return tmp;
return NULL;
}
/* Returns 1 for batching firmware requests with the same name */
-static int fw_lookup_and_allocate_buf(const char *fw_name,
- struct firmware_cache *fwc,
- struct firmware_buf **buf, void *dbuf,
- size_t size)
+static int alloc_lookup_fw_priv(const char *fw_name,
+ struct firmware_cache *fwc,
+ struct fw_priv **fw_priv, void *dbuf,
+ size_t size)
{
- struct firmware_buf *tmp;
+ struct fw_priv *tmp;
spin_lock(&fwc->lock);
- tmp = __fw_lookup_buf(fw_name);
+ tmp = __lookup_fw_priv(fw_name);
if (tmp) {
kref_get(&tmp->ref);
spin_unlock(&fwc->lock);
- *buf = tmp;
- pr_debug("batched request - sharing the same struct firmware_buf and lookup for multiple requests\n");
+ *fw_priv = tmp;
+ pr_debug("batched request - sharing the same struct fw_priv and lookup for multiple requests\n");
return 1;
}
- tmp = __allocate_fw_buf(fw_name, fwc, dbuf, size);
+ tmp = __allocate_fw_priv(fw_name, fwc, dbuf, size);
if (tmp)
list_add(&tmp->list, &fwc->head);
spin_unlock(&fwc->lock);
- *buf = tmp;
+ *fw_priv = tmp;
return tmp ? 0 : -ENOMEM;
}
-static void __fw_free_buf(struct kref *ref)
+static void __free_fw_priv(struct kref *ref)
__releases(&fwc->lock)
{
- struct firmware_buf *buf = to_fwbuf(ref);
- struct firmware_cache *fwc = buf->fwc;
+ struct fw_priv *fw_priv = to_fw_priv(ref);
+ struct firmware_cache *fwc = fw_priv->fwc;
- pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
- __func__, buf->fw_id, buf, buf->data,
- (unsigned int)buf->size);
+ pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
+ __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
+ (unsigned int)fw_priv->size);
- list_del(&buf->list);
+ list_del(&fw_priv->list);
spin_unlock(&fwc->lock);
#ifdef CONFIG_FW_LOADER_USER_HELPER
- if (buf->is_paged_buf) {
+ if (fw_priv->is_paged_buf) {
int i;
- vunmap(buf->data);
- for (i = 0; i < buf->nr_pages; i++)
- __free_page(buf->pages[i]);
- vfree(buf->pages);
+ vunmap(fw_priv->data);
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ vfree(fw_priv->pages);
} else
#endif
- if (!buf->allocated_size)
- vfree(buf->data);
- kfree_const(buf->fw_id);
- kfree(buf);
+ if (!fw_priv->allocated_size)
+ vfree(fw_priv->data);
+ kfree_const(fw_priv->fw_name);
+ kfree(fw_priv);
}
-static void fw_free_buf(struct firmware_buf *buf)
+static void free_fw_priv(struct fw_priv *fw_priv)
{
- struct firmware_cache *fwc = buf->fwc;
+ struct firmware_cache *fwc = fw_priv->fwc;
spin_lock(&fwc->lock);
- if (!kref_put(&buf->ref, __fw_free_buf))
+ if (!kref_put(&fw_priv->ref, __free_fw_priv))
spin_unlock(&fwc->lock);
}
@@ -383,7 +408,7 @@ 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");
static int
-fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
+fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv)
{
loff_t size;
int i, len;
@@ -393,9 +418,9 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
size_t msize = INT_MAX;
/* Already populated data member means we're loading into a buffer */
- if (buf->data) {
+ if (fw_priv->data) {
id = READING_FIRMWARE_PREALLOC_BUFFER;
- msize = buf->allocated_size;
+ msize = fw_priv->allocated_size;
}
path = __getname();
@@ -408,15 +433,15 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
continue;
len = snprintf(path, PATH_MAX, "%s/%s",
- fw_path[i], buf->fw_id);
+ fw_path[i], fw_priv->fw_name);
if (len >= PATH_MAX) {
rc = -ENAMETOOLONG;
break;
}
- buf->size = 0;
- rc = kernel_read_file_from_path(path, &buf->data, &size, msize,
- id);
+ fw_priv->size = 0;
+ rc = kernel_read_file_from_path(path, &fw_priv->data, &size,
+ msize, id);
if (rc) {
if (rc == -ENOENT)
dev_dbg(device, "loading %s failed with error %d\n",
@@ -426,9 +451,9 @@ fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf)
path, rc);
continue;
}
- dev_dbg(device, "direct-loading %s\n", buf->fw_id);
- buf->size = size;
- fw_state_done(&buf->fw_st);
+ dev_dbg(device, "direct-loading %s\n", fw_priv->fw_name);
+ fw_priv->size = size;
+ fw_state_done(fw_priv);
break;
}
__putname(path);
@@ -444,22 +469,22 @@ static void firmware_free_data(const struct firmware *fw)
vfree(fw->data);
return;
}
- fw_free_buf(fw->priv);
+ free_fw_priv(fw->priv);
}
/* store the pages buffer info firmware from buf */
-static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
+static void fw_set_page_data(struct fw_priv *fw_priv, struct firmware *fw)
{
- fw->priv = buf;
+ fw->priv = fw_priv;
#ifdef CONFIG_FW_LOADER_USER_HELPER
- fw->pages = buf->pages;
+ fw->pages = fw_priv->pages;
#endif
- fw->size = buf->size;
- fw->data = buf->data;
+ fw->size = fw_priv->size;
+ fw->data = fw_priv->data;
- pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
- __func__, buf->fw_id, buf, buf->data,
- (unsigned int)buf->size);
+ pr_debug("%s: fw-%s fw_priv=%p data=%p size=%u\n",
+ __func__, fw_priv->fw_name, fw_priv, fw_priv->data,
+ (unsigned int)fw_priv->size);
}
#ifdef CONFIG_PM_SLEEP
@@ -523,13 +548,13 @@ static int fw_add_devm_name(struct device *dev, const char *name)
}
#endif
-static int assign_firmware_buf(struct firmware *fw, struct device *device,
- unsigned int opt_flags)
+static int assign_fw(struct firmware *fw, struct device *device,
+ unsigned int opt_flags)
{
- struct firmware_buf *buf = fw->priv;
+ struct fw_priv *fw_priv = fw->priv;
mutex_lock(&fw_lock);
- if (!buf->size || fw_state_is_aborted(&buf->fw_st)) {
+ if (!fw_priv->size || fw_state_is_aborted(fw_priv)) {
mutex_unlock(&fw_lock);
return -ENOENT;
}
@@ -544,20 +569,20 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
/* don't cache firmware handled without uevent */
if (device && (opt_flags & FW_OPT_UEVENT) &&
!(opt_flags & FW_OPT_NOCACHE))
- fw_add_devm_name(device, buf->fw_id);
+ fw_add_devm_name(device, fw_priv->fw_name);
/*
* After caching firmware image is started, let it piggyback
* on request firmware.
*/
if (!(opt_flags & FW_OPT_NOCACHE) &&
- buf->fwc->state == FW_LOADER_START_CACHE) {
- if (fw_cache_piggyback_on_request(buf->fw_id))
- kref_get(&buf->ref);
+ fw_priv->fwc->state == FW_LOADER_START_CACHE) {
+ if (fw_cache_piggyback_on_request(fw_priv->fw_name))
+ kref_get(&fw_priv->ref);
}
/* pass the pages buffer to driver at the last minute */
- fw_set_page_data(buf, fw);
+ fw_set_page_data(fw_priv, fw);
mutex_unlock(&fw_lock);
return 0;
}
@@ -566,49 +591,50 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device,
* user-mode helper code
*/
#ifdef CONFIG_FW_LOADER_USER_HELPER
-struct firmware_priv {
+struct fw_sysfs {
bool nowait;
struct device dev;
- struct firmware_buf *buf;
+ struct fw_priv *fw_priv;
struct firmware *fw;
};
-static struct firmware_priv *to_firmware_priv(struct device *dev)
+static struct fw_sysfs *to_fw_sysfs(struct device *dev)
{
- return container_of(dev, struct firmware_priv, dev);
+ return container_of(dev, struct fw_sysfs, dev);
}
-static void __fw_load_abort(struct firmware_buf *buf)
+static void __fw_load_abort(struct fw_priv *fw_priv)
{
/*
* There is a small window in which user can write to 'loading'
* between loading done and disappearance of 'loading'
*/
- if (fw_state_is_done(&buf->fw_st))
+ if (fw_sysfs_done(fw_priv))
return;
- list_del_init(&buf->pending_list);
- fw_state_aborted(&buf->fw_st);
+ list_del_init(&fw_priv->pending_list);
+ fw_state_aborted(fw_priv);
}
-static void fw_load_abort(struct firmware_priv *fw_priv)
+static void fw_load_abort(struct fw_sysfs *fw_sysfs)
{
- struct firmware_buf *buf = fw_priv->buf;
+ struct fw_priv *fw_priv = fw_sysfs->fw_priv;
- __fw_load_abort(buf);
+ __fw_load_abort(fw_priv);
}
static LIST_HEAD(pending_fw_head);
static void kill_pending_fw_fallback_reqs(bool only_kill_custom)
{
- struct firmware_buf *buf;
- struct firmware_buf *next;
+ struct fw_priv *fw_priv;
+ struct fw_priv *next;
mutex_lock(&fw_lock);
- list_for_each_entry_safe(buf, next, &pending_fw_head, pending_list) {
- if (!buf->need_uevent || !only_kill_custom)
- __fw_load_abort(buf);
+ list_for_each_entry_safe(fw_priv, next, &pending_fw_head,
+ pending_list) {
+ if (!fw_priv->need_uevent || !only_kill_custom)
+ __fw_load_abort(fw_priv);
}
mutex_unlock(&fw_lock);
}
@@ -651,18 +677,18 @@ ATTRIBUTE_GROUPS(firmware_class);
static void fw_dev_release(struct device *dev)
{
- struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
- kfree(fw_priv);
+ kfree(fw_sysfs);
}
-static int do_firmware_uevent(struct firmware_priv *fw_priv, struct kobj_uevent_env *env)
+static int do_firmware_uevent(struct fw_sysfs *fw_sysfs, struct kobj_uevent_env *env)
{
- if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->buf->fw_id))
+ if (add_uevent_var(env, "FIRMWARE=%s", fw_sysfs->fw_priv->fw_name))
return -ENOMEM;
if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
return -ENOMEM;
- if (add_uevent_var(env, "ASYNC=%d", fw_priv->nowait))
+ if (add_uevent_var(env, "ASYNC=%d", fw_sysfs->nowait))
return -ENOMEM;
return 0;
@@ -670,12 +696,12 @@ static int do_firmware_uevent(struct firmware_priv *fw_priv, struct kobj_uevent_
static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
{
- struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
int err = 0;
mutex_lock(&fw_lock);
- if (fw_priv->buf)
- err = do_firmware_uevent(fw_priv, env);
+ if (fw_sysfs->fw_priv)
+ err = do_firmware_uevent(fw_sysfs, env);
mutex_unlock(&fw_lock);
return err;
}
@@ -687,15 +713,25 @@ static struct class firmware_class = {
.dev_release = fw_dev_release,
};
+static inline int register_sysfs_loader(void)
+{
+ return class_register(&firmware_class);
+}
+
+static inline void unregister_sysfs_loader(void)
+{
+ class_unregister(&firmware_class);
+}
+
static ssize_t firmware_loading_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
int loading = 0;
mutex_lock(&fw_lock);
- if (fw_priv->buf)
- loading = fw_state_is_loading(&fw_priv->buf->fw_st);
+ if (fw_sysfs->fw_priv)
+ loading = fw_sysfs_loading(fw_sysfs->fw_priv);
mutex_unlock(&fw_lock);
return sprintf(buf, "%d\n", loading);
@@ -707,14 +743,15 @@ static ssize_t firmware_loading_show(struct device *dev,
#endif
/* one pages buffer should be mapped/unmapped only once */
-static int fw_map_pages_buf(struct firmware_buf *buf)
+static int map_fw_priv_pages(struct fw_priv *fw_priv)
{
- if (!buf->is_paged_buf)
+ if (!fw_priv->is_paged_buf)
return 0;
- vunmap(buf->data);
- buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
- if (!buf->data)
+ vunmap(fw_priv->data);
+ fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
+ PAGE_KERNEL_RO);
+ if (!fw_priv->data)
return -ENOMEM;
return 0;
}
@@ -736,32 +773,32 @@ static ssize_t firmware_loading_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
- struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware_buf *fw_buf;
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ struct fw_priv *fw_priv;
ssize_t written = count;
int loading = simple_strtol(buf, NULL, 10);
int i;
mutex_lock(&fw_lock);
- fw_buf = fw_priv->buf;
- if (fw_state_is_aborted(&fw_buf->fw_st))
+ fw_priv = fw_sysfs->fw_priv;
+ if (fw_state_is_aborted(fw_priv))
goto out;
switch (loading) {
case 1:
/* discarding any previous partial load */
- if (!fw_state_is_done(&fw_buf->fw_st)) {
- for (i = 0; i < fw_buf->nr_pages; i++)
- __free_page(fw_buf->pages[i]);
- vfree(fw_buf->pages);
- fw_buf->pages = NULL;
- fw_buf->page_array_size = 0;
- fw_buf->nr_pages = 0;
- fw_state_start(&fw_buf->fw_st);
+ if (!fw_sysfs_done(fw_priv)) {
+ for (i = 0; i < fw_priv->nr_pages; i++)
+ __free_page(fw_priv->pages[i]);
+ vfree(fw_priv->pages);
+ fw_priv->pages = NULL;
+ fw_priv->page_array_size = 0;
+ fw_priv->nr_pages = 0;
+ fw_state_start(fw_priv);
}
break;
case 0:
- if (fw_state_is_loading(&fw_buf->fw_st)) {
+ if (fw_sysfs_loading(fw_priv)) {
int rc;
/*
@@ -770,25 +807,25 @@ static ssize_t firmware_loading_store(struct device *dev,
* see the mapped 'buf->data' once the loading
* is completed.
* */
- rc = fw_map_pages_buf(fw_buf);
+ rc = map_fw_priv_pages(fw_priv);
if (rc)
dev_err(dev, "%s: map pages failed\n",
__func__);
else
rc = security_kernel_post_read_file(NULL,
- fw_buf->data, fw_buf->size,
+ fw_priv->data, fw_priv->size,
READING_FIRMWARE);
/*
* Same logic as fw_load_abort, only the DONE bit
* is ignored and we set ABORT only on failure.
*/
- list_del_init(&fw_buf->pending_list);
+ list_del_init(&fw_priv->pending_list);
if (rc) {
- fw_state_aborted(&fw_buf->fw_st);
+ fw_state_aborted(fw_priv);
written = rc;
} else {
- fw_state_done(&fw_buf->fw_st);
+ fw_state_done(fw_priv);
}
break;
}
@@ -797,7 +834,7 @@ static ssize_t firmware_loading_store(struct device *dev,
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
- fw_load_abort(fw_priv);
+ fw_load_abort(fw_sysfs);
break;
}
out:
@@ -807,16 +844,16 @@ out:
static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store);
-static void firmware_rw_buf(struct firmware_buf *buf, char *buffer,
+static void firmware_rw_data(struct fw_priv *fw_priv, char *buffer,
loff_t offset, size_t count, bool read)
{
if (read)
- memcpy(buffer, buf->data + offset, count);
+ memcpy(buffer, fw_priv->data + offset, count);
else
- memcpy(buf->data + offset, buffer, count);
+ memcpy(fw_priv->data + offset, buffer, count);
}
-static void firmware_rw(struct firmware_buf *buf, char *buffer,
+static void firmware_rw(struct fw_priv *fw_priv, char *buffer,
loff_t offset, size_t count, bool read)
{
while (count) {
@@ -825,14 +862,14 @@ static void firmware_rw(struct firmware_buf *buf, char *buffer,
int page_ofs = offset & (PAGE_SIZE-1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
- page_data = kmap(buf->pages[page_nr]);
+ page_data = kmap(fw_priv->pages[page_nr]);
if (read)
memcpy(buffer, page_data + page_ofs, page_cnt);
else
memcpy(page_data + page_ofs, buffer, page_cnt);
- kunmap(buf->pages[page_nr]);
+ kunmap(fw_priv->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
@@ -844,69 +881,69 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
- struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware_buf *buf;
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ struct fw_priv *fw_priv;
ssize_t ret_count;
mutex_lock(&fw_lock);
- buf = fw_priv->buf;
- if (!buf || fw_state_is_done(&buf->fw_st)) {
+ fw_priv = fw_sysfs->fw_priv;
+ if (!fw_priv || fw_sysfs_done(fw_priv)) {
ret_count = -ENODEV;
goto out;
}
- if (offset > buf->size) {
+ if (offset > fw_priv->size) {
ret_count = 0;
goto out;
}
- if (count > buf->size - offset)
- count = buf->size - offset;
+ if (count > fw_priv->size - offset)
+ count = fw_priv->size - offset;
ret_count = count;
- if (buf->data)
- firmware_rw_buf(buf, buffer, offset, count, true);
+ if (fw_priv->data)
+ firmware_rw_data(fw_priv, buffer, offset, count, true);
else
- firmware_rw(buf, buffer, offset, count, true);
+ firmware_rw(fw_priv, buffer, offset, count, true);
out:
mutex_unlock(&fw_lock);
return ret_count;
}
-static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
+static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
{
- struct firmware_buf *buf = fw_priv->buf;
+ struct fw_priv *fw_priv= fw_sysfs->fw_priv;
int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;
/* If the array of pages is too small, grow it... */
- if (buf->page_array_size < pages_needed) {
+ if (fw_priv->page_array_size < pages_needed) {
int new_array_size = max(pages_needed,
- buf->page_array_size * 2);
+ fw_priv->page_array_size * 2);
struct page **new_pages;
new_pages = vmalloc(new_array_size * sizeof(void *));
if (!new_pages) {
- fw_load_abort(fw_priv);
+ fw_load_abort(fw_sysfs);
return -ENOMEM;
}
- 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));
- vfree(buf->pages);
- buf->pages = new_pages;
- buf->page_array_size = new_array_size;
+ 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));
+ vfree(fw_priv->pages);
+ fw_priv->pages = new_pages;
+ fw_priv->page_array_size = new_array_size;
}
- while (buf->nr_pages < pages_needed) {
- buf->pages[buf->nr_pages] =
+ while (fw_priv->nr_pages < pages_needed) {
+ fw_priv->pages[fw_priv->nr_pages] =
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
- if (!buf->pages[buf->nr_pages]) {
- fw_load_abort(fw_priv);
+ if (!fw_priv->pages[fw_priv->nr_pages]) {
+ fw_load_abort(fw_sysfs);
return -ENOMEM;
}
- buf->nr_pages++;
+ fw_priv->nr_pages++;
}
return 0;
}
@@ -928,37 +965,37 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
char *buffer, loff_t offset, size_t count)
{
struct device *dev = kobj_to_dev(kobj);
- struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware_buf *buf;
+ struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev);
+ struct fw_priv *fw_priv;
ssize_t retval;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
mutex_lock(&fw_lock);
- buf = fw_priv->buf;
- if (!buf || fw_state_is_done(&buf->fw_st)) {
+ fw_priv = fw_sysfs->fw_priv;
+ if (!fw_priv || fw_sysfs_done(fw_priv)) {
retval = -ENODEV;
goto out;
}
- if (buf->data) {
- if (offset + count > buf->allocated_size) {
+ if (fw_priv->data) {
+ if (offset + count > fw_priv->allocated_size) {
retval = -ENOMEM;
goto out;
}
- firmware_rw_buf(buf, buffer, offset, count, false);
+ firmware_rw_data(fw_priv, buffer, offset, count, false);
retval = count;
} else {
- retval = fw_realloc_buffer(fw_priv, offset + count);
+ retval = fw_realloc_pages(fw_sysfs, offset + count);
if (retval)
goto out;
retval = count;
- firmware_rw(buf, buffer, offset, count, false);
+ firmware_rw(fw_priv, buffer, offset, count, false);
}
- buf->size = max_t(size_t, offset + count, buf->size);
+ fw_priv->size = max_t(size_t, offset + count, fw_priv->size);
out:
mutex_unlock(&fw_lock);
return retval;
@@ -991,22 +1028,22 @@ static const struct attribute_group *fw_dev_attr_groups[] = {
NULL
};
-static struct firmware_priv *
+static struct fw_sysfs *
fw_create_instance(struct firmware *firmware, const char *fw_name,
struct device *device, unsigned int opt_flags)
{
- struct firmware_priv *fw_priv;
+ struct fw_sysfs *fw_sysfs;
struct device *f_dev;
- fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
- if (!fw_priv) {
- fw_priv = ERR_PTR(-ENOMEM);
+ fw_sysfs = kzalloc(sizeof(*fw_sysfs), GFP_KERNEL);
+ if (!fw_sysfs) {
+ fw_sysfs = ERR_PTR(-ENOMEM);
goto exit;
}
- fw_priv->nowait = !!(opt_flags & FW_OPT_NOWAIT);
- fw_priv->fw = firmware;
- f_dev = &fw_priv->dev;
+ fw_sysfs->nowait = !!(opt_flags & FW_OPT_NOWAIT);
+ fw_sysfs->fw = firmware;
+ f_dev = &fw_sysfs->dev;
device_initialize(f_dev);
dev_set_name(f_dev, "%s", fw_name);
@@ -1014,20 +1051,20 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
f_dev->class = &firmware_class;
f_dev->groups = fw_dev_attr_groups;
exit:
- return fw_priv;
+ return fw_sysfs;
}
/* load a firmware via user helper */
-static int _request_firmware_load(struct firmware_priv *fw_priv,
+static int _request_firmware_load(struct fw_sysfs *fw_sysfs,
unsigned int opt_flags, long timeout)
{
int retval = 0;
- struct device *f_dev = &fw_priv->dev;
- struct firmware_buf *buf = fw_priv->buf;
+ struct device *f_dev = &fw_sysfs->dev;
+ struct fw_priv *fw_priv = fw_sysfs->fw_priv;
/* fall back on userspace loading */
- if (!buf->data)
- buf->is_paged_buf = true;
+ if (!fw_priv->data)
+ fw_priv->is_paged_buf = true;
dev_set_uevent_suppress(f_dev, true);
@@ -1038,31 +1075,31 @@ static int _request_firmware_load(struct firmware_priv *fw_priv,
}
mutex_lock(&fw_lock);
- list_add(&buf->pending_list, &pending_fw_head);
+ list_add(&fw_priv->pending_list, &pending_fw_head);
mutex_unlock(&fw_lock);
if (opt_flags & FW_OPT_UEVENT) {
- buf->need_uevent = true;
+ fw_priv->need_uevent = true;
dev_set_uevent_suppress(f_dev, false);
- dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
- kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
+ dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_name);
+ kobject_uevent(&fw_sysfs->dev.kobj, KOBJ_ADD);
} else {
timeout = MAX_JIFFY_OFFSET;
}
- retval = fw_state_wait_timeout(&buf->fw_st, timeout);
+ retval = fw_sysfs_wait_timeout(fw_priv, timeout);
if (retval < 0) {
mutex_lock(&fw_lock);
- fw_load_abort(fw_priv);
+ fw_load_abort(fw_sysfs);
mutex_unlock(&fw_lock);
}
- if (fw_state_is_aborted(&buf->fw_st)) {
+ if (fw_state_is_aborted(fw_priv)) {
if (retval == -ERESTARTSYS)
retval = -EINTR;
else
retval = -EAGAIN;
- } else if (buf->is_paged_buf && !buf->data)
+ } else if (fw_priv->is_paged_buf && !fw_priv->data)
retval = -ENOMEM;
device_del(f_dev);
@@ -1075,7 +1112,7 @@ static int fw_load_from_user_helper(struct firmware *firmware,
const char *name, struct device *device,
unsigned int opt_flags)
{
- struct firmware_priv *fw_priv;
+ struct fw_sysfs *fw_sysfs;
long timeout;
int ret;
@@ -1096,17 +1133,17 @@ static int fw_load_from_user_helper(struct firmware *firmware,
}
}
- fw_priv = fw_create_instance(firmware, name, device, opt_flags);
- if (IS_ERR(fw_priv)) {
- ret = PTR_ERR(fw_priv);
+ fw_sysfs = fw_create_instance(firmware, name, device, opt_flags);
+ if (IS_ERR(fw_sysfs)) {
+ ret = PTR_ERR(fw_sysfs);
goto out_unlock;
}
- fw_priv->buf = firmware->priv;
- ret = _request_firmware_load(fw_priv, opt_flags, timeout);
+ fw_sysfs->fw_priv = firmware->priv;
+ ret = _request_firmware_load(fw_sysfs, opt_flags, timeout);
if (!ret)
- ret = assign_firmware_buf(firmware, device, opt_flags);
+ ret = assign_fw(firmware, device, opt_flags);
out_unlock:
usermodehelper_read_unlock();
@@ -1114,16 +1151,60 @@ out_unlock:
return ret;
}
+#ifdef CONFIG_FW_LOADER_USER_HELPER_FALLBACK
+static bool fw_force_sysfs_fallback(unsigned int opt_flags)
+{
+ return true;
+}
+#else
+static bool fw_force_sysfs_fallback(unsigned int opt_flags)
+{
+ if (!(opt_flags & FW_OPT_USERHELPER))
+ return false;
+ return true;
+}
+#endif
+
+static bool fw_run_sysfs_fallback(unsigned int opt_flags)
+{
+ if ((opt_flags & FW_OPT_NOFALLBACK))
+ return false;
+
+ return fw_force_sysfs_fallback(opt_flags);
+}
+
+static int fw_sysfs_fallback(struct firmware *fw, const char *name,
+ struct device *device,
+ unsigned int opt_flags,
+ int ret)
+{
+ if (!fw_run_sysfs_fallback(opt_flags))
+ return ret;
+
+ dev_warn(device, "Falling back to user helper\n");
+ return fw_load_from_user_helper(fw, name, device, opt_flags);
+}
#else /* CONFIG_FW_LOADER_USER_HELPER */
-static inline int
-fw_load_from_user_helper(struct firmware *firmware, const char *name,
- struct device *device, unsigned int opt_flags)
+static int fw_sysfs_fallback(struct firmware *fw, const char *name,
+ struct device *device,
+ unsigned int opt_flags,
+ int ret)
{
- return -ENOENT;
+ /* Keep carrying over the same error */
+ return ret;
}
static inline void kill_pending_fw_fallback_reqs(bool only_kill_custom) { }
+static inline int register_sysfs_loader(void)
+{
+ return 0;
+}
+
+static inline void unregister_sysfs_loader(void)
+{
+}
+
#endif /* CONFIG_FW_LOADER_USER_HELPER */
/* prepare firmware and firmware_buf structs;
@@ -1135,7 +1216,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
struct device *device, void *dbuf, size_t size)
{
struct firmware *firmware;
- struct firmware_buf *buf;
+ struct fw_priv *fw_priv;
int ret;
*firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL);
@@ -1150,18 +1231,18 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
return 0; /* assigned */
}
- ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf, dbuf, size);
+ ret = alloc_lookup_fw_priv(name, &fw_cache, &fw_priv, dbuf, size);
/*
- * bind with 'buf' now to avoid warning in failure path
+ * bind with 'priv' now to avoid warning in failure path
* of requesting firmware.
*/
- firmware->priv = buf;
+ firmware->priv = fw_priv;
if (ret > 0) {
- ret = fw_state_wait(&buf->fw_st);
+ ret = fw_state_wait(fw_priv);
if (!ret) {
- fw_set_page_data(buf, firmware);
+ fw_set_page_data(fw_priv, firmware);
return 0; /* assigned */
}
}
@@ -1177,20 +1258,20 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name,
* released until the last user calls release_firmware().
*
* Failed batched requests are possible as well, in such cases we just share
- * the struct firmware_buf and won't release it until all requests are woken
+ * the struct fw_priv and won't release it until all requests are woken
* and have gone through this same path.
*/
static void fw_abort_batch_reqs(struct firmware *fw)
{
- struct firmware_buf *buf;
+ struct fw_priv *fw_priv;
/* Loaded directly? */
if (!fw || !fw->priv)
return;
- buf = fw->priv;
- if (!fw_state_is_aborted(&buf->fw_st))
- fw_state_aborted(&buf->fw_st);
+ fw_priv = fw->priv;
+ if (!fw_state_is_aborted(fw_priv))
+ fw_state_aborted(fw_priv);
}
/* called from request_firmware() and request_firmware_work_func() */
@@ -1220,13 +1301,9 @@ _request_firmware(const struct firmware **firmware_p, const char *name,
dev_warn(device,
"Direct firmware load for %s failed with error %d\n",
name, ret);
- if (opt_flags & FW_OPT_USERHELPER) {
- dev_warn(device, "Falling back to user helper\n");
- ret = fw_load_from_user_helper(fw, name, device,
- opt_flags);
- }
+ ret = fw_sysfs_fallback(fw, name, device, opt_flags, ret);
} else
- ret = assign_firmware_buf(fw, device, opt_flags);
+ ret = assign_fw(fw, device, opt_flags);
out:
if (ret < 0) {
@@ -1268,7 +1345,7 @@ request_firmware(const struct firmware **firmware_p, const char *name,
/* Need to pin this module until return */
__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, NULL, 0,
- FW_OPT_UEVENT | FW_OPT_FALLBACK);
+ FW_OPT_UEVENT);
module_put(THIS_MODULE);
return ret;
}
@@ -1292,7 +1369,8 @@ int request_firmware_direct(const struct firmware **firmware_p,
__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, NULL, 0,
- FW_OPT_UEVENT | FW_OPT_NO_WARN);
+ FW_OPT_UEVENT | FW_OPT_NO_WARN |
+ FW_OPT_NOFALLBACK);
module_put(THIS_MODULE);
return ret;
}
@@ -1321,8 +1399,7 @@ request_firmware_into_buf(const struct firmware **firmware_p, const char *name,
__module_get(THIS_MODULE);
ret = _request_firmware(firmware_p, name, device, buf, size,
- FW_OPT_UEVENT | FW_OPT_FALLBACK |
- FW_OPT_NOCACHE);
+ FW_OPT_UEVENT | FW_OPT_NOCACHE);
module_put(THIS_MODULE);
return ret;
}
@@ -1414,7 +1491,7 @@ request_firmware_nowait(
fw_work->device = device;
fw_work->context = context;
fw_work->cont = cont;
- fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK |
+ fw_work->opt_flags = FW_OPT_NOWAIT |
(uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER);
if (!try_module_get(module)) {
@@ -1463,13 +1540,13 @@ static int cache_firmware(const char *fw_name)
return ret;
}
-static struct firmware_buf *fw_lookup_buf(const char *fw_name)
+static struct fw_priv *lookup_fw_priv(const char *fw_name)
{
- struct firmware_buf *tmp;
+ struct fw_priv *tmp;
struct firmware_cache *fwc = &fw_cache;
spin_lock(&fwc->lock);
- tmp = __fw_lookup_buf(fw_name);
+ tmp = __lookup_fw_priv(fw_name);
spin_unlock(&fwc->lock);
return tmp;
@@ -1488,7 +1565,7 @@ static struct firmware_buf *fw_lookup_buf(const char *fw_name)
*/
static int uncache_firmware(const char *fw_name)
{
- struct firmware_buf *buf;
+ struct fw_priv *fw_priv;
struct firmware fw;
pr_debug("%s: %s\n", __func__, fw_name);
@@ -1496,9 +1573,9 @@ static int uncache_firmware(const char *fw_name)
if (fw_get_builtin_firmware(&fw, fw_name, NULL, 0))
return 0;
- buf = fw_lookup_buf(fw_name);
- if (buf) {
- fw_free_buf(buf);
+ fw_priv = lookup_fw_priv(fw_name);
+ if (fw_priv) {
+ free_fw_priv(fw_priv);
return 0;
}
@@ -1767,20 +1844,11 @@ static int fw_suspend(void)
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)
+static int __init register_fw_pm_ops(void)
{
- spin_lock_init(&fw_cache.lock);
- INIT_LIST_HEAD(&fw_cache.head);
- fw_cache.state = FW_LOADER_NO_CACHE;
+ int ret;
-#ifdef CONFIG_PM_SLEEP
spin_lock_init(&fw_cache.name_lock);
INIT_LIST_HEAD(&fw_cache.fw_names);
@@ -1788,10 +1856,39 @@ static void __init fw_cache_init(void)
device_uncache_fw_images_work);
fw_cache.pm_notify.notifier_call = fw_pm_notify;
- register_pm_notifier(&fw_cache.pm_notify);
+ ret = register_pm_notifier(&fw_cache.pm_notify);
+ if (ret)
+ return ret;
register_syscore_ops(&fw_syscore_ops);
+
+ return ret;
+}
+
+static inline void unregister_fw_pm_ops(void)
+{
+ unregister_syscore_ops(&fw_syscore_ops);
+ unregister_pm_notifier(&fw_cache.pm_notify);
+}
+#else
+static int fw_cache_piggyback_on_request(const char *name)
+{
+ return 0;
+}
+static inline int register_fw_pm_ops(void)
+{
+ return 0;
+}
+static inline void unregister_fw_pm_ops(void)
+{
+}
#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;
}
static int fw_shutdown_notify(struct notifier_block *unused1,
@@ -1812,25 +1909,31 @@ static struct notifier_block fw_shutdown_nb = {
static int __init firmware_class_init(void)
{
+ int ret;
+
+ /* No need to unfold these on exit */
fw_cache_init();
- register_reboot_notifier(&fw_shutdown_nb);
-#ifdef CONFIG_FW_LOADER_USER_HELPER
- return class_register(&firmware_class);
-#else
- return 0;
-#endif
+
+ ret = register_fw_pm_ops();
+ if (ret)
+ return ret;
+
+ ret = register_reboot_notifier(&fw_shutdown_nb);
+ if (ret)
+ goto out;
+
+ return register_sysfs_loader();
+
+out:
+ unregister_fw_pm_ops();
+ return ret;
}
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
+ unregister_fw_pm_ops();
unregister_reboot_notifier(&fw_shutdown_nb);
-#ifdef CONFIG_FW_LOADER_USER_HELPER
- class_unregister(&firmware_class);
-#endif
+ unregister_sysfs_loader();
}
fs_initcall(firmware_class_init);
diff --git a/drivers/base/hypervisor.c b/drivers/base/hypervisor.c
index 4f8b741f4615..1ce59b4b53ce 100644
--- a/drivers/base/hypervisor.c
+++ b/drivers/base/hypervisor.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* hypervisor.c - /sys/hypervisor subsystem.
*
* Copyright (C) IBM Corp. 2006
* Copyright (C) 2007 Greg Kroah-Hartman <gregkh@suse.de>
* Copyright (C) 2007 Novell Inc.
- *
- * This file is released under the GPLv2
*/
#include <linux/kobject.h>
diff --git a/drivers/base/init.c b/drivers/base/init.c
index 48c0e220acc0..dd85b05a6a16 100644
--- a/drivers/base/init.c
+++ b/drivers/base/init.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
- *
- * This file is released under the GPLv2
*/
#include <linux/device.h>
diff --git a/drivers/base/isa.c b/drivers/base/isa.c
index 372d10af2600..2772f5d1948a 100644
--- a/drivers/base/isa.c
+++ b/drivers/base/isa.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* ISA bus.
*/
diff --git a/drivers/base/map.c b/drivers/base/map.c
index c1d38234d725..5650ab2b247a 100644
--- a/drivers/base/map.c
+++ b/drivers/base/map.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* linux/drivers/base/map.c
*
* (C) Copyright Al Viro 2002,2003
- * Released under GPL v2.
*
* NOTE: data structure needs to be changed. It works, but for large dev_t
* it will be too slow. It is isolated, though, so these changes will be
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 1d60b58a8c19..fe4b24f05f6a 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -569,7 +569,7 @@ store_hard_offline_page(struct device *dev,
if (kstrtoull(buf, 0, &pfn) < 0)
return -EINVAL;
pfn >>= PAGE_SHIFT;
- ret = memory_failure(pfn, 0, 0);
+ ret = memory_failure(pfn, 0);
return ret ? ret : count;
}
diff --git a/drivers/base/module.c b/drivers/base/module.c
index 2a215780eda2..46ad4d636731 100644
--- a/drivers/base/module.c
+++ b/drivers/base/module.c
@@ -1,8 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* module.c - module sysfs fun for drivers
- *
- * This file is released under the GPLv2
- *
*/
#include <linux/device.h>
#include <linux/module.h>
diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c
index eb929dd6ef1e..c22864458511 100644
--- a/drivers/base/pinctrl.c
+++ b/drivers/base/pinctrl.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver core interface to the pinctrl subsystem.
*
@@ -6,8 +7,6 @@
* Based on bits of regulator core, gpio core and clk core
*
* Author: Linus Walleij <linus.walleij@linaro.org>
- *
- * License terms: GNU General Public License (GPL) version 2
*/
#include <linux/device.h>
diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c
index e5473525e7b2..8e22073aeeed 100644
--- a/drivers/base/platform-msi.c
+++ b/drivers/base/platform-msi.c
@@ -1,20 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* MSI framework for platform devices
*
* Copyright (C) 2015 ARM Limited, All Rights Reserved.
* Author: Marc Zyngier <marc.zyngier@arm.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/device.h>
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index c203fb90c1a0..f1bf7b38d91c 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* platform.c - platform 'pseudo' bus for legacy devices
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
*
- * This file is released under the GPLv2
- *
* Please see Documentation/driver-model/platform.txt for more
* information.
*/
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 0c80bea05bcb..528b24149bc7 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -1032,15 +1032,12 @@ static int genpd_prepare(struct device *dev)
static int genpd_finish_suspend(struct device *dev, bool poweroff)
{
struct generic_pm_domain *genpd;
- int ret;
+ int ret = 0;
genpd = dev_to_genpd(dev);
if (IS_ERR(genpd))
return -EINVAL;
- if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
- return 0;
-
if (poweroff)
ret = pm_generic_poweroff_noirq(dev);
else
@@ -1048,10 +1045,19 @@ static int genpd_finish_suspend(struct device *dev, bool poweroff)
if (ret)
return ret;
- if (genpd->dev_ops.stop && genpd->dev_ops.start) {
- ret = pm_runtime_force_suspend(dev);
- if (ret)
+ if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
+ return 0;
+
+ if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+ !pm_runtime_status_suspended(dev)) {
+ ret = genpd_stop_dev(genpd, dev);
+ if (ret) {
+ if (poweroff)
+ pm_generic_restore_noirq(dev);
+ else
+ pm_generic_resume_noirq(dev);
return ret;
+ }
}
genpd_lock(genpd);
@@ -1085,7 +1091,7 @@ static int genpd_suspend_noirq(struct device *dev)
static int genpd_resume_noirq(struct device *dev)
{
struct generic_pm_domain *genpd;
- int ret = 0;
+ int ret;
dev_dbg(dev, "%s()\n", __func__);
@@ -1094,21 +1100,21 @@ static int genpd_resume_noirq(struct device *dev)
return -EINVAL;
if (dev->power.wakeup_path && genpd_is_active_wakeup(genpd))
- return 0;
+ return pm_generic_resume_noirq(dev);
genpd_lock(genpd);
genpd_sync_power_on(genpd, true, 0);
genpd->suspended_count--;
genpd_unlock(genpd);
- if (genpd->dev_ops.stop && genpd->dev_ops.start)
- ret = pm_runtime_force_resume(dev);
-
- ret = pm_generic_resume_noirq(dev);
- if (ret)
- return ret;
+ if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+ !pm_runtime_status_suspended(dev)) {
+ ret = genpd_start_dev(genpd, dev);
+ if (ret)
+ return ret;
+ }
- return ret;
+ return pm_generic_resume_noirq(dev);
}
/**
@@ -1135,8 +1141,9 @@ static int genpd_freeze_noirq(struct device *dev)
if (ret)
return ret;
- if (genpd->dev_ops.stop && genpd->dev_ops.start)
- ret = pm_runtime_force_suspend(dev);
+ if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+ !pm_runtime_status_suspended(dev))
+ ret = genpd_stop_dev(genpd, dev);
return ret;
}
@@ -1159,8 +1166,9 @@ static int genpd_thaw_noirq(struct device *dev)
if (IS_ERR(genpd))
return -EINVAL;
- if (genpd->dev_ops.stop && genpd->dev_ops.start) {
- ret = pm_runtime_force_resume(dev);
+ if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+ !pm_runtime_status_suspended(dev)) {
+ ret = genpd_start_dev(genpd, dev);
if (ret)
return ret;
}
@@ -1217,8 +1225,9 @@ static int genpd_restore_noirq(struct device *dev)
genpd_sync_power_on(genpd, true, 0);
genpd_unlock(genpd);
- if (genpd->dev_ops.stop && genpd->dev_ops.start) {
- ret = pm_runtime_force_resume(dev);
+ if (genpd->dev_ops.stop && genpd->dev_ops.start &&
+ !pm_runtime_status_suspended(dev)) {
+ ret = genpd_start_dev(genpd, dev);
if (ret)
return ret;
}
@@ -2199,20 +2208,8 @@ int genpd_dev_pm_attach(struct device *dev)
ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
"#power-domain-cells", 0, &pd_args);
- if (ret < 0) {
- if (ret != -ENOENT)
- return ret;
-
- /*
- * Try legacy Samsung-specific bindings
- * (for backwards compatibility of DT ABI)
- */
- pd_args.args_count = 0;
- pd_args.np = of_parse_phandle(dev->of_node,
- "samsung,power-domain", 0);
- if (!pd_args.np)
- return -ENOENT;
- }
+ if (ret < 0)
+ return ret;
mutex_lock(&gpd_list_lock);
pd = genpd_get_from_provider(&pd_args);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 08744b572af6..02a497e7c785 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -18,7 +18,6 @@
*/
#include <linux/device.h>
-#include <linux/kallsyms.h>
#include <linux/export.h>
#include <linux/mutex.h>
#include <linux/pm.h>
@@ -541,6 +540,73 @@ void dev_pm_skip_next_resume_phases(struct device *dev)
}
/**
+ * suspend_event - Return a "suspend" message for given "resume" one.
+ * @resume_msg: PM message representing a system-wide resume transition.
+ */
+static pm_message_t suspend_event(pm_message_t resume_msg)
+{
+ switch (resume_msg.event) {
+ case PM_EVENT_RESUME:
+ return PMSG_SUSPEND;
+ case PM_EVENT_THAW:
+ case PM_EVENT_RESTORE:
+ return PMSG_FREEZE;
+ case PM_EVENT_RECOVER:
+ return PMSG_HIBERNATE;
+ }
+ return PMSG_ON;
+}
+
+/**
+ * dev_pm_may_skip_resume - System-wide device resume optimization check.
+ * @dev: Target device.
+ *
+ * Checks whether or not the device may be left in suspend after a system-wide
+ * transition to the working state.
+ */
+bool dev_pm_may_skip_resume(struct device *dev)
+{
+ return !dev->power.must_resume && pm_transition.event != PM_EVENT_RESTORE;
+}
+
+static pm_callback_t dpm_subsys_resume_noirq_cb(struct device *dev,
+ pm_message_t state,
+ const char **info_p)
+{
+ pm_callback_t callback;
+ const char *info;
+
+ if (dev->pm_domain) {
+ info = "noirq power domain ";
+ callback = pm_noirq_op(&dev->pm_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
+ info = "noirq type ";
+ callback = pm_noirq_op(dev->type->pm, state);
+ } else if (dev->class && dev->class->pm) {
+ info = "noirq class ";
+ callback = pm_noirq_op(dev->class->pm, state);
+ } else if (dev->bus && dev->bus->pm) {
+ info = "noirq bus ";
+ callback = pm_noirq_op(dev->bus->pm, state);
+ } else {
+ return NULL;
+ }
+
+ if (info_p)
+ *info_p = info;
+
+ return callback;
+}
+
+static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
+ pm_message_t state,
+ const char **info_p);
+
+static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
+ pm_message_t state,
+ const char **info_p);
+
+/**
* device_resume_noirq - Execute a "noirq resume" callback for given device.
* @dev: Device to handle.
* @state: PM transition of the system being carried out.
@@ -551,8 +617,9 @@ void dev_pm_skip_next_resume_phases(struct device *dev)
*/
static int device_resume_noirq(struct device *dev, pm_message_t state, bool async)
{
- pm_callback_t callback = NULL;
- const char *info = NULL;
+ pm_callback_t callback;
+ const char *info;
+ bool skip_resume;
int error = 0;
TRACE_DEVICE(dev);
@@ -566,29 +633,61 @@ static int device_resume_noirq(struct device *dev, pm_message_t state, bool asyn
dpm_wait_for_superior(dev, async);
- if (dev->pm_domain) {
- info = "noirq power domain ";
- callback = pm_noirq_op(&dev->pm_domain->ops, state);
- } else if (dev->type && dev->type->pm) {
- info = "noirq type ";
- callback = pm_noirq_op(dev->type->pm, state);
- } else if (dev->class && dev->class->pm) {
- info = "noirq class ";
- callback = pm_noirq_op(dev->class->pm, state);
- } else if (dev->bus && dev->bus->pm) {
- info = "noirq bus ";
- callback = pm_noirq_op(dev->bus->pm, state);
+ skip_resume = dev_pm_may_skip_resume(dev);
+
+ callback = dpm_subsys_resume_noirq_cb(dev, state, &info);
+ if (callback)
+ goto Run;
+
+ if (skip_resume)
+ goto Skip;
+
+ if (dev_pm_smart_suspend_and_suspended(dev)) {
+ pm_message_t suspend_msg = suspend_event(state);
+
+ /*
+ * If "freeze" callbacks have been skipped during a transition
+ * related to hibernation, the subsequent "thaw" callbacks must
+ * be skipped too or bad things may happen. Otherwise, resume
+ * callbacks are going to be run for the device, so its runtime
+ * PM status must be changed to reflect the new state after the
+ * transition under way.
+ */
+ if (!dpm_subsys_suspend_late_cb(dev, suspend_msg, NULL) &&
+ !dpm_subsys_suspend_noirq_cb(dev, suspend_msg, NULL)) {
+ if (state.event == PM_EVENT_THAW) {
+ skip_resume = true;
+ goto Skip;
+ } else {
+ pm_runtime_set_active(dev);
+ }
+ }
}
- if (!callback && dev->driver && dev->driver->pm) {
+ if (dev->driver && dev->driver->pm) {
info = "noirq driver ";
callback = pm_noirq_op(dev->driver->pm, state);
}
+Run:
error = dpm_run_callback(callback, dev, state, info);
+
+Skip:
dev->power.is_noirq_suspended = false;
- Out:
+ if (skip_resume) {
+ /*
+ * The device is going to be left in suspend, but it might not
+ * have been in runtime suspend before the system suspended, so
+ * its runtime PM status needs to be updated to avoid confusing
+ * the runtime PM framework when runtime PM is enabled for the
+ * device again.
+ */
+ pm_runtime_set_suspended(dev);
+ dev_pm_skip_next_resume_phases(dev);
+ }
+
+Out:
complete_all(&dev->power.completion);
TRACE_RESUME(error);
return error;
@@ -681,6 +780,35 @@ void dpm_resume_noirq(pm_message_t state)
dpm_noirq_end();
}
+static pm_callback_t dpm_subsys_resume_early_cb(struct device *dev,
+ pm_message_t state,
+ const char **info_p)
+{
+ pm_callback_t callback;
+ const char *info;
+
+ if (dev->pm_domain) {
+ info = "early power domain ";
+ callback = pm_late_early_op(&dev->pm_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
+ info = "early type ";
+ callback = pm_late_early_op(dev->type->pm, state);
+ } else if (dev->class && dev->class->pm) {
+ info = "early class ";
+ callback = pm_late_early_op(dev->class->pm, state);
+ } else if (dev->bus && dev->bus->pm) {
+ info = "early bus ";
+ callback = pm_late_early_op(dev->bus->pm, state);
+ } else {
+ return NULL;
+ }
+
+ if (info_p)
+ *info_p = info;
+
+ return callback;
+}
+
/**
* device_resume_early - Execute an "early resume" callback for given device.
* @dev: Device to handle.
@@ -691,8 +819,8 @@ void dpm_resume_noirq(pm_message_t state)
*/
static int device_resume_early(struct device *dev, pm_message_t state, bool async)
{
- pm_callback_t callback = NULL;
- const char *info = NULL;
+ pm_callback_t callback;
+ const char *info;
int error = 0;
TRACE_DEVICE(dev);
@@ -706,19 +834,7 @@ static int device_resume_early(struct device *dev, pm_message_t state, bool asyn
dpm_wait_for_superior(dev, async);
- if (dev->pm_domain) {
- info = "early power domain ";
- callback = pm_late_early_op(&dev->pm_domain->ops, state);
- } else if (dev->type && dev->type->pm) {
- info = "early type ";
- callback = pm_late_early_op(dev->type->pm, state);
- } else if (dev->class && dev->class->pm) {
- info = "early class ";
- callback = pm_late_early_op(dev->class->pm, state);
- } else if (dev->bus && dev->bus->pm) {
- info = "early bus ";
- callback = pm_late_early_op(dev->bus->pm, state);
- }
+ callback = dpm_subsys_resume_early_cb(dev, state, &info);
if (!callback && dev->driver && dev->driver->pm) {
info = "early driver ";
@@ -1089,6 +1205,77 @@ static pm_message_t resume_event(pm_message_t sleep_state)
return PMSG_ON;
}
+static void dpm_superior_set_must_resume(struct device *dev)
+{
+ struct device_link *link;
+ int idx;
+
+ if (dev->parent)
+ dev->parent->power.must_resume = true;
+
+ idx = device_links_read_lock();
+
+ list_for_each_entry_rcu(link, &dev->links.suppliers, c_node)
+ link->supplier->power.must_resume = true;
+
+ device_links_read_unlock(idx);
+}
+
+static pm_callback_t dpm_subsys_suspend_noirq_cb(struct device *dev,
+ pm_message_t state,
+ const char **info_p)
+{
+ pm_callback_t callback;
+ const char *info;
+
+ if (dev->pm_domain) {
+ info = "noirq power domain ";
+ callback = pm_noirq_op(&dev->pm_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
+ info = "noirq type ";
+ callback = pm_noirq_op(dev->type->pm, state);
+ } else if (dev->class && dev->class->pm) {
+ info = "noirq class ";
+ callback = pm_noirq_op(dev->class->pm, state);
+ } else if (dev->bus && dev->bus->pm) {
+ info = "noirq bus ";
+ callback = pm_noirq_op(dev->bus->pm, state);
+ } else {
+ return NULL;
+ }
+
+ if (info_p)
+ *info_p = info;
+
+ return callback;
+}
+
+static bool device_must_resume(struct device *dev, pm_message_t state,
+ bool no_subsys_suspend_noirq)
+{
+ pm_message_t resume_msg = resume_event(state);
+
+ /*
+ * If all of the device driver's "noirq", "late" and "early" callbacks
+ * are invoked directly by the core, the decision to allow the device to
+ * stay in suspend can be based on its current runtime PM status and its
+ * wakeup settings.
+ */
+ if (no_subsys_suspend_noirq &&
+ !dpm_subsys_suspend_late_cb(dev, state, NULL) &&
+ !dpm_subsys_resume_early_cb(dev, resume_msg, NULL) &&
+ !dpm_subsys_resume_noirq_cb(dev, resume_msg, NULL))
+ return !pm_runtime_status_suspended(dev) &&
+ (resume_msg.event != PM_EVENT_RESUME ||
+ (device_can_wakeup(dev) && !device_may_wakeup(dev)));
+
+ /*
+ * The only safe strategy here is to require that if the device may not
+ * be left in suspend, resume callbacks must be invoked for it.
+ */
+ return !dev->power.may_skip_resume;
+}
+
/**
* __device_suspend_noirq - Execute a "noirq suspend" callback for given device.
* @dev: Device to handle.
@@ -1100,8 +1287,9 @@ static pm_message_t resume_event(pm_message_t sleep_state)
*/
static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool async)
{
- pm_callback_t callback = NULL;
- const char *info = NULL;
+ pm_callback_t callback;
+ const char *info;
+ bool no_subsys_cb = false;
int error = 0;
TRACE_DEVICE(dev);
@@ -1120,30 +1308,40 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
- if (dev->pm_domain) {
- info = "noirq power domain ";
- callback = pm_noirq_op(&dev->pm_domain->ops, state);
- } else if (dev->type && dev->type->pm) {
- info = "noirq type ";
- callback = pm_noirq_op(dev->type->pm, state);
- } else if (dev->class && dev->class->pm) {
- info = "noirq class ";
- callback = pm_noirq_op(dev->class->pm, state);
- } else if (dev->bus && dev->bus->pm) {
- info = "noirq bus ";
- callback = pm_noirq_op(dev->bus->pm, state);
- }
+ callback = dpm_subsys_suspend_noirq_cb(dev, state, &info);
+ if (callback)
+ goto Run;
- if (!callback && dev->driver && dev->driver->pm) {
+ no_subsys_cb = !dpm_subsys_suspend_late_cb(dev, state, NULL);
+
+ if (dev_pm_smart_suspend_and_suspended(dev) && no_subsys_cb)
+ goto Skip;
+
+ if (dev->driver && dev->driver->pm) {
info = "noirq driver ";
callback = pm_noirq_op(dev->driver->pm, state);
}
+Run:
error = dpm_run_callback(callback, dev, state, info);
- if (!error)
- dev->power.is_noirq_suspended = true;
- else
+ if (error) {
async_error = error;
+ goto Complete;
+ }
+
+Skip:
+ dev->power.is_noirq_suspended = true;
+
+ if (dev_pm_test_driver_flags(dev, DPM_FLAG_LEAVE_SUSPENDED)) {
+ dev->power.must_resume = dev->power.must_resume ||
+ atomic_read(&dev->power.usage_count) > 1 ||
+ device_must_resume(dev, state, no_subsys_cb);
+ } else {
+ dev->power.must_resume = true;
+ }
+
+ if (dev->power.must_resume)
+ dpm_superior_set_must_resume(dev);
Complete:
complete_all(&dev->power.completion);
@@ -1249,6 +1447,50 @@ int dpm_suspend_noirq(pm_message_t state)
return ret;
}
+static void dpm_propagate_wakeup_to_parent(struct device *dev)
+{
+ struct device *parent = dev->parent;
+
+ if (!parent)
+ return;
+
+ spin_lock_irq(&parent->power.lock);
+
+ if (dev->power.wakeup_path && !parent->power.ignore_children)
+ parent->power.wakeup_path = true;
+
+ spin_unlock_irq(&parent->power.lock);
+}
+
+static pm_callback_t dpm_subsys_suspend_late_cb(struct device *dev,
+ pm_message_t state,
+ const char **info_p)
+{
+ pm_callback_t callback;
+ const char *info;
+
+ if (dev->pm_domain) {
+ info = "late power domain ";
+ callback = pm_late_early_op(&dev->pm_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
+ info = "late type ";
+ callback = pm_late_early_op(dev->type->pm, state);
+ } else if (dev->class && dev->class->pm) {
+ info = "late class ";
+ callback = pm_late_early_op(dev->class->pm, state);
+ } else if (dev->bus && dev->bus->pm) {
+ info = "late bus ";
+ callback = pm_late_early_op(dev->bus->pm, state);
+ } else {
+ return NULL;
+ }
+
+ if (info_p)
+ *info_p = info;
+
+ return callback;
+}
+
/**
* __device_suspend_late - Execute a "late suspend" callback for given device.
* @dev: Device to handle.
@@ -1259,8 +1501,8 @@ int dpm_suspend_noirq(pm_message_t state)
*/
static int __device_suspend_late(struct device *dev, pm_message_t state, bool async)
{
- pm_callback_t callback = NULL;
- const char *info = NULL;
+ pm_callback_t callback;
+ const char *info;
int error = 0;
TRACE_DEVICE(dev);
@@ -1281,30 +1523,29 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as
if (dev->power.syscore || dev->power.direct_complete)
goto Complete;
- if (dev->pm_domain) {
- info = "late power domain ";
- callback = pm_late_early_op(&dev->pm_domain->ops, state);
- } else if (dev->type && dev->type->pm) {
- info = "late type ";
- callback = pm_late_early_op(dev->type->pm, state);
- } else if (dev->class && dev->class->pm) {
- info = "late class ";
- callback = pm_late_early_op(dev->class->pm, state);
- } else if (dev->bus && dev->bus->pm) {
- info = "late bus ";
- callback = pm_late_early_op(dev->bus->pm, state);
- }
+ callback = dpm_subsys_suspend_late_cb(dev, state, &info);
+ if (callback)
+ goto Run;
- if (!callback && dev->driver && dev->driver->pm) {
+ if (dev_pm_smart_suspend_and_suspended(dev) &&
+ !dpm_subsys_suspend_noirq_cb(dev, state, NULL))
+ goto Skip;
+
+ if (dev->driver && dev->driver->pm) {
info = "late driver ";
callback = pm_late_early_op(dev->driver->pm, state);
}
+Run:
error = dpm_run_callback(callback, dev, state, info);
- if (!error)
- dev->power.is_late_suspended = true;
- else
+ if (error) {
async_error = error;
+ goto Complete;
+ }
+ dpm_propagate_wakeup_to_parent(dev);
+
+Skip:
+ dev->power.is_late_suspended = true;
Complete:
TRACE_SUSPEND(error);
@@ -1435,11 +1676,17 @@ static int legacy_suspend(struct device *dev, pm_message_t state,
return error;
}
-static void dpm_clear_suppliers_direct_complete(struct device *dev)
+static void dpm_clear_superiors_direct_complete(struct device *dev)
{
struct device_link *link;
int idx;
+ if (dev->parent) {
+ spin_lock_irq(&dev->parent->power.lock);
+ dev->parent->power.direct_complete = false;
+ spin_unlock_irq(&dev->parent->power.lock);
+ }
+
idx = device_links_read_lock();
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node) {
@@ -1500,6 +1747,9 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
dev->power.direct_complete = false;
}
+ dev->power.may_skip_resume = false;
+ dev->power.must_resume = false;
+
dpm_watchdog_set(&wd, dev);
device_lock(dev);
@@ -1543,20 +1793,12 @@ static int __device_suspend(struct device *dev, pm_message_t state, bool async)
End:
if (!error) {
- struct device *parent = dev->parent;
-
dev->power.is_suspended = true;
- if (parent) {
- spin_lock_irq(&parent->power.lock);
-
- dev->parent->power.direct_complete = false;
- if (dev->power.wakeup_path
- && !dev->parent->power.ignore_children)
- dev->parent->power.wakeup_path = true;
+ if (device_may_wakeup(dev))
+ dev->power.wakeup_path = true;
- spin_unlock_irq(&parent->power.lock);
- }
- dpm_clear_suppliers_direct_complete(dev);
+ dpm_propagate_wakeup_to_parent(dev);
+ dpm_clear_superiors_direct_complete(dev);
}
device_unlock(dev);
@@ -1665,8 +1907,9 @@ static int device_prepare(struct device *dev, pm_message_t state)
if (dev->power.syscore)
return 0;
- WARN_ON(dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND) &&
- !pm_runtime_enabled(dev));
+ WARN_ON(!pm_runtime_enabled(dev) &&
+ dev_pm_test_driver_flags(dev, DPM_FLAG_SMART_SUSPEND |
+ DPM_FLAG_LEAVE_SUSPENDED));
/*
* If a device's parent goes into runtime suspend at the wrong time,
@@ -1678,7 +1921,7 @@ static int device_prepare(struct device *dev, pm_message_t state)
device_lock(dev);
- dev->power.wakeup_path = device_may_wakeup(dev);
+ dev->power.wakeup_path = false;
if (dev->power.no_pm_callbacks) {
ret = 1; /* Let device go direct_complete */
diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h
index 7beee75399d4..21244c53e377 100644
--- a/drivers/base/power/power.h
+++ b/drivers/base/power/power.h
@@ -41,20 +41,15 @@ extern void dev_pm_disable_wake_irq_check(struct device *dev);
#ifdef CONFIG_PM_SLEEP
-extern int device_wakeup_attach_irq(struct device *dev,
- struct wake_irq *wakeirq);
+extern void device_wakeup_attach_irq(struct device *dev, struct wake_irq *wakeirq);
extern void device_wakeup_detach_irq(struct device *dev);
extern void device_wakeup_arm_wake_irqs(void);
extern void device_wakeup_disarm_wake_irqs(void);
#else
-static inline int
-device_wakeup_attach_irq(struct device *dev,
- struct wake_irq *wakeirq)
-{
- return 0;
-}
+static inline void device_wakeup_attach_irq(struct device *dev,
+ struct wake_irq *wakeirq) {}
static inline void device_wakeup_detach_irq(struct device *dev)
{
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 6e89b51ea3d9..8bef3cb2424d 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -1613,22 +1613,34 @@ void pm_runtime_drop_link(struct device *dev)
spin_unlock_irq(&dev->power.lock);
}
+static bool pm_runtime_need_not_resume(struct device *dev)
+{
+ return atomic_read(&dev->power.usage_count) <= 1 &&
+ (atomic_read(&dev->power.child_count) == 0 ||
+ dev->power.ignore_children);
+}
+
/**
* pm_runtime_force_suspend - Force a device into suspend state if needed.
* @dev: Device to suspend.
*
* Disable runtime PM so we safely can check the device's runtime PM status and
- * if it is active, invoke it's .runtime_suspend callback to bring it into
- * suspend state. Keep runtime PM disabled to preserve the state unless we
- * encounter errors.
+ * if it is active, invoke its ->runtime_suspend callback to suspend it and
+ * change its runtime PM status field to RPM_SUSPENDED. Also, if the device's
+ * usage and children counters don't indicate that the device was in use before
+ * the system-wide transition under way, decrement its parent's children counter
+ * (if there is a parent). Keep runtime PM disabled to preserve the state
+ * unless we encounter errors.
*
* Typically this function may be invoked from a system suspend callback to make
- * sure the device is put into low power state.
+ * sure the device is put into low power state and it should only be used during
+ * system-wide PM transitions to sleep states. It assumes that the analogous
+ * pm_runtime_force_resume() will be used to resume the device.
*/
int pm_runtime_force_suspend(struct device *dev)
{
int (*callback)(struct device *);
- int ret = 0;
+ int ret;
pm_runtime_disable(dev);
if (pm_runtime_status_suspended(dev))
@@ -1636,27 +1648,23 @@ int pm_runtime_force_suspend(struct device *dev)
callback = RPM_GET_CALLBACK(dev, runtime_suspend);
- if (!callback) {
- ret = -ENOSYS;
- goto err;
- }
-
- ret = callback(dev);
+ ret = callback ? callback(dev) : 0;
if (ret)
goto err;
/*
- * Increase the runtime PM usage count for the device's parent, in case
- * when we find the device being used when system suspend was invoked.
- * This informs pm_runtime_force_resume() to resume the parent
- * immediately, which is needed to be able to resume its children,
- * when not deferring the resume to be managed via runtime PM.
+ * If the device can stay in suspend after the system-wide transition
+ * to the working state that will follow, drop the children counter of
+ * its parent, but set its status to RPM_SUSPENDED anyway in case this
+ * function will be called again for it in the meantime.
*/
- if (dev->parent && atomic_read(&dev->power.usage_count) > 1)
- pm_runtime_get_noresume(dev->parent);
+ if (pm_runtime_need_not_resume(dev))
+ pm_runtime_set_suspended(dev);
+ else
+ __update_runtime_status(dev, RPM_SUSPENDED);
- pm_runtime_set_suspended(dev);
return 0;
+
err:
pm_runtime_enable(dev);
return ret;
@@ -1669,13 +1677,9 @@ EXPORT_SYMBOL_GPL(pm_runtime_force_suspend);
*
* Prior invoking this function we expect the user to have brought the device
* into low power state by a call to pm_runtime_force_suspend(). Here we reverse
- * those actions and brings the device into full power, if it is expected to be
- * used on system resume. To distinguish that, we check whether the runtime PM
- * usage count is greater than 1 (the PM core increases the usage count in the
- * system PM prepare phase), as that indicates a real user (such as a subsystem,
- * driver, userspace, etc.) is using it. If that is the case, the device is
- * expected to be used on system resume as well, so then we resume it. In the
- * other case, we defer the resume to be managed via runtime PM.
+ * those actions and bring the device into full power, if it is expected to be
+ * used on system resume. In the other case, we defer the resume to be managed
+ * via runtime PM.
*
* Typically this function may be invoked from a system resume callback.
*/
@@ -1684,32 +1688,18 @@ int pm_runtime_force_resume(struct device *dev)
int (*callback)(struct device *);
int ret = 0;
- callback = RPM_GET_CALLBACK(dev, runtime_resume);
-
- if (!callback) {
- ret = -ENOSYS;
- goto out;
- }
-
- if (!pm_runtime_status_suspended(dev))
+ if (!pm_runtime_status_suspended(dev) || pm_runtime_need_not_resume(dev))
goto out;
/*
- * Decrease the parent's runtime PM usage count, if we increased it
- * during system suspend in pm_runtime_force_suspend().
- */
- if (atomic_read(&dev->power.usage_count) > 1) {
- if (dev->parent)
- pm_runtime_put_noidle(dev->parent);
- } else {
- goto out;
- }
+ * The value of the parent's children counter is correct already, so
+ * just update the status of the device.
+ */
+ __update_runtime_status(dev, RPM_ACTIVE);
- ret = pm_runtime_set_active(dev);
- if (ret)
- goto out;
+ callback = RPM_GET_CALLBACK(dev, runtime_resume);
- ret = callback(dev);
+ ret = callback ? callback(dev) : 0;
if (ret) {
pm_runtime_set_suspended(dev);
goto out;
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index e153e28b1857..0f651efc58a1 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -108,16 +108,10 @@ static ssize_t control_show(struct device *dev, struct device_attribute *attr,
static ssize_t control_store(struct device * dev, struct device_attribute *attr,
const char * buf, size_t n)
{
- char *cp;
- int len = n;
-
- cp = memchr(buf, '\n', n);
- if (cp)
- len = cp - buf;
device_lock(dev);
- if (len == sizeof ctrl_auto - 1 && strncmp(buf, ctrl_auto, len) == 0)
+ if (sysfs_streq(buf, ctrl_auto))
pm_runtime_allow(dev);
- else if (len == sizeof ctrl_on - 1 && strncmp(buf, ctrl_on, len) == 0)
+ else if (sysfs_streq(buf, ctrl_on))
pm_runtime_forbid(dev);
else
n = -EINVAL;
@@ -125,9 +119,9 @@ static ssize_t control_store(struct device * dev, struct device_attribute *attr,
return n;
}
-static DEVICE_ATTR(control, 0644, control_show, control_store);
+static DEVICE_ATTR_RW(control);
-static ssize_t rtpm_active_time_show(struct device *dev,
+static ssize_t runtime_active_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
@@ -138,9 +132,9 @@ static ssize_t rtpm_active_time_show(struct device *dev,
return ret;
}
-static DEVICE_ATTR(runtime_active_time, 0444, rtpm_active_time_show, NULL);
+static DEVICE_ATTR_RO(runtime_active_time);
-static ssize_t rtpm_suspended_time_show(struct device *dev,
+static ssize_t runtime_suspended_time_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int ret;
@@ -152,9 +146,9 @@ static ssize_t rtpm_suspended_time_show(struct device *dev,
return ret;
}
-static DEVICE_ATTR(runtime_suspended_time, 0444, rtpm_suspended_time_show, NULL);
+static DEVICE_ATTR_RO(runtime_suspended_time);
-static ssize_t rtpm_status_show(struct device *dev,
+static ssize_t runtime_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
const char *p;
@@ -184,7 +178,7 @@ static ssize_t rtpm_status_show(struct device *dev,
return sprintf(buf, p);
}
-static DEVICE_ATTR(runtime_status, 0444, rtpm_status_show, NULL);
+static DEVICE_ATTR_RO(runtime_status);
static ssize_t autosuspend_delay_ms_show(struct device *dev,
struct device_attribute *attr, char *buf)
@@ -211,26 +205,25 @@ static ssize_t autosuspend_delay_ms_store(struct device *dev,
return n;
}
-static DEVICE_ATTR(autosuspend_delay_ms, 0644, autosuspend_delay_ms_show,
- autosuspend_delay_ms_store);
+static DEVICE_ATTR_RW(autosuspend_delay_ms);
-static ssize_t pm_qos_resume_latency_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t pm_qos_resume_latency_us_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
s32 value = dev_pm_qos_requested_resume_latency(dev);
if (value == 0)
return sprintf(buf, "n/a\n");
- else if (value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
+ if (value == PM_QOS_RESUME_LATENCY_NO_CONSTRAINT)
value = 0;
return sprintf(buf, "%d\n", value);
}
-static ssize_t pm_qos_resume_latency_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t n)
+static ssize_t pm_qos_resume_latency_us_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t n)
{
s32 value;
int ret;
@@ -245,7 +238,7 @@ static ssize_t pm_qos_resume_latency_store(struct device *dev,
if (value == 0)
value = PM_QOS_RESUME_LATENCY_NO_CONSTRAINT;
- } else if (!strcmp(buf, "n/a") || !strcmp(buf, "n/a\n")) {
+ } else if (sysfs_streq(buf, "n/a")) {
value = 0;
} else {
return -EINVAL;
@@ -256,26 +249,25 @@ static ssize_t pm_qos_resume_latency_store(struct device *dev,
return ret < 0 ? ret : n;
}
-static DEVICE_ATTR(pm_qos_resume_latency_us, 0644,
- pm_qos_resume_latency_show, pm_qos_resume_latency_store);
+static DEVICE_ATTR_RW(pm_qos_resume_latency_us);
-static ssize_t pm_qos_latency_tolerance_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t pm_qos_latency_tolerance_us_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
s32 value = dev_pm_qos_get_user_latency_tolerance(dev);
if (value < 0)
return sprintf(buf, "auto\n");
- else if (value == PM_QOS_LATENCY_ANY)
+ if (value == PM_QOS_LATENCY_ANY)
return sprintf(buf, "any\n");
return sprintf(buf, "%d\n", value);
}
-static ssize_t pm_qos_latency_tolerance_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t n)
+static ssize_t pm_qos_latency_tolerance_us_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t n)
{
s32 value;
int ret;
@@ -285,9 +277,9 @@ static ssize_t pm_qos_latency_tolerance_store(struct device *dev,
if (value < 0)
return -EINVAL;
} else {
- if (!strcmp(buf, "auto") || !strcmp(buf, "auto\n"))
+ if (sysfs_streq(buf, "auto"))
value = PM_QOS_LATENCY_TOLERANCE_NO_CONSTRAINT;
- else if (!strcmp(buf, "any") || !strcmp(buf, "any\n"))
+ else if (sysfs_streq(buf, "any"))
value = PM_QOS_LATENCY_ANY;
else
return -EINVAL;
@@ -296,8 +288,7 @@ static ssize_t pm_qos_latency_tolerance_store(struct device *dev,
return ret < 0 ? ret : n;
}
-static DEVICE_ATTR(pm_qos_latency_tolerance_us, 0644,
- pm_qos_latency_tolerance_show, pm_qos_latency_tolerance_store);
+static DEVICE_ATTR_RW(pm_qos_latency_tolerance_us);
static ssize_t pm_qos_no_power_off_show(struct device *dev,
struct device_attribute *attr,
@@ -323,49 +314,39 @@ static ssize_t pm_qos_no_power_off_store(struct device *dev,
return ret < 0 ? ret : n;
}
-static DEVICE_ATTR(pm_qos_no_power_off, 0644,
- pm_qos_no_power_off_show, pm_qos_no_power_off_store);
+static DEVICE_ATTR_RW(pm_qos_no_power_off);
#ifdef CONFIG_PM_SLEEP
static const char _enabled[] = "enabled";
static const char _disabled[] = "disabled";
-static ssize_t
-wake_show(struct device * dev, struct device_attribute *attr, char * buf)
+static ssize_t wakeup_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
return sprintf(buf, "%s\n", device_can_wakeup(dev)
? (device_may_wakeup(dev) ? _enabled : _disabled)
: "");
}
-static ssize_t
-wake_store(struct device * dev, struct device_attribute *attr,
- const char * buf, size_t n)
+static ssize_t wakeup_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t n)
{
- char *cp;
- int len = n;
-
if (!device_can_wakeup(dev))
return -EINVAL;
- cp = memchr(buf, '\n', n);
- if (cp)
- len = cp - buf;
- if (len == sizeof _enabled - 1
- && strncmp(buf, _enabled, sizeof _enabled - 1) == 0)
+ if (sysfs_streq(buf, _enabled))
device_set_wakeup_enable(dev, 1);
- else if (len == sizeof _disabled - 1
- && strncmp(buf, _disabled, sizeof _disabled - 1) == 0)
+ else if (sysfs_streq(buf, _disabled))
device_set_wakeup_enable(dev, 0);
else
return -EINVAL;
return n;
}
-static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
+static DEVICE_ATTR_RW(wakeup);
static ssize_t wakeup_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
unsigned long count = 0;
bool enabled = false;
@@ -379,10 +360,11 @@ static ssize_t wakeup_count_show(struct device *dev,
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_count, 0444, wakeup_count_show, NULL);
+static DEVICE_ATTR_RO(wakeup_count);
static ssize_t wakeup_active_count_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
unsigned long count = 0;
bool enabled = false;
@@ -396,11 +378,11 @@ static ssize_t wakeup_active_count_show(struct device *dev,
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_active_count, 0444, wakeup_active_count_show, NULL);
+static DEVICE_ATTR_RO(wakeup_active_count);
static ssize_t wakeup_abort_count_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+ struct device_attribute *attr,
+ char *buf)
{
unsigned long count = 0;
bool enabled = false;
@@ -414,7 +396,7 @@ static ssize_t wakeup_abort_count_show(struct device *dev,
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_abort_count, 0444, wakeup_abort_count_show, NULL);
+static DEVICE_ATTR_RO(wakeup_abort_count);
static ssize_t wakeup_expire_count_show(struct device *dev,
struct device_attribute *attr,
@@ -432,10 +414,10 @@ static ssize_t wakeup_expire_count_show(struct device *dev,
return enabled ? sprintf(buf, "%lu\n", count) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_expire_count, 0444, wakeup_expire_count_show, NULL);
+static DEVICE_ATTR_RO(wakeup_expire_count);
static ssize_t wakeup_active_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+ struct device_attribute *attr, char *buf)
{
unsigned int active = 0;
bool enabled = false;
@@ -449,10 +431,11 @@ static ssize_t wakeup_active_show(struct device *dev,
return enabled ? sprintf(buf, "%u\n", active) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_active, 0444, wakeup_active_show, NULL);
+static DEVICE_ATTR_RO(wakeup_active);
-static ssize_t wakeup_total_time_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t wakeup_total_time_ms_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
s64 msec = 0;
bool enabled = false;
@@ -466,10 +449,10 @@ static ssize_t wakeup_total_time_show(struct device *dev,
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_total_time_ms, 0444, wakeup_total_time_show, NULL);
+static DEVICE_ATTR_RO(wakeup_total_time_ms);
-static ssize_t wakeup_max_time_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t wakeup_max_time_ms_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
s64 msec = 0;
bool enabled = false;
@@ -483,10 +466,11 @@ static ssize_t wakeup_max_time_show(struct device *dev,
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_max_time_ms, 0444, wakeup_max_time_show, NULL);
+static DEVICE_ATTR_RO(wakeup_max_time_ms);
-static ssize_t wakeup_last_time_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t wakeup_last_time_ms_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
s64 msec = 0;
bool enabled = false;
@@ -500,12 +484,12 @@ static ssize_t wakeup_last_time_show(struct device *dev,
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_last_time_ms, 0444, wakeup_last_time_show, NULL);
+static DEVICE_ATTR_RO(wakeup_last_time_ms);
#ifdef CONFIG_PM_AUTOSLEEP
-static ssize_t wakeup_prevent_sleep_time_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t wakeup_prevent_sleep_time_ms_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
s64 msec = 0;
bool enabled = false;
@@ -519,40 +503,39 @@ static ssize_t wakeup_prevent_sleep_time_show(struct device *dev,
return enabled ? sprintf(buf, "%lld\n", msec) : sprintf(buf, "\n");
}
-static DEVICE_ATTR(wakeup_prevent_sleep_time_ms, 0444,
- wakeup_prevent_sleep_time_show, NULL);
+static DEVICE_ATTR_RO(wakeup_prevent_sleep_time_ms);
#endif /* CONFIG_PM_AUTOSLEEP */
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_ADVANCED_DEBUG
-static ssize_t rtpm_usagecount_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t runtime_usage_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", atomic_read(&dev->power.usage_count));
}
+static DEVICE_ATTR_RO(runtime_usage);
-static ssize_t rtpm_children_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t runtime_active_kids_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
{
return sprintf(buf, "%d\n", dev->power.ignore_children ?
0 : atomic_read(&dev->power.child_count));
}
+static DEVICE_ATTR_RO(runtime_active_kids);
-static ssize_t rtpm_enabled_show(struct device *dev,
- struct device_attribute *attr, char *buf)
+static ssize_t runtime_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
- if ((dev->power.disable_depth) && (dev->power.runtime_auto == false))
+ if (dev->power.disable_depth && (dev->power.runtime_auto == false))
return sprintf(buf, "disabled & forbidden\n");
- else if (dev->power.disable_depth)
+ if (dev->power.disable_depth)
return sprintf(buf, "disabled\n");
- else if (dev->power.runtime_auto == false)
+ if (dev->power.runtime_auto == false)
return sprintf(buf, "forbidden\n");
return sprintf(buf, "enabled\n");
}
-
-static DEVICE_ATTR(runtime_usage, 0444, rtpm_usagecount_show, NULL);
-static DEVICE_ATTR(runtime_active_kids, 0444, rtpm_children_show, NULL);
-static DEVICE_ATTR(runtime_enabled, 0444, rtpm_enabled_show, NULL);
+static DEVICE_ATTR_RO(runtime_enabled);
#ifdef CONFIG_PM_SLEEP
static ssize_t async_show(struct device *dev, struct device_attribute *attr,
@@ -566,23 +549,16 @@ static ssize_t async_show(struct device *dev, struct device_attribute *attr,
static ssize_t async_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t n)
{
- char *cp;
- int len = n;
-
- cp = memchr(buf, '\n', n);
- if (cp)
- len = cp - buf;
- if (len == sizeof _enabled - 1 && strncmp(buf, _enabled, len) == 0)
+ if (sysfs_streq(buf, _enabled))
device_enable_async_suspend(dev);
- else if (len == sizeof _disabled - 1 &&
- strncmp(buf, _disabled, len) == 0)
+ else if (sysfs_streq(buf, _disabled))
device_disable_async_suspend(dev);
else
return -EINVAL;
return n;
}
-static DEVICE_ATTR(async, 0644, async_show, async_store);
+static DEVICE_ATTR_RW(async);
#endif /* CONFIG_PM_SLEEP */
#endif /* CONFIG_PM_ADVANCED_DEBUG */
diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c
index ae0429827f31..a8ac86e4d79e 100644
--- a/drivers/base/power/wakeirq.c
+++ b/drivers/base/power/wakeirq.c
@@ -33,7 +33,6 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq,
struct wake_irq *wirq)
{
unsigned long flags;
- int err;
if (!dev || !wirq)
return -EINVAL;
@@ -45,12 +44,11 @@ static int dev_pm_attach_wake_irq(struct device *dev, int irq,
return -EEXIST;
}
- err = device_wakeup_attach_irq(dev, wirq);
- if (!err)
- dev->power.wakeirq = wirq;
+ dev->power.wakeirq = wirq;
+ device_wakeup_attach_irq(dev, wirq);
spin_unlock_irqrestore(&dev->power.lock, flags);
- return err;
+ return 0;
}
/**
diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c
index 38559f04db2c..ea01621ed769 100644
--- a/drivers/base/power/wakeup.c
+++ b/drivers/base/power/wakeup.c
@@ -19,6 +19,11 @@
#include "power.h"
+#ifndef CONFIG_SUSPEND
+suspend_state_t pm_suspend_target_state;
+#define pm_suspend_target_state (PM_SUSPEND_ON)
+#endif
+
/*
* If set, the suspend/hibernate code will abort transitions to a sleep state
* if wakeup events are registered during or immediately before the transition.
@@ -268,6 +273,9 @@ int device_wakeup_enable(struct device *dev)
if (!dev || !dev->power.can_wakeup)
return -EINVAL;
+ if (pm_suspend_target_state != PM_SUSPEND_ON)
+ dev_dbg(dev, "Suspicious %s() during system transition!\n", __func__);
+
ws = wakeup_source_register(dev_name(dev));
if (!ws)
return -ENOMEM;
@@ -291,22 +299,19 @@ EXPORT_SYMBOL_GPL(device_wakeup_enable);
*
* Call under the device's power.lock lock.
*/
-int device_wakeup_attach_irq(struct device *dev,
+void device_wakeup_attach_irq(struct device *dev,
struct wake_irq *wakeirq)
{
struct wakeup_source *ws;
ws = dev->power.wakeup;
- if (!ws) {
- dev_err(dev, "forgot to call call device_init_wakeup?\n");
- return -EINVAL;
- }
+ if (!ws)
+ return;
if (ws->wakeirq)
- return -EEXIST;
+ dev_err(dev, "Leftover wakeup IRQ found, overriding\n");
ws->wakeirq = wakeirq;
- return 0;
}
/**
@@ -448,9 +453,7 @@ int device_init_wakeup(struct device *dev, bool enable)
device_set_wakeup_capable(dev, true);
ret = device_wakeup_enable(dev);
} else {
- if (dev->power.can_wakeup)
- device_wakeup_disable(dev);
-
+ device_wakeup_disable(dev);
device_set_wakeup_capable(dev, false);
}
@@ -464,9 +467,6 @@ EXPORT_SYMBOL_GPL(device_init_wakeup);
*/
int device_set_wakeup_enable(struct device *dev, bool enable)
{
- if (!dev || !dev->power.can_wakeup)
- return -EINVAL;
-
return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev);
}
EXPORT_SYMBOL_GPL(device_set_wakeup_enable);
diff --git a/drivers/base/property.c b/drivers/base/property.c
index 851b1b6596a4..302236281d83 100644
--- a/drivers/base/property.c
+++ b/drivers/base/property.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* property.c - Unified device property interface.
*
* Copyright (C) 2014, Intel Corporation
* Authors: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
* Mika Westerberg <mika.westerberg@linux.intel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/acpi.h>
@@ -16,6 +13,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_graph.h>
+#include <linux/of_irq.h>
#include <linux/property.h>
#include <linux/etherdevice.h>
#include <linux/phy.h>
@@ -698,6 +696,23 @@ int fwnode_property_get_reference_args(const struct fwnode_handle *fwnode,
}
EXPORT_SYMBOL_GPL(fwnode_property_get_reference_args);
+static void property_entry_free_data(const struct property_entry *p)
+{
+ size_t i, nval;
+
+ if (p->is_array) {
+ if (p->is_string && p->pointer.str) {
+ nval = p->length / sizeof(const char *);
+ for (i = 0; i < nval; i++)
+ kfree(p->pointer.str[i]);
+ }
+ kfree(p->pointer.raw_data);
+ } else if (p->is_string) {
+ kfree(p->value.str);
+ }
+ kfree(p->name);
+}
+
static int property_copy_string_array(struct property_entry *dst,
const struct property_entry *src)
{
@@ -728,34 +743,24 @@ static int property_entry_copy_data(struct property_entry *dst,
{
int error;
- dst->name = kstrdup(src->name, GFP_KERNEL);
- if (!dst->name)
- return -ENOMEM;
-
if (src->is_array) {
- if (!src->length) {
- error = -ENODATA;
- goto out_free_name;
- }
+ if (!src->length)
+ return -ENODATA;
if (src->is_string) {
error = property_copy_string_array(dst, src);
if (error)
- goto out_free_name;
+ return error;
} else {
dst->pointer.raw_data = kmemdup(src->pointer.raw_data,
src->length, GFP_KERNEL);
- if (!dst->pointer.raw_data) {
- error = -ENOMEM;
- goto out_free_name;
- }
+ if (!dst->pointer.raw_data)
+ return -ENOMEM;
}
} else if (src->is_string) {
dst->value.str = kstrdup(src->value.str, GFP_KERNEL);
- if (!dst->value.str && src->value.str) {
- error = -ENOMEM;
- goto out_free_name;
- }
+ if (!dst->value.str && src->value.str)
+ return -ENOMEM;
} else {
dst->value.raw_data = src->value.raw_data;
}
@@ -764,28 +769,15 @@ static int property_entry_copy_data(struct property_entry *dst,
dst->is_array = src->is_array;
dst->is_string = src->is_string;
+ dst->name = kstrdup(src->name, GFP_KERNEL);
+ if (!dst->name)
+ goto out_free_data;
+
return 0;
-out_free_name:
- kfree(dst->name);
- return error;
-}
-
-static void property_entry_free_data(const struct property_entry *p)
-{
- size_t i, nval;
-
- if (p->is_array) {
- if (p->is_string && p->pointer.str) {
- nval = p->length / sizeof(const char *);
- for (i = 0; i < nval; i++)
- kfree(p->pointer.str[i]);
- }
- kfree(p->pointer.raw_data);
- } else if (p->is_string) {
- kfree(p->value.str);
- }
- kfree(p->name);
+out_free_data:
+ property_entry_free_data(dst);
+ return -ENOMEM;
}
/**
@@ -997,6 +989,32 @@ fwnode_get_next_child_node(const struct fwnode_handle *fwnode,
EXPORT_SYMBOL_GPL(fwnode_get_next_child_node);
/**
+ * fwnode_get_next_available_child_node - Return the next
+ * available child node handle for a node
+ * @fwnode: Firmware node to find the next child node for.
+ * @child: Handle to one of the node's child nodes or a %NULL handle.
+ */
+struct fwnode_handle *
+fwnode_get_next_available_child_node(const struct fwnode_handle *fwnode,
+ struct fwnode_handle *child)
+{
+ struct fwnode_handle *next_child = child;
+
+ if (!fwnode)
+ return NULL;
+
+ do {
+ next_child = fwnode_get_next_child_node(fwnode, next_child);
+
+ if (!next_child || fwnode_device_is_available(next_child))
+ break;
+ } while (next_child);
+
+ return next_child;
+}
+EXPORT_SYMBOL_GPL(fwnode_get_next_available_child_node);
+
+/**
* device_get_next_child_node - Return the next child node handle for a device
* @dev: Device to find the next child node for.
* @child: Handle to one of the device's child nodes or a null handle.
@@ -1126,21 +1144,21 @@ enum dev_dma_attr device_get_dma_attr(struct device *dev)
EXPORT_SYMBOL_GPL(device_get_dma_attr);
/**
- * device_get_phy_mode - Get phy mode for given device
- * @dev: Pointer to the given device
+ * fwnode_get_phy_mode - Get phy mode for given firmware node
+ * @fwnode: Pointer to the given node
*
* The function gets phy interface string from property 'phy-mode' or
* 'phy-connection-type', and return its index in phy_modes table, or errno in
* error case.
*/
-int device_get_phy_mode(struct device *dev)
+int fwnode_get_phy_mode(struct fwnode_handle *fwnode)
{
const char *pm;
int err, i;
- err = device_property_read_string(dev, "phy-mode", &pm);
+ err = fwnode_property_read_string(fwnode, "phy-mode", &pm);
if (err < 0)
- err = device_property_read_string(dev,
+ err = fwnode_property_read_string(fwnode,
"phy-connection-type", &pm);
if (err < 0)
return err;
@@ -1151,13 +1169,27 @@ int device_get_phy_mode(struct device *dev)
return -ENODEV;
}
+EXPORT_SYMBOL_GPL(fwnode_get_phy_mode);
+
+/**
+ * device_get_phy_mode - Get phy mode for given device
+ * @dev: Pointer to the given device
+ *
+ * The function gets phy interface string from property 'phy-mode' or
+ * 'phy-connection-type', and return its index in phy_modes table, or errno in
+ * error case.
+ */
+int device_get_phy_mode(struct device *dev)
+{
+ return fwnode_get_phy_mode(dev_fwnode(dev));
+}
EXPORT_SYMBOL_GPL(device_get_phy_mode);
-static void *device_get_mac_addr(struct device *dev,
+static void *fwnode_get_mac_addr(struct fwnode_handle *fwnode,
const char *name, char *addr,
int alen)
{
- int ret = device_property_read_u8_array(dev, name, addr, alen);
+ int ret = fwnode_property_read_u8_array(fwnode, name, addr, alen);
if (ret == 0 && alen == ETH_ALEN && is_valid_ether_addr(addr))
return addr;
@@ -1165,8 +1197,8 @@ static void *device_get_mac_addr(struct device *dev,
}
/**
- * device_get_mac_address - Get the MAC for a given device
- * @dev: Pointer to the device
+ * fwnode_get_mac_address - Get the MAC from the firmware node
+ * @fwnode: Pointer to the firmware node
* @addr: Address of buffer to store the MAC in
* @alen: Length of the buffer pointed to by addr, should be ETH_ALEN
*
@@ -1187,23 +1219,60 @@ static void *device_get_mac_addr(struct device *dev,
* In this case, the real MAC is in 'local-mac-address', and 'mac-address'
* exists but is all zeros.
*/
-void *device_get_mac_address(struct device *dev, char *addr, int alen)
+void *fwnode_get_mac_address(struct fwnode_handle *fwnode, char *addr, int alen)
{
char *res;
- res = device_get_mac_addr(dev, "mac-address", addr, alen);
+ res = fwnode_get_mac_addr(fwnode, "mac-address", addr, alen);
if (res)
return res;
- res = device_get_mac_addr(dev, "local-mac-address", addr, alen);
+ res = fwnode_get_mac_addr(fwnode, "local-mac-address", addr, alen);
if (res)
return res;
- return device_get_mac_addr(dev, "address", addr, alen);
+ return fwnode_get_mac_addr(fwnode, "address", addr, alen);
+}
+EXPORT_SYMBOL(fwnode_get_mac_address);
+
+/**
+ * device_get_mac_address - Get the MAC for a given device
+ * @dev: Pointer to the device
+ * @addr: Address of buffer to store the MAC in
+ * @alen: Length of the buffer pointed to by addr, should be ETH_ALEN
+ */
+void *device_get_mac_address(struct device *dev, char *addr, int alen)
+{
+ return fwnode_get_mac_address(dev_fwnode(dev), addr, alen);
}
EXPORT_SYMBOL(device_get_mac_address);
/**
+ * fwnode_irq_get - Get IRQ directly from a fwnode
+ * @fwnode: Pointer to the firmware node
+ * @index: Zero-based index of the IRQ
+ *
+ * Returns Linux IRQ number on success. Other values are determined
+ * accordingly to acpi_/of_ irq_get() operation.
+ */
+int fwnode_irq_get(struct fwnode_handle *fwnode, unsigned int index)
+{
+ struct device_node *of_node = to_of_node(fwnode);
+ struct resource res;
+ int ret;
+
+ if (IS_ENABLED(CONFIG_OF) && of_node)
+ return of_irq_get(of_node, index);
+
+ ret = acpi_irq_get(ACPI_HANDLE_FWNODE(fwnode), index, &res);
+ if (ret)
+ return ret;
+
+ return res.start;
+}
+EXPORT_SYMBOL(fwnode_irq_get);
+
+/**
* device_graph_get_next_endpoint - Get next endpoint firmware node
* @fwnode: Pointer to the parent firmware node
* @prev: Previous endpoint node or %NULL to get the first
@@ -1340,3 +1409,10 @@ int fwnode_graph_parse_endpoint(const struct fwnode_handle *fwnode,
return fwnode_call_int_op(fwnode, graph_parse_endpoint, endpoint);
}
EXPORT_SYMBOL(fwnode_graph_parse_endpoint);
+
+void *device_get_match_data(struct device *dev)
+{
+ return fwnode_call_ptr_op(dev_fwnode(dev), device_get_match_data,
+ dev);
+}
+EXPORT_SYMBOL_GPL(device_get_match_data);
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 3a1535d812d8..aff34c0c2a3e 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -6,7 +6,6 @@
config REGMAP
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
select IRQ_DOMAIN if REGMAP_IRQ
- select REGMAP_HWSPINLOCK if HWSPINLOCK=y
bool
config REGCACHE_COMPRESSED
@@ -21,6 +20,10 @@ config REGMAP_I2C
tristate
depends on I2C
+config REGMAP_SLIMBUS
+ tristate
+ depends on SLIMBUS
+
config REGMAP_SPI
tristate
depends on SPI
@@ -39,5 +42,6 @@ config REGMAP_MMIO
config REGMAP_IRQ
bool
-config REGMAP_HWSPINLOCK
- bool
+config REGMAP_SOUNDWIRE
+ tristate
+ depends on SOUNDWIRE_BUS
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index 0d298c446108..5ed0023fabda 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -8,8 +8,10 @@ obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+obj-$(CONFIG_REGMAP_SLIMBUS) += regmap-slimbus.o
obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
obj-$(CONFIG_REGMAP_W1) += regmap-w1.o
+obj-$(CONFIG_REGMAP_SOUNDWIRE) += regmap-sdw.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 8641183cac2f..53785e0e297a 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -77,6 +77,7 @@ struct regmap {
int async_ret;
#ifdef CONFIG_DEBUG_FS
+ bool debugfs_disable;
struct dentry *debugfs;
const char *debugfs_name;
@@ -215,10 +216,17 @@ struct regmap_field {
extern void regmap_debugfs_initcall(void);
extern void regmap_debugfs_init(struct regmap *map, const char *name);
extern void regmap_debugfs_exit(struct regmap *map);
+
+static inline void regmap_debugfs_disable(struct regmap *map)
+{
+ map->debugfs_disable = true;
+}
+
#else
static inline void regmap_debugfs_initcall(void) { }
static inline void regmap_debugfs_init(struct regmap *map, const char *name) { }
static inline void regmap_debugfs_exit(struct regmap *map) { }
+static inline void regmap_debugfs_disable(struct regmap *map) { }
#endif
/* regcache core declarations */
diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c
index 4d2e50bfc726..bc6cd88b8cc6 100644
--- a/drivers/base/regmap/regcache-flat.c
+++ b/drivers/base/regmap/regcache-flat.c
@@ -37,9 +37,12 @@ static int regcache_flat_init(struct regmap *map)
cache = map->cache;
- for (i = 0; i < map->num_reg_defaults; i++)
- cache[regcache_flat_get_index(map, map->reg_defaults[i].reg)] =
- map->reg_defaults[i].def;
+ for (i = 0; i < map->num_reg_defaults; i++) {
+ unsigned int reg = map->reg_defaults[i].reg;
+ unsigned int index = regcache_flat_get_index(map, reg);
+
+ cache[index] = map->reg_defaults[i].def;
+ }
return 0;
}
@@ -56,8 +59,9 @@ static int regcache_flat_read(struct regmap *map,
unsigned int reg, unsigned int *value)
{
unsigned int *cache = map->cache;
+ unsigned int index = regcache_flat_get_index(map, reg);
- *value = cache[regcache_flat_get_index(map, reg)];
+ *value = cache[index];
return 0;
}
@@ -66,8 +70,9 @@ static int regcache_flat_write(struct regmap *map, unsigned int reg,
unsigned int value)
{
unsigned int *cache = map->cache;
+ unsigned int index = regcache_flat_get_index(map, reg);
- cache[regcache_flat_get_index(map, reg)] = value;
+ cache[index] = value;
return 0;
}
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 36ce3511c733..f3266334063e 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -529,6 +529,18 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
struct regmap_range_node *range_node;
const char *devname = "dummy";
+ /*
+ * Userspace can initiate reads from the hardware over debugfs.
+ * Normally internal regmap structures and buffers are protected with
+ * a mutex or a spinlock, but if the regmap owner decided to disable
+ * all locking mechanisms, this is no longer the case. For safety:
+ * don't create the debugfs entries if locking is disabled.
+ */
+ if (map->debugfs_disable) {
+ dev_dbg(map->dev, "regmap locking disabled - not creating debugfs entries\n");
+ return;
+ }
+
/* If we don't have the debugfs root yet, postpone init */
if (!regmap_debugfs_root) {
struct regmap_debugfs_node *node;
diff --git a/drivers/base/regmap/regmap-sdw.c b/drivers/base/regmap/regmap-sdw.c
new file mode 100644
index 000000000000..50a66382d87d
--- /dev/null
+++ b/drivers/base/regmap/regmap-sdw.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2015-17 Intel Corporation.
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include "internal.h"
+
+static int regmap_sdw_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct device *dev = context;
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+
+ return sdw_write(slave, reg, val);
+}
+
+static int regmap_sdw_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct device *dev = context;
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int read;
+
+ read = sdw_read(slave, reg);
+ if (read < 0)
+ return read;
+
+ *val = read;
+ return 0;
+}
+
+static struct regmap_bus regmap_sdw = {
+ .reg_read = regmap_sdw_read,
+ .reg_write = regmap_sdw_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+static int regmap_sdw_config_check(const struct regmap_config *config)
+{
+ /* All register are 8-bits wide as per MIPI Soundwire 1.0 Spec */
+ if (config->val_bits != 8)
+ return -ENOTSUPP;
+
+ /* Registers are 32 bits wide */
+ if (config->reg_bits != 32)
+ return -ENOTSUPP;
+
+ if (config->pad_bits != 0)
+ return -ENOTSUPP;
+
+ return 0;
+}
+
+struct regmap *__regmap_init_sdw(struct sdw_slave *sdw,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ int ret;
+
+ ret = regmap_sdw_config_check(config);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return __regmap_init(&sdw->dev, &regmap_sdw,
+ &sdw->dev, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_sdw);
+
+struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ int ret;
+
+ ret = regmap_sdw_config_check(config);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return __devm_regmap_init(&sdw->dev, &regmap_sdw,
+ &sdw->dev, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw);
+
+MODULE_DESCRIPTION("Regmap SoundWire Module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c
new file mode 100644
index 000000000000..c90bee81d954
--- /dev/null
+++ b/drivers/base/regmap/regmap-slimbus.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2017, Linaro Ltd.
+
+#include <linux/regmap.h>
+#include <linux/slimbus.h>
+#include <linux/module.h>
+
+#include "internal.h"
+
+static int regmap_slimbus_byte_reg_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct slim_device *sdev = context;
+ int v;
+
+ v = slim_readb(sdev, reg);
+
+ if (v < 0)
+ return v;
+
+ *val = v;
+
+ return 0;
+}
+
+static int regmap_slimbus_byte_reg_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct slim_device *sdev = context;
+
+ return slim_writeb(sdev, reg, val);
+}
+
+static struct regmap_bus regmap_slimbus_bus = {
+ .reg_write = regmap_slimbus_byte_reg_write,
+ .reg_read = regmap_slimbus_byte_reg_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+static const struct regmap_bus *regmap_get_slimbus(struct slim_device *slim,
+ const struct regmap_config *config)
+{
+ if (config->val_bits == 8 && config->reg_bits == 8)
+ return &regmap_slimbus_bus;
+
+ return ERR_PTR(-ENOTSUPP);
+}
+
+struct regmap *__regmap_init_slimbus(struct slim_device *slimbus,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ const struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);
+
+ if (IS_ERR(bus))
+ return ERR_CAST(bus);
+
+ return __regmap_init(&slimbus->dev, bus, &slimbus->dev, config,
+ lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_slimbus);
+
+struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
+ const struct regmap_config *config,
+ struct lock_class_key *lock_key,
+ const char *lock_name)
+{
+ const struct regmap_bus *bus = regmap_get_slimbus(slimbus, config);
+
+ if (IS_ERR(bus))
+ return ERR_CAST(bus);
+
+ return __devm_regmap_init(&slimbus->dev, bus, &slimbus, config,
+ lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 8d516a9bfc01..ee302ccdfbc8 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -414,7 +414,6 @@ static unsigned int regmap_parse_64_native(const void *buf)
}
#endif
-#ifdef REGMAP_HWSPINLOCK
static void regmap_lock_hwlock(void *__map)
{
struct regmap *map = __map;
@@ -457,7 +456,11 @@ static void regmap_unlock_hwlock_irqrestore(void *__map)
hwspin_unlock_irqrestore(map->hwlock, &map->spinlock_flags);
}
-#endif
+
+static void regmap_lock_unlock_none(void *__map)
+{
+
+}
static void regmap_lock_mutex(void *__map)
{
@@ -669,16 +672,26 @@ struct regmap *__regmap_init(struct device *dev,
goto err;
}
- if (config->lock && config->unlock) {
+ if (config->name) {
+ map->name = kstrdup_const(config->name, GFP_KERNEL);
+ if (!map->name) {
+ ret = -ENOMEM;
+ goto err_map;
+ }
+ }
+
+ if (config->disable_locking) {
+ map->lock = map->unlock = regmap_lock_unlock_none;
+ regmap_debugfs_disable(map);
+ } else if (config->lock && config->unlock) {
map->lock = config->lock;
map->unlock = config->unlock;
map->lock_arg = config->lock_arg;
- } else if (config->hwlock_id) {
-#ifdef REGMAP_HWSPINLOCK
+ } else if (config->use_hwlock) {
map->hwlock = hwspin_lock_request_specific(config->hwlock_id);
if (!map->hwlock) {
ret = -ENXIO;
- goto err_map;
+ goto err_name;
}
switch (config->hwlock_mode) {
@@ -697,10 +710,6 @@ struct regmap *__regmap_init(struct device *dev,
}
map->lock_arg = map;
-#else
- ret = -EINVAL;
- goto err_map;
-#endif
} else {
if ((bus && bus->fast_io) ||
config->fast_io) {
@@ -762,14 +771,15 @@ struct regmap *__regmap_init(struct device *dev,
map->volatile_reg = config->volatile_reg;
map->precious_reg = config->precious_reg;
map->cache_type = config->cache_type;
- map->name = config->name;
spin_lock_init(&map->async_lock);
INIT_LIST_HEAD(&map->async_list);
INIT_LIST_HEAD(&map->async_free);
init_waitqueue_head(&map->async_waitq);
- if (config->read_flag_mask || config->write_flag_mask) {
+ if (config->read_flag_mask ||
+ config->write_flag_mask ||
+ config->zero_flag_mask) {
map->read_flag_mask = config->read_flag_mask;
map->write_flag_mask = config->write_flag_mask;
} else if (bus) {
@@ -1116,8 +1126,10 @@ err_range:
regmap_range_exit(map);
kfree(map->work_buf);
err_hwlock:
- if (IS_ENABLED(REGMAP_HWSPINLOCK) && map->hwlock)
+ if (map->hwlock)
hwspin_lock_free(map->hwlock);
+err_name:
+ kfree_const(map->name);
err_map:
kfree(map);
err:
@@ -1305,8 +1317,9 @@ void regmap_exit(struct regmap *map)
kfree(async->work_buf);
kfree(async);
}
- if (IS_ENABLED(REGMAP_HWSPINLOCK) && map->hwlock)
+ if (map->hwlock)
hwspin_lock_free(map->hwlock);
+ kfree_const(map->name);
kfree(map);
}
EXPORT_SYMBOL_GPL(regmap_exit);
@@ -2423,13 +2436,15 @@ static int _regmap_bus_read(void *context, unsigned int reg,
{
int ret;
struct regmap *map = context;
+ void *work_val = map->work_buf + map->format.reg_bytes +
+ map->format.pad_bytes;
if (!map->format.parse_val)
return -EINVAL;
- ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes);
+ ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes);
if (ret == 0)
- *val = map->format.parse_val(map->work_buf);
+ *val = map->format.parse_val(work_val);
return ret;
}
diff --git a/drivers/base/soc.c b/drivers/base/soc.c
index 909dedae4c4e..4e80f48ad5d6 100644
--- a/drivers/base/soc.c
+++ b/drivers/base/soc.c
@@ -1,8 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) ST-Ericsson SA 2011
*
* Author: Lee Jones <lee.jones@linaro.org> for ST-Ericsson.
- * License terms: GNU General Public License (GPL), version 2
*/
#include <linux/sysfs.h>
diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c
index 8d98a329f6ea..6e076f359dcc 100644
--- a/drivers/base/syscore.c
+++ b/drivers/base/syscore.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* syscore.c - Execution of system core operations.
*
* Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
- *
- * This file is released under the GPLv2.
*/
#include <linux/syscore_ops.h>
diff --git a/drivers/base/test/test_async_driver_probe.c b/drivers/base/test/test_async_driver_probe.c
index a3355d66bc12..e7f145d662f0 100644
--- a/drivers/base/test/test_async_driver_probe.c
+++ b/drivers/base/test/test_async_driver_probe.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index d936fcf9f1fb..5fd9f167ecc1 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* driver/base/topology.c - Populate sysfs with cpu topology information
*
@@ -6,22 +7,6 @@
* Copyright (C) 2006, Intel Corp.
*
* All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
- * NON INFRINGEMENT. See the GNU General Public License for more
- * details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
*/
#include <linux/mm.h>
#include <linux/cpu.h>
diff --git a/drivers/base/transport_class.c b/drivers/base/transport_class.c
index f6c453c3816e..5ed86ded6e6b 100644
--- a/drivers/base/transport_class.c
+++ b/drivers/base/transport_class.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* transport_class.c - implementation of generic transport classes
* using attribute_containers
*
* Copyright (c) 2005 - James Bottomley <James.Bottomley@steeleye.com>
*
- * This file is licensed under GPLv2
- *
* The basic idea here is to allow any "device controller" (which
* would most often be a Host Bus Adapter to use the services of one
* or more tranport classes for performing transport specific