mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-07 11:26:02 +09:00
Merge 7ac1161c27 ("Merge tag 'driver-core-5.12-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core") into android-mainline
Steps on the way to 5.12-rc1. Resolves conflicts in: drivers/base/core.c Note, this merge sets the fw_devlink_flags to FW_DEVLINK_FLAGS_ON as it should be in the Android kernel tree. Cc: Saravana Kannan <saravanak@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com> Change-Id: I54f6cade1b3365c4427f2a6ed53b3f9d8170c620
This commit is contained in:
@@ -1434,6 +1434,11 @@
|
||||
to enforce probe and suspend/resume ordering.
|
||||
rpm -- Like "on", but also use to order runtime PM.
|
||||
|
||||
fw_devlink.strict=<bool>
|
||||
[KNL] Treat all inferred dependencies as mandatory
|
||||
dependencies. This only applies for fw_devlink=on|rpm.
|
||||
Format: <bool>
|
||||
|
||||
gamecon.map[2|3]=
|
||||
[HW,JOY] Multisystem joystick and NES/SNES/PSX pad
|
||||
support via parallel port (up to 5 devices per port)
|
||||
|
||||
@@ -161,7 +161,7 @@ config HMEM_REPORTING
|
||||
default n
|
||||
depends on NUMA
|
||||
help
|
||||
Enable reporting for heterogenous memory access attributes under
|
||||
Enable reporting for heterogeneous memory access attributes under
|
||||
their non-uniform memory nodes.
|
||||
|
||||
source "drivers/base/test/Kconfig"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/auxiliary_bus.h>
|
||||
#include "base.h"
|
||||
|
||||
static const struct auxiliary_device_id *auxiliary_match_id(const struct auxiliary_device_id *id,
|
||||
const struct auxiliary_device *auxdev)
|
||||
@@ -260,19 +261,11 @@ void auxiliary_driver_unregister(struct auxiliary_driver *auxdrv)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(auxiliary_driver_unregister);
|
||||
|
||||
static int __init auxiliary_bus_init(void)
|
||||
void __init auxiliary_bus_init(void)
|
||||
{
|
||||
return bus_register(&auxiliary_bus_type);
|
||||
WARN_ON(bus_register(&auxiliary_bus_type));
|
||||
}
|
||||
|
||||
static void __exit auxiliary_bus_exit(void)
|
||||
{
|
||||
bus_unregister(&auxiliary_bus_type);
|
||||
}
|
||||
|
||||
module_init(auxiliary_bus_init);
|
||||
module_exit(auxiliary_bus_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Auxiliary Bus");
|
||||
MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>");
|
||||
|
||||
@@ -119,6 +119,11 @@ static inline int hypervisor_init(void) { return 0; }
|
||||
extern int platform_bus_init(void);
|
||||
extern void cpu_dev_init(void);
|
||||
extern void container_dev_init(void);
|
||||
#ifdef CONFIG_AUXILIARY_BUS
|
||||
extern void auxiliary_bus_init(void);
|
||||
#else
|
||||
static inline void auxiliary_bus_init(void) { }
|
||||
#endif
|
||||
|
||||
struct kobject *virtual_device_parent(struct device *dev);
|
||||
|
||||
|
||||
@@ -633,7 +633,7 @@ int bus_add_driver(struct device_driver *drv)
|
||||
error = driver_add_groups(drv, bus->drv_groups);
|
||||
if (error) {
|
||||
/* How the hell do we get out of this pickle? Give up */
|
||||
printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",
|
||||
printk(KERN_ERR "%s: driver_add_groups(%s) failed\n",
|
||||
__func__, drv->name);
|
||||
}
|
||||
|
||||
@@ -729,23 +729,6 @@ int device_reprobe(struct device *dev)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_reprobe);
|
||||
|
||||
/**
|
||||
* find_bus - locate bus by name.
|
||||
* @name: name of bus.
|
||||
*
|
||||
* Call kset_find_obj() to iterate over list of buses to
|
||||
* find a bus by name. Return bus if found.
|
||||
*
|
||||
* Note that kset_find_obj increments bus' reference count.
|
||||
*/
|
||||
#if 0
|
||||
struct bus_type *find_bus(char *name)
|
||||
{
|
||||
struct kobject *k = kset_find_obj(bus_kset, name);
|
||||
return k ? to_bus(k) : NULL;
|
||||
}
|
||||
#endif /* 0 */
|
||||
|
||||
static int bus_add_groups(struct bus_type *bus,
|
||||
const struct attribute_group **groups)
|
||||
{
|
||||
|
||||
@@ -149,6 +149,21 @@ void fwnode_links_purge(struct fwnode_handle *fwnode)
|
||||
fwnode_links_purge_consumers(fwnode);
|
||||
}
|
||||
|
||||
static void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
|
||||
/* Don't purge consumer links of an added child */
|
||||
if (fwnode->dev)
|
||||
return;
|
||||
|
||||
fwnode->flags |= FWNODE_FLAG_NOT_DEVICE;
|
||||
fwnode_links_purge_consumers(fwnode);
|
||||
|
||||
fwnode_for_each_available_child_node(fwnode, child)
|
||||
fw_devlink_purge_absent_suppliers(child);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SRCU
|
||||
static DEFINE_MUTEX(device_links_lock);
|
||||
DEFINE_STATIC_SRCU(device_links_srcu);
|
||||
@@ -245,7 +260,8 @@ int device_is_dependent(struct device *dev, void *target)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node) {
|
||||
if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
|
||||
if ((link->flags & ~DL_FLAG_INFERRED) ==
|
||||
(DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
|
||||
continue;
|
||||
|
||||
if (link->consumer == target)
|
||||
@@ -318,7 +334,8 @@ static int device_reorder_to_tail(struct device *dev, void *not_used)
|
||||
|
||||
device_for_each_child(dev, NULL, device_reorder_to_tail);
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node) {
|
||||
if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
|
||||
if ((link->flags & ~DL_FLAG_INFERRED) ==
|
||||
(DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
|
||||
continue;
|
||||
device_reorder_to_tail(link->consumer, NULL);
|
||||
}
|
||||
@@ -566,7 +583,8 @@ postcore_initcall(devlink_class_init);
|
||||
#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \
|
||||
DL_FLAG_AUTOREMOVE_SUPPLIER | \
|
||||
DL_FLAG_AUTOPROBE_CONSUMER | \
|
||||
DL_FLAG_SYNC_STATE_ONLY)
|
||||
DL_FLAG_SYNC_STATE_ONLY | \
|
||||
DL_FLAG_INFERRED)
|
||||
|
||||
#define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \
|
||||
DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)
|
||||
@@ -635,7 +653,7 @@ struct device_link *device_link_add(struct device *consumer,
|
||||
if (!consumer || !supplier || flags & ~DL_ADD_VALID_FLAGS ||
|
||||
(flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||
|
||||
(flags & DL_FLAG_SYNC_STATE_ONLY &&
|
||||
flags != DL_FLAG_SYNC_STATE_ONLY) ||
|
||||
(flags & ~DL_FLAG_INFERRED) != DL_FLAG_SYNC_STATE_ONLY) ||
|
||||
(flags & DL_FLAG_AUTOPROBE_CONSUMER &&
|
||||
flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
|
||||
DL_FLAG_AUTOREMOVE_SUPPLIER)))
|
||||
@@ -691,6 +709,10 @@ struct device_link *device_link_add(struct device *consumer,
|
||||
if (link->consumer != consumer)
|
||||
continue;
|
||||
|
||||
if (link->flags & DL_FLAG_INFERRED &&
|
||||
!(flags & DL_FLAG_INFERRED))
|
||||
link->flags &= ~DL_FLAG_INFERRED;
|
||||
|
||||
if (flags & DL_FLAG_PM_RUNTIME) {
|
||||
if (!(link->flags & DL_FLAG_PM_RUNTIME)) {
|
||||
pm_runtime_new_link(consumer);
|
||||
@@ -950,6 +972,10 @@ int device_links_check_suppliers(struct device *dev)
|
||||
mutex_lock(&fwnode_link_lock);
|
||||
if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) &&
|
||||
!fw_devlink_is_permissive()) {
|
||||
dev_dbg(dev, "probe deferral - wait for supplier %pfwP\n",
|
||||
list_first_entry(&dev->fwnode->suppliers,
|
||||
struct fwnode_link,
|
||||
c_hook)->supplier);
|
||||
mutex_unlock(&fwnode_link_lock);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
@@ -964,6 +990,8 @@ int device_links_check_suppliers(struct device *dev)
|
||||
if (link->status != DL_STATE_AVAILABLE &&
|
||||
!(link->flags & DL_FLAG_SYNC_STATE_ONLY)) {
|
||||
device_links_missing_supplier(dev);
|
||||
dev_dbg(dev, "probe deferral - supplier %s not ready\n",
|
||||
dev_name(link->supplier));
|
||||
ret = -EPROBE_DEFER;
|
||||
break;
|
||||
}
|
||||
@@ -1142,12 +1170,22 @@ void device_links_driver_bound(struct device *dev)
|
||||
LIST_HEAD(sync_list);
|
||||
|
||||
/*
|
||||
* If a device probes successfully, it's expected to have created all
|
||||
* If a device binds successfully, it's expected to have created all
|
||||
* the device links it needs to or make new device links as it needs
|
||||
* them. So, it no longer needs to wait on any suppliers.
|
||||
* them. So, fw_devlink no longer needs to create device links to any
|
||||
* of the device's suppliers.
|
||||
*
|
||||
* Also, if a child firmware node of this bound device is not added as
|
||||
* a device by now, assume it is never going to be added and make sure
|
||||
* other devices don't defer probe indefinitely by waiting for such a
|
||||
* child device.
|
||||
*/
|
||||
if (dev->fwnode && dev->fwnode->dev == dev)
|
||||
if (dev->fwnode && dev->fwnode->dev == dev) {
|
||||
struct fwnode_handle *child;
|
||||
fwnode_links_purge_suppliers(dev->fwnode);
|
||||
fwnode_for_each_available_child_node(dev->fwnode, child)
|
||||
fw_devlink_purge_absent_suppliers(child);
|
||||
}
|
||||
device_remove_file(dev, &dev_attr_waiting_for_supplier);
|
||||
|
||||
device_links_write_lock();
|
||||
@@ -1458,7 +1496,14 @@ static void device_links_purge(struct device *dev)
|
||||
device_links_write_unlock();
|
||||
}
|
||||
|
||||
static u32 fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER;
|
||||
#define FW_DEVLINK_FLAGS_PERMISSIVE (DL_FLAG_INFERRED | \
|
||||
DL_FLAG_SYNC_STATE_ONLY)
|
||||
#define FW_DEVLINK_FLAGS_ON (DL_FLAG_INFERRED | \
|
||||
DL_FLAG_AUTOPROBE_CONSUMER)
|
||||
#define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \
|
||||
DL_FLAG_PM_RUNTIME)
|
||||
|
||||
static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
|
||||
static int __init fw_devlink_setup(char *arg)
|
||||
{
|
||||
if (!arg)
|
||||
@@ -1467,17 +1512,23 @@ static int __init fw_devlink_setup(char *arg)
|
||||
if (strcmp(arg, "off") == 0) {
|
||||
fw_devlink_flags = 0;
|
||||
} else if (strcmp(arg, "permissive") == 0) {
|
||||
fw_devlink_flags = DL_FLAG_SYNC_STATE_ONLY;
|
||||
fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
} else if (strcmp(arg, "on") == 0) {
|
||||
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER;
|
||||
fw_devlink_flags = FW_DEVLINK_FLAGS_ON;
|
||||
} else if (strcmp(arg, "rpm") == 0) {
|
||||
fw_devlink_flags = DL_FLAG_AUTOPROBE_CONSUMER |
|
||||
DL_FLAG_PM_RUNTIME;
|
||||
fw_devlink_flags = FW_DEVLINK_FLAGS_RPM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
early_param("fw_devlink", fw_devlink_setup);
|
||||
|
||||
static bool fw_devlink_strict;
|
||||
static int __init fw_devlink_strict_setup(char *arg)
|
||||
{
|
||||
return strtobool(arg, &fw_devlink_strict);
|
||||
}
|
||||
early_param("fw_devlink.strict", fw_devlink_strict_setup);
|
||||
|
||||
u32 fw_devlink_get_flags(void)
|
||||
{
|
||||
return fw_devlink_flags;
|
||||
@@ -1485,7 +1536,12 @@ u32 fw_devlink_get_flags(void)
|
||||
|
||||
static bool fw_devlink_is_permissive(void)
|
||||
{
|
||||
return fw_devlink_flags == DL_FLAG_SYNC_STATE_ONLY;
|
||||
return fw_devlink_flags == FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
}
|
||||
|
||||
bool fw_devlink_is_strict(void)
|
||||
{
|
||||
return fw_devlink_strict && !fw_devlink_is_permissive();
|
||||
}
|
||||
|
||||
static void fw_devlink_parse_fwnode(struct fwnode_handle *fwnode)
|
||||
@@ -1507,6 +1563,53 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode)
|
||||
fw_devlink_parse_fwtree(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links
|
||||
* @con: Device to check dependencies for.
|
||||
* @sup: Device to check against.
|
||||
*
|
||||
* Check if @sup depends on @con or any device dependent on it (its child or
|
||||
* its consumer etc). When such a cyclic dependency is found, convert all
|
||||
* device links created solely by fw_devlink into SYNC_STATE_ONLY device links.
|
||||
* This is the equivalent of doing fw_devlink=permissive just between the
|
||||
* devices in the cycle. We need to do this because, at this point, fw_devlink
|
||||
* can't tell which of these dependencies is not a real dependency.
|
||||
*
|
||||
* Return 1 if a cycle is found. Otherwise, return 0.
|
||||
*/
|
||||
static int fw_devlink_relax_cycle(struct device *con, void *sup)
|
||||
{
|
||||
struct device_link *link;
|
||||
int ret;
|
||||
|
||||
if (con == sup)
|
||||
return 1;
|
||||
|
||||
ret = device_for_each_child(con, sup, fw_devlink_relax_cycle);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
list_for_each_entry(link, &con->links.consumers, s_node) {
|
||||
if ((link->flags & ~DL_FLAG_INFERRED) ==
|
||||
(DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
|
||||
continue;
|
||||
|
||||
if (!fw_devlink_relax_cycle(link->consumer, sup))
|
||||
continue;
|
||||
|
||||
ret = 1;
|
||||
|
||||
if (!(link->flags & DL_FLAG_INFERRED))
|
||||
continue;
|
||||
|
||||
pm_runtime_drop_link(link);
|
||||
link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
dev_dbg(link->consumer, "Relaxing link with %s\n",
|
||||
dev_name(link->supplier));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* fw_devlink_create_devlink - Create a device link from a consumer to fwnode
|
||||
* @con - Consumer device for the device link
|
||||
@@ -1534,16 +1637,40 @@ static int fw_devlink_create_devlink(struct device *con,
|
||||
|
||||
sup_dev = get_dev_from_fwnode(sup_handle);
|
||||
if (sup_dev) {
|
||||
/*
|
||||
* If it's one of those drivers that don't actually bind to
|
||||
* their device using driver core, then don't wait on this
|
||||
* supplier device indefinitely.
|
||||
*/
|
||||
if (sup_dev->links.status == DL_DEV_NO_DRIVER &&
|
||||
sup_handle->flags & FWNODE_FLAG_INITIALIZED) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this fails, it is due to cycles in device links. Just
|
||||
* give up on this link and treat it as invalid.
|
||||
*/
|
||||
if (!device_link_add(con, sup_dev, flags))
|
||||
if (!device_link_add(con, sup_dev, flags) &&
|
||||
!(flags & DL_FLAG_SYNC_STATE_ONLY)) {
|
||||
dev_info(con, "Fixing up cyclic dependency with %s\n",
|
||||
dev_name(sup_dev));
|
||||
device_links_write_lock();
|
||||
fw_devlink_relax_cycle(con, sup_dev);
|
||||
device_links_write_unlock();
|
||||
device_link_add(con, sup_dev,
|
||||
FW_DEVLINK_FLAGS_PERMISSIVE);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Supplier that's already initialized without a struct device. */
|
||||
if (sup_handle->flags & FWNODE_FLAG_INITIALIZED)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* DL_FLAG_SYNC_STATE_ONLY doesn't block probing and supports
|
||||
* cycles. So cycle detection isn't necessary and shouldn't be
|
||||
@@ -1632,7 +1759,7 @@ static void __fw_devlink_link_to_consumers(struct device *dev)
|
||||
con_dev = NULL;
|
||||
} else {
|
||||
own_link = false;
|
||||
dl_flags = DL_FLAG_SYNC_STATE_ONLY;
|
||||
dl_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1687,7 +1814,7 @@ static void __fw_devlink_link_to_suppliers(struct device *dev,
|
||||
if (own_link)
|
||||
dl_flags = fw_devlink_get_flags();
|
||||
else
|
||||
dl_flags = DL_FLAG_SYNC_STATE_ONLY;
|
||||
dl_flags = FW_DEVLINK_FLAGS_PERMISSIVE;
|
||||
|
||||
list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) {
|
||||
int ret;
|
||||
|
||||
@@ -32,6 +32,7 @@ void __init driver_init(void)
|
||||
*/
|
||||
of_core_init();
|
||||
platform_bus_init();
|
||||
auxiliary_bus_init();
|
||||
cpu_dev_init();
|
||||
memory_dev_init();
|
||||
container_dev_init();
|
||||
|
||||
@@ -1463,13 +1463,16 @@ static int platform_remove(struct device *_dev)
|
||||
{
|
||||
struct platform_driver *drv = to_platform_driver(_dev->driver);
|
||||
struct platform_device *dev = to_platform_device(_dev);
|
||||
int ret = 0;
|
||||
|
||||
if (drv->remove)
|
||||
ret = drv->remove(dev);
|
||||
if (drv->remove) {
|
||||
int ret = drv->remove(dev);
|
||||
|
||||
if (ret)
|
||||
dev_warn(_dev, "remove callback returned a non-zero value. This will be ignored.\n");
|
||||
}
|
||||
dev_pm_domain_detach(_dev, true);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void platform_shutdown(struct device *_dev)
|
||||
|
||||
@@ -2196,6 +2196,7 @@ static int genpd_add_provider(struct device_node *np, genpd_xlate_t xlate,
|
||||
cp->node = of_node_get(np);
|
||||
cp->data = data;
|
||||
cp->xlate = xlate;
|
||||
fwnode_dev_initialized(&np->fwnode, true);
|
||||
|
||||
mutex_lock(&of_genpd_mutex);
|
||||
list_add(&cp->link, &of_genpd_providers);
|
||||
@@ -2385,6 +2386,7 @@ void of_genpd_del_provider(struct device_node *np)
|
||||
}
|
||||
}
|
||||
|
||||
fwnode_dev_initialized(&cp->node->fwnode, false);
|
||||
list_del(&cp->link);
|
||||
of_node_put(cp->node);
|
||||
kfree(cp);
|
||||
|
||||
@@ -2,3 +2,4 @@
|
||||
obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o
|
||||
|
||||
obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o
|
||||
CFLAGS_REMOVE_property-entry-test.o += -fplugin-arg-structleak_plugin-byref -fplugin-arg-structleak_plugin-byref-all
|
||||
|
||||
@@ -4659,6 +4659,8 @@ int of_clk_add_provider(struct device_node *np,
|
||||
if (ret < 0)
|
||||
of_clk_del_provider(np);
|
||||
|
||||
fwnode_dev_initialized(&np->fwnode, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_clk_add_provider);
|
||||
@@ -4776,6 +4778,7 @@ void of_clk_del_provider(struct device_node *np)
|
||||
list_for_each_entry(cp, &of_clk_providers, link) {
|
||||
if (cp->node == np) {
|
||||
list_del(&cp->link);
|
||||
fwnode_dev_initialized(&np->fwnode, false);
|
||||
of_node_put(cp->node);
|
||||
kfree(cp);
|
||||
break;
|
||||
|
||||
@@ -1039,3 +1039,14 @@ void of_gpiochip_remove(struct gpio_chip *chip)
|
||||
{
|
||||
of_node_put(chip->of_node);
|
||||
}
|
||||
|
||||
void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev)
|
||||
{
|
||||
/* If the gpiochip has an assigned OF node this takes precedence */
|
||||
if (gc->of_node)
|
||||
gdev->dev.of_node = gc->of_node;
|
||||
else
|
||||
gc->of_node = gdev->dev.of_node;
|
||||
if (gdev->dev.of_node)
|
||||
gdev->dev.fwnode = of_fwnode_handle(gdev->dev.of_node);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ int of_gpiochip_add(struct gpio_chip *gc);
|
||||
void of_gpiochip_remove(struct gpio_chip *gc);
|
||||
int of_gpio_get_count(struct device *dev, const char *con_id);
|
||||
bool of_gpio_need_valid_mask(const struct gpio_chip *gc);
|
||||
void of_gpio_dev_init(struct gpio_chip *gc, struct gpio_device *gdev);
|
||||
#else
|
||||
static inline struct gpio_desc *of_find_gpio(struct device *dev,
|
||||
const char *con_id,
|
||||
@@ -33,6 +34,10 @@ static inline bool of_gpio_need_valid_mask(const struct gpio_chip *gc)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline void of_gpio_dev_init(struct gpio_chip *gc,
|
||||
struct gpio_device *gdev)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_OF_GPIO */
|
||||
|
||||
extern struct notifier_block gpio_of_notifier;
|
||||
|
||||
@@ -56,8 +56,10 @@
|
||||
static DEFINE_IDA(gpio_ida);
|
||||
static dev_t gpio_devt;
|
||||
#define GPIO_DEV_MAX 256 /* 256 GPIO chip devices supported */
|
||||
static int gpio_bus_match(struct device *dev, struct device_driver *drv);
|
||||
static struct bus_type gpio_bus_type = {
|
||||
.name = "gpio",
|
||||
.match = gpio_bus_match,
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -590,13 +592,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
|
||||
gdev->dev.of_node = gc->parent->of_node;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF_GPIO
|
||||
/* If the gpiochip has an assigned OF node this takes precedence */
|
||||
if (gc->of_node)
|
||||
gdev->dev.of_node = gc->of_node;
|
||||
else
|
||||
gc->of_node = gdev->dev.of_node;
|
||||
#endif
|
||||
of_gpio_dev_init(gc, gdev);
|
||||
|
||||
gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
|
||||
if (gdev->id < 0) {
|
||||
@@ -4215,6 +4211,41 @@ void gpiod_put_array(struct gpio_descs *descs)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(gpiod_put_array);
|
||||
|
||||
|
||||
static int gpio_bus_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
/*
|
||||
* Only match if the fwnode doesn't already have a proper struct device
|
||||
* created for it.
|
||||
*/
|
||||
if (dev->fwnode && dev->fwnode->dev != dev)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int gpio_stub_drv_probe(struct device *dev)
|
||||
{
|
||||
/*
|
||||
* The DT node of some GPIO chips have a "compatible" property, but
|
||||
* never have a struct device added and probed by a driver to register
|
||||
* the GPIO chip with gpiolib. In such cases, fw_devlink=on will cause
|
||||
* the consumers of the GPIO chip to get probe deferred forever because
|
||||
* they will be waiting for a device associated with the GPIO chip
|
||||
* firmware node to get added and bound to a driver.
|
||||
*
|
||||
* To allow these consumers to probe, we associate the struct
|
||||
* gpio_device of the GPIO chip with the firmware node and then simply
|
||||
* bind it to this stub driver.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct device_driver gpio_stub_drv = {
|
||||
.name = "gpio_stub_drv",
|
||||
.bus = &gpio_bus_type,
|
||||
.probe = gpio_stub_drv_probe,
|
||||
};
|
||||
|
||||
static int __init gpiolib_dev_init(void)
|
||||
{
|
||||
int ret;
|
||||
@@ -4226,9 +4257,16 @@ static int __init gpiolib_dev_init(void)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (driver_register(&gpio_stub_drv) < 0) {
|
||||
pr_err("gpiolib: could not register GPIO stub driver\n");
|
||||
bus_unregister(&gpio_bus_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, GPIOCHIP_NAME);
|
||||
if (ret < 0) {
|
||||
pr_err("gpiolib: failed to allocate char dev region\n");
|
||||
driver_unregister(&gpio_stub_drv);
|
||||
bus_unregister(&gpio_bus_type);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
@@ -1102,7 +1103,9 @@ static int of_link_to_phandle(struct device_node *con_np,
|
||||
* created for them.
|
||||
*/
|
||||
sup_dev = get_dev_from_fwnode(&sup_np->fwnode);
|
||||
if (!sup_dev && of_node_check_flag(sup_np, OF_POPULATED)) {
|
||||
if (!sup_dev &&
|
||||
(of_node_check_flag(sup_np, OF_POPULATED) ||
|
||||
sup_np->fwnode.flags & FWNODE_FLAG_NOT_DEVICE)) {
|
||||
pr_debug("Not linking %pOFP to %pOFP - No struct device\n",
|
||||
con_np, sup_np);
|
||||
of_node_put(sup_np);
|
||||
@@ -1232,6 +1235,7 @@ static struct device_node *parse_##fname(struct device_node *np, \
|
||||
struct supplier_bindings {
|
||||
struct device_node *(*parse_prop)(struct device_node *np,
|
||||
const char *prop_name, int index);
|
||||
bool optional;
|
||||
};
|
||||
|
||||
DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
|
||||
@@ -1244,8 +1248,6 @@ DEFINE_SIMPLE_PROP(dmas, "dmas", "#dma-cells")
|
||||
DEFINE_SIMPLE_PROP(power_domains, "power-domains", "#power-domain-cells")
|
||||
DEFINE_SIMPLE_PROP(hwlocks, "hwlocks", "#hwlock-cells")
|
||||
DEFINE_SIMPLE_PROP(extcon, "extcon", NULL)
|
||||
DEFINE_SIMPLE_PROP(interrupts_extended, "interrupts-extended",
|
||||
"#interrupt-cells")
|
||||
DEFINE_SIMPLE_PROP(nvmem_cells, "nvmem-cells", NULL)
|
||||
DEFINE_SIMPLE_PROP(phys, "phys", "#phy-cells")
|
||||
DEFINE_SIMPLE_PROP(wakeup_parent, "wakeup-parent", NULL)
|
||||
@@ -1271,19 +1273,55 @@ static struct device_node *parse_iommu_maps(struct device_node *np,
|
||||
return of_parse_phandle(np, prop_name, (index * 4) + 1);
|
||||
}
|
||||
|
||||
static struct device_node *parse_gpio_compat(struct device_node *np,
|
||||
const char *prop_name, int index)
|
||||
{
|
||||
struct of_phandle_args sup_args;
|
||||
|
||||
if (strcmp(prop_name, "gpio") && strcmp(prop_name, "gpios"))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Ignore node with gpio-hog property since its gpios are all provided
|
||||
* by its parent.
|
||||
*/
|
||||
if (of_find_property(np, "gpio-hog", NULL))
|
||||
return NULL;
|
||||
|
||||
if (of_parse_phandle_with_args(np, prop_name, "#gpio-cells", index,
|
||||
&sup_args))
|
||||
return NULL;
|
||||
|
||||
return sup_args.np;
|
||||
}
|
||||
|
||||
static struct device_node *parse_interrupts(struct device_node *np,
|
||||
const char *prop_name, int index)
|
||||
{
|
||||
struct of_phandle_args sup_args;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_OF_IRQ) || IS_ENABLED(CONFIG_PPC))
|
||||
return NULL;
|
||||
|
||||
if (strcmp(prop_name, "interrupts") &&
|
||||
strcmp(prop_name, "interrupts-extended"))
|
||||
return NULL;
|
||||
|
||||
return of_irq_parse_one(np, index, &sup_args) ? NULL : sup_args.np;
|
||||
}
|
||||
|
||||
static const struct supplier_bindings of_supplier_bindings[] = {
|
||||
{ .parse_prop = parse_clocks, },
|
||||
{ .parse_prop = parse_interconnects, },
|
||||
{ .parse_prop = parse_iommus, },
|
||||
{ .parse_prop = parse_iommu_maps, },
|
||||
{ .parse_prop = parse_iommus, .optional = true, },
|
||||
{ .parse_prop = parse_iommu_maps, .optional = true, },
|
||||
{ .parse_prop = parse_mboxes, },
|
||||
{ .parse_prop = parse_io_channels, },
|
||||
{ .parse_prop = parse_interrupt_parent, },
|
||||
{ .parse_prop = parse_dmas, },
|
||||
{ .parse_prop = parse_dmas, .optional = true, },
|
||||
{ .parse_prop = parse_power_domains, },
|
||||
{ .parse_prop = parse_hwlocks, },
|
||||
{ .parse_prop = parse_extcon, },
|
||||
{ .parse_prop = parse_interrupts_extended, },
|
||||
{ .parse_prop = parse_nvmem_cells, },
|
||||
{ .parse_prop = parse_phys, },
|
||||
{ .parse_prop = parse_wakeup_parent, },
|
||||
@@ -1296,6 +1334,8 @@ static const struct supplier_bindings of_supplier_bindings[] = {
|
||||
{ .parse_prop = parse_pinctrl6, },
|
||||
{ .parse_prop = parse_pinctrl7, },
|
||||
{ .parse_prop = parse_pinctrl8, },
|
||||
{ .parse_prop = parse_gpio_compat, },
|
||||
{ .parse_prop = parse_interrupts, },
|
||||
{ .parse_prop = parse_regulators, },
|
||||
{ .parse_prop = parse_gpio, },
|
||||
{ .parse_prop = parse_gpios, },
|
||||
@@ -1332,6 +1372,11 @@ static int of_link_property(struct device_node *con_np, const char *prop_name)
|
||||
|
||||
/* Do not stop at first failed link, link all available suppliers. */
|
||||
while (!matched && s->parse_prop) {
|
||||
if (s->optional && !fw_devlink_is_strict()) {
|
||||
s++;
|
||||
continue;
|
||||
}
|
||||
|
||||
while ((phandle = s->parse_prop(con_np, prop_name, i))) {
|
||||
matched = true;
|
||||
i++;
|
||||
|
||||
@@ -298,7 +298,7 @@ struct dentry *debugfs_lookup(const char *name, struct dentry *parent)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
|
||||
if (IS_ERR(parent))
|
||||
if (!debugfs_initialized() || IS_ERR_OR_NULL(name) || IS_ERR(parent))
|
||||
return NULL;
|
||||
|
||||
if (!parent)
|
||||
@@ -319,6 +319,9 @@ static struct dentry *start_creating(const char *name, struct dentry *parent)
|
||||
if (!(debugfs_allow & DEBUGFS_ALLOW_API))
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
if (!debugfs_initialized())
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
pr_debug("creating file '%s'\n", name);
|
||||
|
||||
if (IS_ERR(parent))
|
||||
|
||||
@@ -323,6 +323,7 @@ enum device_link_state {
|
||||
* AUTOPROBE_CONSUMER: Probe consumer driver automatically after supplier binds.
|
||||
* MANAGED: The core tracks presence of supplier/consumer drivers (internal).
|
||||
* SYNC_STATE_ONLY: Link only affects sync_state() behavior.
|
||||
* INFERRED: Inferred from data (eg: firmware) and not from driver actions.
|
||||
*/
|
||||
#define DL_FLAG_STATELESS BIT(0)
|
||||
#define DL_FLAG_AUTOREMOVE_CONSUMER BIT(1)
|
||||
@@ -332,6 +333,7 @@ enum device_link_state {
|
||||
#define DL_FLAG_AUTOPROBE_CONSUMER BIT(5)
|
||||
#define DL_FLAG_MANAGED BIT(6)
|
||||
#define DL_FLAG_SYNC_STATE_ONLY BIT(7)
|
||||
#define DL_FLAG_INFERRED BIT(8)
|
||||
|
||||
/**
|
||||
* enum dl_dev_state - Device driver presence tracking information.
|
||||
|
||||
@@ -75,7 +75,7 @@ enum probe_type {
|
||||
* @resume: Called to bring a device from sleep mode.
|
||||
* @groups: Default attributes that get created by the driver core
|
||||
* automatically.
|
||||
* @dev_groups: Additional attributes attached to device instance once the
|
||||
* @dev_groups: Additional attributes attached to device instance once
|
||||
* it is bound to the driver.
|
||||
* @pm: Power management operations of the device which matched
|
||||
* this driver.
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
struct fwnode_operations;
|
||||
struct device;
|
||||
@@ -18,9 +19,13 @@ struct device;
|
||||
/*
|
||||
* fwnode link flags
|
||||
*
|
||||
* LINKS_ADDED: The fwnode has already be parsed to add fwnode links.
|
||||
* LINKS_ADDED: The fwnode has already be parsed to add fwnode links.
|
||||
* NOT_DEVICE: The fwnode will never be populated as a struct device.
|
||||
* INITIALIZED: The hardware corresponding to fwnode has been initialized.
|
||||
*/
|
||||
#define FWNODE_FLAG_LINKS_ADDED BIT(0)
|
||||
#define FWNODE_FLAG_NOT_DEVICE BIT(1)
|
||||
#define FWNODE_FLAG_INITIALIZED BIT(2)
|
||||
|
||||
struct fwnode_handle {
|
||||
struct fwnode_handle *secondary;
|
||||
@@ -166,7 +171,20 @@ static inline void fwnode_init(struct fwnode_handle *fwnode,
|
||||
INIT_LIST_HEAD(&fwnode->suppliers);
|
||||
}
|
||||
|
||||
static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode,
|
||||
bool initialized)
|
||||
{
|
||||
if (IS_ERR_OR_NULL(fwnode))
|
||||
return;
|
||||
|
||||
if (initialized)
|
||||
fwnode->flags |= FWNODE_FLAG_INITIALIZED;
|
||||
else
|
||||
fwnode->flags &= ~FWNODE_FLAG_INITIALIZED;
|
||||
}
|
||||
|
||||
extern u32 fw_devlink_get_flags(void);
|
||||
extern bool fw_devlink_is_strict(void);
|
||||
int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup);
|
||||
void fwnode_links_purge(struct fwnode_handle *fwnode);
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@ static inline int of_irq_parse_oldworld(struct device_node *device, int index,
|
||||
#endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */
|
||||
|
||||
extern int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq);
|
||||
extern int of_irq_parse_one(struct device_node *device, int index,
|
||||
struct of_phandle_args *out_irq);
|
||||
extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data);
|
||||
extern int of_irq_to_resource(struct device_node *dev, int index,
|
||||
struct resource *r);
|
||||
@@ -42,6 +40,8 @@ extern int of_irq_to_resource(struct device_node *dev, int index,
|
||||
extern void of_irq_init(const struct of_device_id *matches);
|
||||
|
||||
#ifdef CONFIG_OF_IRQ
|
||||
extern int of_irq_parse_one(struct device_node *device, int index,
|
||||
struct of_phandle_args *out_irq);
|
||||
extern int of_irq_count(struct device_node *dev);
|
||||
extern int of_irq_get(struct device_node *dev, int index);
|
||||
extern int of_irq_get_byname(struct device_node *dev, const char *name);
|
||||
@@ -57,6 +57,11 @@ extern struct irq_domain *of_msi_map_get_device_domain(struct device *dev,
|
||||
extern void of_msi_configure(struct device *dev, struct device_node *np);
|
||||
u32 of_msi_map_id(struct device *dev, struct device_node *msi_np, u32 id_in);
|
||||
#else
|
||||
static inline int of_irq_parse_one(struct device_node *device, int index,
|
||||
struct of_phandle_args *out_irq)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int of_irq_count(struct device_node *dev)
|
||||
{
|
||||
return 0;
|
||||
|
||||
@@ -205,6 +205,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
|
||||
}
|
||||
|
||||
fwnode_handle_get(fwnode);
|
||||
fwnode_dev_initialized(fwnode, true);
|
||||
|
||||
/* Fill structure */
|
||||
INIT_RADIX_TREE(&domain->revmap_tree, GFP_KERNEL);
|
||||
@@ -253,6 +254,7 @@ void irq_domain_remove(struct irq_domain *domain)
|
||||
|
||||
pr_debug("Removed domain %s\n", domain->name);
|
||||
|
||||
fwnode_dev_initialized(domain->fwnode, false);
|
||||
fwnode_handle_put(domain->fwnode);
|
||||
if (domain->flags & IRQ_DOMAIN_NAME_ALLOCATED)
|
||||
kfree(domain->name);
|
||||
|
||||
Reference in New Issue
Block a user