From 372a67c0c5ef63f55bd1eb480d9555328d8ec0f2 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:20 -0700 Subject: [PATCH 01/71] driver core: Add fwnode_to_dev() to look up device from fwnode It's often useful to look up a device that corresponds to a fwnode. So add an API to do that irrespective of the bus on which the device has been added to. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 7 +++++++ include/linux/fwnode.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index 2db62d98e3950..510fabf8918cc 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2198,6 +2198,10 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); + + if (dev->fwnode && !dev->fwnode->dev) + dev->fwnode->dev = dev; + bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, @@ -2342,6 +2346,9 @@ void device_del(struct device *dev) kill_device(dev); device_unlock(dev); + if (dev->fwnode && dev->fwnode->dev == dev) + dev->fwnode->dev = NULL; + /* Notify clients of device removal. This call must come * before dpm_sysfs_remove(). */ diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index ababd6bc82f33..d8c6d231d5775 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -17,6 +17,7 @@ struct device; struct fwnode_handle { struct fwnode_handle *secondary; const struct fwnode_operations *ops; + struct device *dev; }; /** @@ -123,5 +124,6 @@ struct fwnode_operations { if (fwnode_has_op(fwnode, op)) \ (fwnode)->ops->op(fwnode, ## __VA_ARGS__); \ } while (false) +#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) #endif From e2ae9bcc4aaacda04edb75c4eea93384719efaa5 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:21 -0700 Subject: [PATCH 02/71] driver core: Add support for linking devices during device addition The firmware corresponding to a device (dev.fwnode) might be able to provide functional dependency information between a device and its supplier and consumer devices. Tracking this functional dependency allows optimizing device probe order and informing a supplier when all its consumers have probed (and thereby actively managing their resources). The existing device links feature allows tracking and using supplier-consumer relationships. So, this patch adds the add_links() fwnode callback to allow firmware to create device links for each device as the device is added. However, when consumer devices are added, they might not have a supplier device to link to despite needing mandatory resources/functionality from one or more suppliers. A waiting_for_suppliers list is created to track such consumers and retry linking them when new devices get added. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/device_link.rst | 3 +- drivers/base/core.c | 88 ++++++++++++++++++++++++ include/linux/device.h | 2 + include/linux/fwnode.h | 17 +++++ 4 files changed, 109 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/device_link.rst b/Documentation/driver-api/device_link.rst index 1b5020ec6517b..bc2d89af88ce2 100644 --- a/Documentation/driver-api/device_link.rst +++ b/Documentation/driver-api/device_link.rst @@ -281,7 +281,8 @@ State machine :c:func:`driver_bound()`.) * Before a consumer device is probed, presence of supplier drivers is - verified by checking that links to suppliers are in ``DL_STATE_AVAILABLE`` + verified by checking the consumer device is not in the wait_for_suppliers + list and by checking that links to suppliers are in ``DL_STATE_AVAILABLE`` state. The state of the links is updated to ``DL_STATE_CONSUMER_PROBE``. (Call to :c:func:`device_links_check_suppliers()` from :c:func:`really_probe()`.) diff --git a/drivers/base/core.c b/drivers/base/core.c index 510fabf8918cc..b3896da73b3de 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -44,6 +44,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); #endif /* Device links support. */ +static LIST_HEAD(wait_for_suppliers); +static DEFINE_MUTEX(wfs_lock); #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -430,6 +432,58 @@ struct device_link *device_link_add(struct device *consumer, } EXPORT_SYMBOL_GPL(device_link_add); +/** + * device_link_wait_for_supplier - Add device to wait_for_suppliers list + * @consumer: Consumer device + * + * Marks the @consumer device as waiting for suppliers to become available by + * adding it to the wait_for_suppliers list. The consumer device will never be + * probed until it's removed from the wait_for_suppliers list. + * + * The caller is responsible for adding the links to the supplier devices once + * they are available and removing the @consumer device from the + * wait_for_suppliers list once links to all the suppliers have been created. + * + * This function is NOT meant to be called from the probe function of the + * consumer but rather from code that creates/adds the consumer device. + */ +static void device_link_wait_for_supplier(struct device *consumer) +{ + mutex_lock(&wfs_lock); + list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); + mutex_unlock(&wfs_lock); +} + +/** + * device_link_add_missing_supplier_links - Add links from consumer devices to + * supplier devices, leaving any + * consumer with inactive suppliers on + * the wait_for_suppliers list + * + * Loops through all consumers waiting on suppliers and tries to add all their + * supplier links. If that succeeds, the consumer device is removed from + * wait_for_suppliers list. Otherwise, they are left in the wait_for_suppliers + * list. Devices left on the wait_for_suppliers list will not be probed. + * + * The fwnode add_links callback is expected to return 0 if it has found and + * added all the supplier links for the consumer device. It should return an + * error if it isn't able to do so. + * + * The caller of device_link_wait_for_supplier() is expected to call this once + * it's aware of potential suppliers becoming available. + */ +static void device_link_add_missing_supplier_links(void) +{ + struct device *dev, *tmp; + + mutex_lock(&wfs_lock); + list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, + links.needs_suppliers) + if (!fwnode_call_int_op(dev->fwnode, add_links, dev)) + list_del_init(&dev->links.needs_suppliers); + mutex_unlock(&wfs_lock); +} + static void device_link_free(struct device_link *link) { while (refcount_dec_not_one(&link->rpm_active)) @@ -564,6 +618,17 @@ int device_links_check_suppliers(struct device *dev) struct device_link *link; int ret = 0; + /* + * Device waiting for supplier to become available is not allowed to + * probe. + */ + mutex_lock(&wfs_lock); + if (!list_empty(&dev->links.needs_suppliers)) { + mutex_unlock(&wfs_lock); + return -EPROBE_DEFER; + } + mutex_unlock(&wfs_lock); + device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { @@ -848,6 +913,10 @@ static void device_links_purge(struct device *dev) { struct device_link *link, *ln; + mutex_lock(&wfs_lock); + list_del(&dev->links.needs_suppliers); + mutex_unlock(&wfs_lock); + /* * Delete all of the remaining links from this device to any other * devices (either consumers or suppliers). @@ -1712,6 +1781,7 @@ void device_initialize(struct device *dev) #endif INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); + INIT_LIST_HEAD(&dev->links.needs_suppliers); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); @@ -2202,6 +2272,24 @@ int device_add(struct device *dev) if (dev->fwnode && !dev->fwnode->dev) dev->fwnode->dev = dev; + /* + * Check if any of the other devices (consumers) have been waiting for + * this device (supplier) to be added so that they can create a device + * link to it. + * + * This needs to happen after device_pm_add() because device_link_add() + * requires the supplier be registered before it's called. + * + * But this also needs to happe before bus_probe_device() to make sure + * waiting consumers can link to it before the driver is bound to the + * device and the driver sync_state callback is called for this device. + */ + device_link_add_missing_supplier_links(); + + if (fwnode_has_op(dev->fwnode, add_links) + && fwnode_call_int_op(dev->fwnode, add_links, dev)) + device_link_wait_for_supplier(dev); + bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, diff --git a/include/linux/device.h b/include/linux/device.h index 297239a08bb77..c6fb5b3431b7d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1135,11 +1135,13 @@ enum dl_dev_state { * struct dev_links_info - Device data related to device links. * @suppliers: List of links to supplier devices. * @consumers: List of links to consumer devices. + * @needs_suppliers: Hook to global list of devices waiting for suppliers. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; + struct list_head needs_suppliers; enum dl_dev_state status; }; diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index d8c6d231d5775..6ae05b9ce3597 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -66,6 +66,21 @@ struct fwnode_reference_args { * endpoint node. * @graph_get_port_parent: Return the parent node of a port node. * @graph_parse_endpoint: Parse endpoint for port and endpoint id. + * @add_links: Called after the device corresponding to the fwnode is added + * using device_add(). The function is expected to create device + * links to all the suppliers of the device that are available at + * the time this function is called. The function must NOT stop + * at the first failed device link if other unlinked supplier + * devices are present in the system. If some suppliers are not + * yet available, this function will be called again when other + * devices are added to allow creating device links to any newly + * available suppliers. + * + * Return 0 if device links have been successfully created to all + * the suppliers of this device or if the supplier information is + * not known. Return an error if and only if the supplier + * information is known but some of the suppliers are not yet + * available to create device links to. */ struct fwnode_operations { struct fwnode_handle *(*get)(struct fwnode_handle *fwnode); @@ -103,6 +118,8 @@ struct fwnode_operations { (*graph_get_port_parent)(struct fwnode_handle *fwnode); int (*graph_parse_endpoint)(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint); + int (*add_links)(const struct fwnode_handle *fwnode, + struct device *dev); }; #define fwnode_has_op(fwnode, op) \ From a3e1d1a7f5fcccaf1d252278425fea9a4a553100 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:22 -0700 Subject: [PATCH 03/71] of: property: Add functional dependency link from DT bindings Add device links after the devices are created (but before they are probed) by looking at common DT bindings like clocks and interconnects. Automatically adding device links for functional dependencies at the framework level provides the following benefits: - Optimizes device probe order and avoids the useless work of attempting probes of devices that will not probe successfully (because their suppliers aren't present or haven't probed yet). For example, in a commonly available mobile SoC, registering just one consumer device's driver at an initcall level earlier than the supplier device's driver causes 11 failed probe attempts before the consumer device probes successfully. This was with a kernel with all the drivers statically compiled in. This problem gets a lot worse if all the drivers are loaded as modules without direct symbol dependencies. - Supplier devices like clock providers, interconnect providers, etc need to keep the resources they provide active and at a particular state(s) during boot up even if their current set of consumers don't request the resource to be active. This is because the rest of the consumers might not have probed yet and turning off the resource before all the consumers have probed could lead to a hang or undesired user experience. Some frameworks (Eg: regulator) handle this today by turning off "unused" resources at late_initcall_sync and hoping all the devices have probed by then. This is not a valid assumption for systems with loadable modules. Other frameworks (Eg: clock) just don't handle this due to the lack of a clear signal for when they can turn off resources. This leads to downstream hacks to handle cases like this that can easily be solved in the upstream kernel. By linking devices before they are probed, we give suppliers a clear count of the number of dependent consumers. Once all of the consumers are active, the suppliers can turn off the unused resources without making assumptions about the number of consumers. By default we just add device-links to track "driver presence" (probe succeeded) of the supplier device. If any other functionality provided by device-links are needed, it is left to the consumer/supplier devices to change the link when they probe. kbuild test robot reported clang error about missing const Reported-by: kbuild test robot Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-4-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- .../admin-guide/kernel-parameters.rst | 1 + .../admin-guide/kernel-parameters.txt | 6 + drivers/of/property.c | 241 ++++++++++++++++++ 3 files changed, 248 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst index d05d531b4ec9f..6d421694d98e9 100644 --- a/Documentation/admin-guide/kernel-parameters.rst +++ b/Documentation/admin-guide/kernel-parameters.rst @@ -127,6 +127,7 @@ parameter is applicable:: NET Appropriate network support is enabled. NUMA NUMA support is enabled. NFS Appropriate NFS support is enabled. + OF Devicetree is enabled. OSS OSS sound support is enabled. PV_OPS A paravirtualized kernel is enabled. PARIDE The ParIDE (parallel port IDE) subsystem is enabled. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index c7ac2f3ac99fd..02df1a24f0c77 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3194,6 +3194,12 @@ This can be set from sysctl after boot. See Documentation/admin-guide/sysctl/vm.rst for details. + of_devlink [OF, KNL] Create device links between consumer and + supplier devices by scanning the devictree to infer the + consumer/supplier relationships. A consumer device + will not be probed until all the supplier devices have + probed successfully. + ohci1394_dma=early [HW] enable debugging via the ohci1394 driver. See Documentation/debugging-via-ohci1394.txt for more info. diff --git a/drivers/of/property.c b/drivers/of/property.c index d7fa75e31f224..23b5ee5b05708 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "of_private.h" @@ -985,6 +986,245 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, return of_device_get_match_data(dev); } +static bool of_is_ancestor_of(struct device_node *test_ancestor, + struct device_node *child) +{ + of_node_get(child); + while (child) { + if (child == test_ancestor) { + of_node_put(child); + return false; + } + child = of_get_next_parent(child); + } + return true; +} + +/** + * of_link_to_phandle - Add device link to supplier from supplier phandle + * @dev: consumer device + * @sup_np: phandle to supplier device tree node + * + * Given a phandle to a supplier device tree node (@sup_np), this function + * finds the device that owns the supplier device tree node and creates a + * device link from @dev consumer device to the supplier device. This function + * doesn't create device links for invalid scenarios such as trying to create a + * link with a parent device as the consumer of its child device. In such + * cases, it returns an error. + * + * Returns: + * - 0 if link successfully created to supplier + * - -EAGAIN if linking to the supplier should be reattempted + * - -EINVAL if the supplier link is invalid and should not be created + * - -ENODEV if there is no device that corresponds to the supplier phandle + */ +static int of_link_to_phandle(struct device *dev, struct device_node *sup_np) +{ + struct device *sup_dev; + u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER; + int ret = 0; + struct device_node *tmp_np = sup_np; + + of_node_get(sup_np); + /* + * Find the device node that contains the supplier phandle. It may be + * @sup_np or it may be an ancestor of @sup_np. + */ + while (sup_np && !of_find_property(sup_np, "compatible", NULL)) + sup_np = of_get_next_parent(sup_np); + if (!sup_np) { + dev_dbg(dev, "Not linking to %pOFP - No device\n", tmp_np); + return -ENODEV; + } + + /* + * Don't allow linking a device node as a consumer of one of its + * descendant nodes. By definition, a child node can't be a functional + * dependency for the parent node. + */ + if (!of_is_ancestor_of(dev->of_node, sup_np)) { + dev_dbg(dev, "Not linking to %pOFP - is descendant\n", sup_np); + of_node_put(sup_np); + return -EINVAL; + } + sup_dev = get_dev_from_fwnode(&sup_np->fwnode); + of_node_put(sup_np); + if (!sup_dev) + return -EAGAIN; + if (!device_link_add(dev, sup_dev, dl_flags)) + ret = -EAGAIN; + put_device(sup_dev); + return ret; +} + +/** + * parse_prop_cells - Property parsing function for suppliers + * + * @np: Pointer to device tree node containing a list + * @prop_name: Name of property to be parsed. Expected to hold phandle values + * @index: For properties holding a list of phandles, this is the index + * into the list. + * @list_name: Property name that is known to contain list of phandle(s) to + * supplier(s) + * @cells_name: property name that specifies phandles' arguments count + * + * This is a helper function to parse properties that have a known fixed name + * and are a list of phandles and phandle arguments. + * + * Returns: + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +static struct device_node *parse_prop_cells(struct device_node *np, + const char *prop_name, int index, + const char *list_name, + const char *cells_name) +{ + struct of_phandle_args sup_args; + + if (strcmp(prop_name, list_name)) + return NULL; + + if (of_parse_phandle_with_args(np, list_name, cells_name, index, + &sup_args)) + return NULL; + + return sup_args.np; +} + +static struct device_node *parse_clocks(struct device_node *np, + const char *prop_name, int index) +{ + return parse_prop_cells(np, prop_name, index, "clocks", "#clock-cells"); +} + +static struct device_node *parse_interconnects(struct device_node *np, + const char *prop_name, int index) +{ + return parse_prop_cells(np, prop_name, index, "interconnects", + "#interconnect-cells"); +} + +static int strcmp_suffix(const char *str, const char *suffix) +{ + unsigned int len, suffix_len; + + len = strlen(str); + suffix_len = strlen(suffix); + if (len <= suffix_len) + return -1; + return strcmp(str + len - suffix_len, suffix); +} + +static struct device_node *parse_regulators(struct device_node *np, + const char *prop_name, int index) +{ + if (index || strcmp_suffix(prop_name, "-supply")) + return NULL; + + return of_parse_phandle(np, prop_name, 0); +} + +/** + * struct supplier_bindings - Property parsing functions for suppliers + * + * @parse_prop: function name + * parse_prop() finds the node corresponding to a supplier phandle + * @parse_prop.np: Pointer to device node holding supplier phandle property + * @parse_prop.prop_name: Name of property holding a phandle value + * @parse_prop.index: For properties holding a list of phandles, this is the + * index into the list + * + * Returns: + * parse_prop() return values are + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +struct supplier_bindings { + struct device_node *(*parse_prop)(struct device_node *np, + const char *prop_name, int index); +}; + +static const struct supplier_bindings bindings[] = { + { .parse_prop = parse_clocks, }, + { .parse_prop = parse_interconnects, }, + { .parse_prop = parse_regulators, }, + {}, +}; + +/** + * of_link_property - Create device links to suppliers listed in a property + * @dev: Consumer device + * @con_np: The consumer device tree node which contains the property + * @prop_name: Name of property to be parsed + * + * This function checks if the property @prop_name that is present in the + * @con_np device tree node is one of the known common device tree bindings + * that list phandles to suppliers. If @prop_name isn't one, this function + * doesn't do anything. + * + * If @prop_name is one, this function attempts to create device links from the + * consumer device @dev to all the devices of the suppliers listed in + * @prop_name. + * + * Any failed attempt to create a device link will NOT result in an immediate + * return. of_link_property() must create links to all the available supplier + * devices even when attempts to create a link to one or more suppliers fail. + */ +static int of_link_property(struct device *dev, struct device_node *con_np, + const char *prop_name) +{ + struct device_node *phandle; + const struct supplier_bindings *s = bindings; + unsigned int i = 0; + bool matched = false; + int ret = 0; + + /* Do not stop at first failed link, link all available suppliers. */ + while (!matched && s->parse_prop) { + while ((phandle = s->parse_prop(con_np, prop_name, i))) { + matched = true; + i++; + if (of_link_to_phandle(dev, phandle) == -EAGAIN) + ret = -EAGAIN; + of_node_put(phandle); + } + s++; + } + return ret; +} + +static int __of_link_to_suppliers(struct device *dev, + struct device_node *con_np) +{ + struct device_node *child; + struct property *p; + int ret = 0; + + for_each_property_of_node(con_np, p) + if (of_link_property(dev, con_np, p->name)) + ret = -EAGAIN; + + return ret; +} + +static bool of_devlink; +core_param(of_devlink, of_devlink, bool, 0); + +static int of_fwnode_add_links(const struct fwnode_handle *fwnode, + struct device *dev) +{ + if (!of_devlink) + return 0; + + if (unlikely(!is_of_node(fwnode))) + return 0; + + return __of_link_to_suppliers(dev, to_of_node(fwnode)); +} + const struct fwnode_operations of_fwnode_ops = { .get = of_fwnode_get, .put = of_fwnode_put, @@ -1001,5 +1241,6 @@ const struct fwnode_operations of_fwnode_ops = { .graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint, .graph_get_port_parent = of_fwnode_graph_get_port_parent, .graph_parse_endpoint = of_fwnode_graph_parse_endpoint, + .add_links = of_fwnode_add_links, }; EXPORT_SYMBOL_GPL(of_fwnode_ops); From fc5a251d0fd7ca9038bab78a8c97932c8c6ca23b Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:23 -0700 Subject: [PATCH 04/71] driver core: Add sync_state driver/bus callback This sync_state driver/bus callback is called once all the consumers of a supplier have probed successfully. This allows the supplier device's driver/bus to sync the supplier device's state to the software state with the guarantee that all the consumers are actively managing the resources provided by the supplier device. To maintain backwards compatibility and ease transition from existing frameworks and resource cleanup schemes, late_initcall_sync is the earliest when the sync_state callback might be called. There is no upper bound on the time by which the sync_state callback has to be called. This is because if a consumer device never probes, the supplier has to maintain its resources in the state left by the bootloader. For example, if the bootloader leaves the display backlight at a fixed voltage and the backlight driver is never probed, you don't want the backlight to ever be turned off after boot up. Also, when multiple devices are added after kernel init, some suppliers could be added before their consumer devices get added. In these instances, the supplier devices could get their sync_state callback called right after they probe because the consumers devices haven't had a chance to create device links to the suppliers. To handle this correctly, this change also provides APIs to pause/resume sync state callbacks so that when multiple devices are added, their sync_state callback evaluation can be postponed to happen after all of them are added. kbuild test robot reported missing documentation for device.state_synced Reported-by: kbuild test robot Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-5-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 72 ++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 24 ++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index b3896da73b3de..acbf0b1414abf 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -46,6 +46,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); /* Device links support. */ static LIST_HEAD(wait_for_suppliers); static DEFINE_MUTEX(wfs_lock); +static LIST_HEAD(deferred_sync); +static unsigned int defer_sync_state_count = 1; #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -648,6 +650,69 @@ int device_links_check_suppliers(struct device *dev) return ret; } +static void __device_links_supplier_sync_state(struct device *dev) +{ + struct device_link *link; + + if (dev->state_synced) + return; + + list_for_each_entry(link, &dev->links.consumers, s_node) { + if (!(link->flags & DL_FLAG_MANAGED)) + continue; + if (link->status != DL_STATE_ACTIVE) + return; + } + + if (dev->bus->sync_state) + dev->bus->sync_state(dev); + else if (dev->driver && dev->driver->sync_state) + dev->driver->sync_state(dev); + + dev->state_synced = true; +} + +void device_links_supplier_sync_state_pause(void) +{ + device_links_write_lock(); + defer_sync_state_count++; + device_links_write_unlock(); +} + +void device_links_supplier_sync_state_resume(void) +{ + struct device *dev, *tmp; + + device_links_write_lock(); + if (!defer_sync_state_count) { + WARN(true, "Unmatched sync_state pause/resume!"); + goto out; + } + defer_sync_state_count--; + if (defer_sync_state_count) + goto out; + + list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) { + __device_links_supplier_sync_state(dev); + list_del_init(&dev->links.defer_sync); + } +out: + device_links_write_unlock(); +} + +static int sync_state_resume_initcall(void) +{ + device_links_supplier_sync_state_resume(); + return 0; +} +late_initcall(sync_state_resume_initcall); + +static void __device_links_supplier_defer_sync(struct device *sup) +{ + if (list_empty(&sup->links.defer_sync)) + list_add_tail(&sup->links.defer_sync, &deferred_sync); +} + /** * device_links_driver_bound - Update device links after probing its driver. * @dev: Device to update the links for. @@ -692,6 +757,11 @@ void device_links_driver_bound(struct device *dev) WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); WRITE_ONCE(link->status, DL_STATE_ACTIVE); + + if (defer_sync_state_count) + __device_links_supplier_defer_sync(link->supplier); + else + __device_links_supplier_sync_state(link->supplier); } dev->links.status = DL_DEV_DRIVER_BOUND; @@ -808,6 +878,7 @@ void device_links_driver_cleanup(struct device *dev) WRITE_ONCE(link->status, DL_STATE_DORMANT); } + list_del_init(&dev->links.defer_sync); __device_links_no_driver(dev); device_links_write_unlock(); @@ -1782,6 +1853,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.needs_suppliers); + INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); diff --git a/include/linux/device.h b/include/linux/device.h index c6fb5b3431b7d..6978bb471567a 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -80,6 +80,13 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * that generate uevents to add the environment variables. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. + * @sync_state: Called to sync device state to software state after all the + * state tracking consumers linked to this device (present at + * the time of late_initcall) have successfully bound to a + * driver. If the device has no consumers, this function will + * be called at late_initcall_sync level. If the device has + * consumers that are never bound to a driver, this function + * will never get called until they do. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. * @@ -123,6 +130,7 @@ struct bus_type { int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); + void (*sync_state)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); @@ -340,6 +348,13 @@ enum probe_type { * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. + * @sync_state: Called to sync device state to software state after all the + * state tracking consumers linked to this device (present at + * the time of late_initcall) have successfully bound to a + * driver. If the device has no consumers, this function will + * be called at late_initcall_sync level. If the device has + * consumers that are never bound to a driver, this function + * will never get called until they do. * @remove: Called when the device is removed from the system to * unbind a device from this driver. * @shutdown: Called at shut-down time to quiesce the device. @@ -379,6 +394,7 @@ struct device_driver { const struct acpi_device_id *acpi_match_table; int (*probe) (struct device *dev); + void (*sync_state)(struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); @@ -1136,12 +1152,14 @@ enum dl_dev_state { * @suppliers: List of links to supplier devices. * @consumers: List of links to consumer devices. * @needs_suppliers: Hook to global list of devices waiting for suppliers. + * @defer_sync: Hook to global list of devices that have deferred sync_state. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; struct list_head needs_suppliers; + struct list_head defer_sync; enum dl_dev_state status; }; @@ -1217,6 +1235,9 @@ struct dev_links_info { * @offline: Set after successful invocation of bus type's .offline(). * @of_node_reused: Set if the device-tree node is shared with an ancestor * device. + * @state_synced: The hardware state of this device has been synced to match + * the software state of this device by calling the driver/bus + * sync_state() callback. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @@ -1313,6 +1334,7 @@ struct device { bool offline_disabled:1; bool offline:1; bool of_node_reused:1; + bool state_synced:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -1655,6 +1677,8 @@ struct device_link *device_link_add(struct device *consumer, struct device *supplier, u32 flags); void device_link_del(struct device_link *link); void device_link_remove(void *consumer, struct device *supplier); +void device_links_supplier_sync_state_pause(void); +void device_links_supplier_sync_state_resume(void); #ifndef dev_fmt #define dev_fmt(fmt) fmt From 5e6669387e2287f25f09fd0abd279dae104cfa7e Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:24 -0700 Subject: [PATCH 05/71] of/platform: Pause/resume sync state during init and of_platform_populate() When all the top level devices are populated from DT during kernel init, the supplier devices could be added and probed before the consumer devices are added and linked to the suppliers. To avoid the sync_state() callback from being called prematurely, pause the sync_state() callbacks before populating the devices and resume them at late_initcall_sync(). Similarly, when children devices are populated from a module using of_platform_populate(), there could be supplier-consumer dependencies between the children devices that are populated. To avoid the same problem with sync_state() being called prematurely, pause and resume sync_state() callbacks across of_platform_populate(). Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-6-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/platform.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b47a2292fe8e8..d93891a05f603 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -480,6 +480,7 @@ int of_platform_populate(struct device_node *root, pr_debug("%s()\n", __func__); pr_debug(" starting at: %pOF\n", root); + device_links_supplier_sync_state_pause(); for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); if (rc) { @@ -487,6 +488,8 @@ int of_platform_populate(struct device_node *root, break; } } + device_links_supplier_sync_state_resume(); + of_node_set_flag(root, OF_POPULATED_BUS); of_node_put(root); @@ -518,6 +521,7 @@ static int __init of_platform_default_populate_init(void) if (!of_have_populated_dt()) return -ENODEV; + device_links_supplier_sync_state_pause(); /* * Handle certain compatibles explicitly, since we don't want to create * platform_devices for every node in /reserved-memory with a @@ -538,6 +542,14 @@ static int __init of_platform_default_populate_init(void) return 0; } arch_initcall_sync(of_platform_default_populate_init); + +static int __init of_platform_sync_state_init(void) +{ + if (of_have_populated_dt()) + device_links_supplier_sync_state_resume(); + return 0; +} +late_initcall_sync(of_platform_sync_state_init); #endif int of_platform_device_destroy(struct device *dev, void *data) From d4387cd117414ba80230f27a514be5ca4a09cfcc Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 4 Sep 2019 14:11:25 -0700 Subject: [PATCH 06/71] of: property: Create device links for all child-supplier depencencies A parent device can have child devices that it adds when it probes. But this probing of the parent device can happen way after kernel init is done -- for example, when the parent device's driver is loaded as a module. In such cases, if the child devices depend on a supplier in the system, we need to make sure the supplier gets the sync_state() callback only after these child devices are added and probed. To achieve this, when creating device links for a device by looking at its DT node, don't just look at DT references at the top node level. Look at DT references in all the descendant nodes too and create device links from the ancestor device to all these supplier devices. This way, when the parent device probes and adds child devices, the child devices can then create their own device links to the suppliers and further delay the supplier's sync_state() callback to after the child devices are probed. Example: In this illustration, -> denotes DT references and indentation represents child status. Device node A Device node B -> D Device node C -> B, D Device node D Assume all these devices have their drivers loaded as modules. Without this patch, this is the sequence of events: 1. D is added. 2. A is added. 3. Device D probes. 4. Device D gets its sync_state() callback. 5. Device B and C might malfunction because their resources got altered/turned off before they can make active requests for them. With this patch, this is the sequence of events: 1. D is added. 2. A is added and creates device links to D. 3. Device link from A to B is not added because A is a parent of B. 4. Device D probes. 5. Device D does not get it's sync_state() callback because consumer A hasn't probed yet. 5. Device A probes. 5. a. Devices B and C are added. 5. b. Device links from B and C to D are added. 5. c. Device A's probe completes. 6. Device D does not get it's sync_state() callback because consumer A has probed but consumers B and C haven't probed yet. 7. Device B and C probe. 8. Device D gets it's sync_state() callback because all its consumers have probed. 9. None of the devices malfunction. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20190904211126.47518-7-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/property.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/of/property.c b/drivers/of/property.c index 23b5ee5b05708..923d6f88a99cb 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1207,6 +1207,10 @@ static int __of_link_to_suppliers(struct device *dev, if (of_link_property(dev, con_np, p->name)) ret = -EAGAIN; + for_each_child_of_node(con_np, child) + if (__of_link_to_suppliers(dev, child)) + ret = -EAGAIN; + return ret; } From 507fd01d5333338753a1cc26322dfc9f856c109f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 3 Oct 2019 11:29:12 +0200 Subject: [PATCH 07/71] drivers: move the early platform device support to arch/sh SuperH is the only user of the current implementation of early platform device support. We want to introduce a more robust approach to early probing. As the first step - move all the current early platform code to arch/sh. In order not to export internal drivers/base functions to arch code for this temporary solution - copy the two needed routines for driver matching from drivers/base/platform.c to arch/sh/drivers/platform_early.c. Also: call early_platform_cleanup() from subsys_initcall() so that it's called after all early devices are probed. Signed-off-by: Bartosz Golaszewski Cc: Rich Felker Link: https://lore.kernel.org/r/20191003092913.10731-2-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- arch/sh/drivers/Makefile | 2 +- arch/sh/drivers/platform_early.c | 347 +++++++++++++++++++++++++ arch/sh/include/asm/platform_early.h | 61 +++++ arch/sh/kernel/cpu/sh2/setup-sh7619.c | 1 + arch/sh/kernel/cpu/sh2a/setup-mxg.c | 1 + arch/sh/kernel/cpu/sh2a/setup-sh7201.c | 1 + arch/sh/kernel/cpu/sh2a/setup-sh7203.c | 1 + arch/sh/kernel/cpu/sh2a/setup-sh7206.c | 1 + arch/sh/kernel/cpu/sh2a/setup-sh7264.c | 1 + arch/sh/kernel/cpu/sh2a/setup-sh7269.c | 1 + arch/sh/kernel/cpu/sh3/setup-sh3.c | 1 + arch/sh/kernel/cpu/sh3/setup-sh7705.c | 1 + arch/sh/kernel/cpu/sh3/setup-sh770x.c | 1 + arch/sh/kernel/cpu/sh3/setup-sh7710.c | 1 + arch/sh/kernel/cpu/sh3/setup-sh7720.c | 1 + arch/sh/kernel/cpu/sh4/setup-sh4-202.c | 1 + arch/sh/kernel/cpu/sh4/setup-sh7750.c | 1 + arch/sh/kernel/cpu/sh4/setup-sh7760.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7343.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7366.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7723.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7734.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7757.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7763.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7770.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7780.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7785.c | 1 + arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 1 + arch/sh/kernel/cpu/sh4a/setup-shx3.c | 1 + arch/sh/kernel/cpu/sh5/setup-sh5.c | 1 + arch/sh/kernel/setup.c | 1 + arch/sh/kernel/time.c | 1 + drivers/base/platform.c | 288 -------------------- drivers/clocksource/sh_cmt.c | 7 + drivers/clocksource/sh_mtu2.c | 7 + drivers/clocksource/sh_tmu.c | 8 + drivers/tty/serial/sh-sci.c | 7 +- include/linux/platform_device.h | 64 +---- 40 files changed, 480 insertions(+), 342 deletions(-) create mode 100644 arch/sh/drivers/platform_early.c create mode 100644 arch/sh/include/asm/platform_early.h diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile index 3e93b434e6048..56b0acace6e7c 100644 --- a/arch/sh/drivers/Makefile +++ b/arch/sh/drivers/Makefile @@ -3,7 +3,7 @@ # Makefile for the Linux SuperH-specific device drivers. # -obj-y += dma/ +obj-y += dma/ platform_early.o obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_SUPERHYWAY) += superhyway/ diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c new file mode 100644 index 0000000000000..3dfe9f3541bb7 --- /dev/null +++ b/arch/sh/drivers/platform_early.c @@ -0,0 +1,347 @@ +// SPDX--License-Identifier: GPL-2.0 + +#include +#include +#include + +static __initdata LIST_HEAD(early_platform_driver_list); +static __initdata LIST_HEAD(early_platform_device_list); + +static const struct platform_device_id * +platform_match_id(const struct platform_device_id *id, + struct platform_device *pdev) +{ + while (id->name[0]) { + if (strcmp(pdev->name, id->name) == 0) { + pdev->id_entry = id; + return id; + } + id++; + } + return NULL; +} + +static int platform_match(struct device *dev, struct device_driver *drv) +{ + struct platform_device *pdev = to_platform_device(dev); + struct platform_driver *pdrv = to_platform_driver(drv); + + /* When driver_override is set, only bind to the matching driver */ + if (pdev->driver_override) + return !strcmp(pdev->driver_override, drv->name); + + /* Then try to match against the id table */ + if (pdrv->id_table) + return platform_match_id(pdrv->id_table, pdev) != NULL; + + /* fall-back to driver name match */ + return (strcmp(pdev->name, drv->name) == 0); +} + +#ifdef CONFIG_PM +static void device_pm_init_common(struct device *dev) +{ + if (!dev->power.early_init) { + spin_lock_init(&dev->power.lock); + dev->power.qos = NULL; + dev->power.early_init = true; + } +} + +static void pm_runtime_early_init(struct device *dev) +{ + dev->power.disable_depth = 1; + device_pm_init_common(dev); +} +#else +static void pm_runtime_early_init(struct device *dev) {} +#endif + +/** + * early_platform_driver_register - register early platform driver + * @epdrv: early_platform driver structure + * @buf: string passed from early_param() + * + * Helper function for early_platform_init() / early_platform_init_buffer() + */ +int __init early_platform_driver_register(struct early_platform_driver *epdrv, + char *buf) +{ + char *tmp; + int n; + + /* Simply add the driver to the end of the global list. + * Drivers will by default be put on the list in compiled-in order. + */ + if (!epdrv->list.next) { + INIT_LIST_HEAD(&epdrv->list); + list_add_tail(&epdrv->list, &early_platform_driver_list); + } + + /* If the user has specified device then make sure the driver + * gets prioritized. The driver of the last device specified on + * command line will be put first on the list. + */ + n = strlen(epdrv->pdrv->driver.name); + if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) { + list_move(&epdrv->list, &early_platform_driver_list); + + /* Allow passing parameters after device name */ + if (buf[n] == '\0' || buf[n] == ',') + epdrv->requested_id = -1; + else { + epdrv->requested_id = simple_strtoul(&buf[n + 1], + &tmp, 10); + + if (buf[n] != '.' || (tmp == &buf[n + 1])) { + epdrv->requested_id = EARLY_PLATFORM_ID_ERROR; + n = 0; + } else + n += strcspn(&buf[n + 1], ",") + 1; + } + + if (buf[n] == ',') + n++; + + if (epdrv->bufsize) { + memcpy(epdrv->buffer, &buf[n], + min_t(int, epdrv->bufsize, strlen(&buf[n]) + 1)); + epdrv->buffer[epdrv->bufsize - 1] = '\0'; + } + } + + return 0; +} + +/** + * early_platform_add_devices - adds a number of early platform devices + * @devs: array of early platform devices to add + * @num: number of early platform devices in array + * + * Used by early architecture code to register early platform devices and + * their platform data. + */ +void __init early_platform_add_devices(struct platform_device **devs, int num) +{ + struct device *dev; + int i; + + /* simply add the devices to list */ + for (i = 0; i < num; i++) { + dev = &devs[i]->dev; + + if (!dev->devres_head.next) { + pm_runtime_early_init(dev); + INIT_LIST_HEAD(&dev->devres_head); + list_add_tail(&dev->devres_head, + &early_platform_device_list); + } + } +} + +/** + * early_platform_driver_register_all - register early platform drivers + * @class_str: string to identify early platform driver class + * + * Used by architecture code to register all early platform drivers + * for a certain class. If omitted then only early platform drivers + * with matching kernel command line class parameters will be registered. + */ +void __init early_platform_driver_register_all(char *class_str) +{ + /* The "class_str" parameter may or may not be present on the kernel + * command line. If it is present then there may be more than one + * matching parameter. + * + * Since we register our early platform drivers using early_param() + * we need to make sure that they also get registered in the case + * when the parameter is missing from the kernel command line. + * + * We use parse_early_options() to make sure the early_param() gets + * called at least once. The early_param() may be called more than + * once since the name of the preferred device may be specified on + * the kernel command line. early_platform_driver_register() handles + * this case for us. + */ + parse_early_options(class_str); +} + +/** + * early_platform_match - find early platform device matching driver + * @epdrv: early platform driver structure + * @id: id to match against + */ +static struct platform_device * __init +early_platform_match(struct early_platform_driver *epdrv, int id) +{ + struct platform_device *pd; + + list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) + if (platform_match(&pd->dev, &epdrv->pdrv->driver)) + if (pd->id == id) + return pd; + + return NULL; +} + +/** + * early_platform_left - check if early platform driver has matching devices + * @epdrv: early platform driver structure + * @id: return true if id or above exists + */ +static int __init early_platform_left(struct early_platform_driver *epdrv, + int id) +{ + struct platform_device *pd; + + list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) + if (platform_match(&pd->dev, &epdrv->pdrv->driver)) + if (pd->id >= id) + return 1; + + return 0; +} + +/** + * early_platform_driver_probe_id - probe drivers matching class_str and id + * @class_str: string to identify early platform driver class + * @id: id to match against + * @nr_probe: number of platform devices to successfully probe before exiting + */ +static int __init early_platform_driver_probe_id(char *class_str, + int id, + int nr_probe) +{ + struct early_platform_driver *epdrv; + struct platform_device *match; + int match_id; + int n = 0; + int left = 0; + + list_for_each_entry(epdrv, &early_platform_driver_list, list) { + /* only use drivers matching our class_str */ + if (strcmp(class_str, epdrv->class_str)) + continue; + + if (id == -2) { + match_id = epdrv->requested_id; + left = 1; + + } else { + match_id = id; + left += early_platform_left(epdrv, id); + + /* skip requested id */ + switch (epdrv->requested_id) { + case EARLY_PLATFORM_ID_ERROR: + case EARLY_PLATFORM_ID_UNSET: + break; + default: + if (epdrv->requested_id == id) + match_id = EARLY_PLATFORM_ID_UNSET; + } + } + + switch (match_id) { + case EARLY_PLATFORM_ID_ERROR: + pr_warn("%s: unable to parse %s parameter\n", + class_str, epdrv->pdrv->driver.name); + /* fall-through */ + case EARLY_PLATFORM_ID_UNSET: + match = NULL; + break; + default: + match = early_platform_match(epdrv, match_id); + } + + if (match) { + /* + * Set up a sensible init_name to enable + * dev_name() and others to be used before the + * rest of the driver core is initialized. + */ + if (!match->dev.init_name && slab_is_available()) { + if (match->id != -1) + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s.%d", + match->name, + match->id); + else + match->dev.init_name = + kasprintf(GFP_KERNEL, "%s", + match->name); + + if (!match->dev.init_name) + return -ENOMEM; + } + + if (epdrv->pdrv->probe(match)) + pr_warn("%s: unable to probe %s early.\n", + class_str, match->name); + else + n++; + } + + if (n >= nr_probe) + break; + } + + if (left) + return n; + else + return -ENODEV; +} + +/** + * early_platform_driver_probe - probe a class of registered drivers + * @class_str: string to identify early platform driver class + * @nr_probe: number of platform devices to successfully probe before exiting + * @user_only: only probe user specified early platform devices + * + * Used by architecture code to probe registered early platform drivers + * within a certain class. For probe to happen a registered early platform + * device matching a registered early platform driver is needed. + */ +int __init early_platform_driver_probe(char *class_str, + int nr_probe, + int user_only) +{ + int k, n, i; + + n = 0; + for (i = -2; n < nr_probe; i++) { + k = early_platform_driver_probe_id(class_str, i, nr_probe - n); + + if (k < 0) + break; + + n += k; + + if (user_only) + break; + } + + return n; +} + +/** + * early_platform_cleanup - clean up early platform code + */ +static int __init early_platform_cleanup(void) +{ + struct platform_device *pd, *pd2; + + /* clean up the devres list used to chain devices */ + list_for_each_entry_safe(pd, pd2, &early_platform_device_list, + dev.devres_head) { + list_del(&pd->dev.devres_head); + memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head)); + } + + return 0; +} +/* + * This must happen once after all early devices are probed but before probing + * real platform devices. + */ +subsys_initcall(early_platform_cleanup); diff --git a/arch/sh/include/asm/platform_early.h b/arch/sh/include/asm/platform_early.h new file mode 100644 index 0000000000000..4590ab757d5fa --- /dev/null +++ b/arch/sh/include/asm/platform_early.h @@ -0,0 +1,61 @@ +/* SPDX--License-Identifier: GPL-2.0 */ + +#ifndef __PLATFORM_EARLY__ +#define __PLATFORM_EARLY__ + +#include +#include +#include +#include + +struct early_platform_driver { + const char *class_str; + struct platform_driver *pdrv; + struct list_head list; + int requested_id; + char *buffer; + int bufsize; +}; + +#define EARLY_PLATFORM_ID_UNSET -2 +#define EARLY_PLATFORM_ID_ERROR -3 + +extern int early_platform_driver_register(struct early_platform_driver *epdrv, + char *buf); +extern void early_platform_add_devices(struct platform_device **devs, int num); + +static inline int is_early_platform_device(struct platform_device *pdev) +{ + return !pdev->dev.driver; +} + +extern void early_platform_driver_register_all(char *class_str); +extern int early_platform_driver_probe(char *class_str, + int nr_probe, int user_only); + +#define early_platform_init(class_string, platdrv) \ + early_platform_init_buffer(class_string, platdrv, NULL, 0) + +#ifndef MODULE +#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ +static __initdata struct early_platform_driver early_driver = { \ + .class_str = class_string, \ + .buffer = buf, \ + .bufsize = bufsiz, \ + .pdrv = platdrv, \ + .requested_id = EARLY_PLATFORM_ID_UNSET, \ +}; \ +static int __init early_platform_driver_setup_func(char *buffer) \ +{ \ + return early_platform_driver_register(&early_driver, buffer); \ +} \ +early_param(class_string, early_platform_driver_setup_func) +#else /* MODULE */ +#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ +static inline char *early_platform_driver_setup_func(void) \ +{ \ + return bufsiz ? buf : NULL; \ +} +#endif /* MODULE */ + +#endif /* __PLATFORM_EARLY__ */ diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c index f5b6841ef7e1c..27d459e8a5981 100644 --- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c +++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c @@ -12,6 +12,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh2a/setup-mxg.c b/arch/sh/kernel/cpu/sh2a/setup-mxg.c index 52350ad0b0a26..8a19c5754978e 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-mxg.c +++ b/arch/sh/kernel/cpu/sh2a/setup-mxg.c @@ -9,6 +9,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c index b51ed761ae087..d975e4d404bda 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c @@ -11,6 +11,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c index 89b3e49fc2503..318b8cd8d5b71 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c @@ -10,6 +10,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c index 36ff3a3139dab..8e6fae60074a3 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c @@ -11,6 +11,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c index d199618d877c0..2d95c9b38330c 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c @@ -11,6 +11,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c index 9095c960b4558..ef4ff76cbe9e1 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c @@ -12,6 +12,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh3/setup-sh3.c b/arch/sh/kernel/cpu/sh3/setup-sh3.c index 8058c01cf09df..cf2a3f09fee44 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh3.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh3.c @@ -8,6 +8,7 @@ #include #include #include +#include /* All SH3 devices are equipped with IRQ0->5 (except sh7708) */ diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c index e19d1ce7b6ad1..9b34d1f4e797a 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c @@ -14,6 +14,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c index 5c5144bee6bcc..39f09504bd968 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -18,6 +18,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7710.c b/arch/sh/kernel/cpu/sh3/setup-sh7710.c index 4776e2495738f..c006c8fcfa0fe 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7710.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7710.c @@ -13,6 +13,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c index 1d4c34e7b7db9..cf2eec9f6abd9 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c @@ -19,6 +19,7 @@ #include #include #include +#include #include static struct resource rtc_resources[] = { diff --git a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c index a40ef35d101a9..2480ce4a3b1e9 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c @@ -12,6 +12,7 @@ #include #include #include +#include static struct plat_sci_port scif0_platform_data = { .scscr = SCSCR_REIE, diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c index b37bda66a532e..4592d660b9426 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -13,6 +13,7 @@ #include #include #include +#include static struct resource rtc_resources[] = { [0] = { diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c index 86845da859979..e64eb96e35892 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7760.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c @@ -11,6 +11,7 @@ #include #include #include +#include enum { UNUSED = 0, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c index a15e25690b5f9..0fa56128bdca0 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c @@ -12,6 +12,7 @@ #include #include #include +#include /* Serial */ static struct plat_sci_port scif0_platform_data = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c index 7bd2776441ba0..9f2f75285ec35 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c @@ -15,6 +15,7 @@ #include #include #include +#include static struct plat_sci_port scif0_platform_data = { .scscr = SCSCR_REIE, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index 1ce65f88f060e..8e5eaac789a73 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c index edb6499506625..b4b54aa0c2677 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* Serial */ diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c index 3e9825031d3d7..ec237ab112cf7 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c index 06a91569697a7..7ea9a8b439559 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c @@ -18,6 +18,7 @@ #include #include #include +#include #include /* SCIF */ diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c index 2501ce656511b..922d9886be181 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c @@ -19,6 +19,7 @@ #include #include #include +#include static struct plat_sci_port scif2_platform_data = { .scscr = SCSCR_REIE, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c index 419c5efe4a17f..8725d4751bafe 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c @@ -14,6 +14,7 @@ #include #include #include +#include static struct plat_sci_port scif0_platform_data = { .scscr = SCSCR_REIE, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c index 5fb4cf9b58c69..035a0d4f2a2ec 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c @@ -11,6 +11,7 @@ #include #include #include +#include static struct plat_sci_port scif0_platform_data = { .scscr = SCSCR_REIE | SCSCR_TOIE, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c index ab7d6b715865b..983be87cb7be4 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c @@ -13,6 +13,7 @@ #include #include #include +#include static struct plat_sci_port scif0_platform_data = { .scscr = SCSCR_REIE | SCSCR_CKE1, diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c index a438da47285d6..bb0cb710c16e0 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c @@ -14,6 +14,7 @@ #include #include #include +#include #include static struct plat_sci_port scif0_platform_data = { diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index d894165a0ef6f..b5d94366eed32 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -23,6 +23,7 @@ #include #include #include +#include static struct plat_sci_port scif0_platform_data = { .scscr = SCSCR_REIE | SCSCR_CKE1, diff --git a/arch/sh/kernel/cpu/sh4a/setup-shx3.c b/arch/sh/kernel/cpu/sh4a/setup-shx3.c index 14aa4552bc45b..e82398d80b74e 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-shx3.c +++ b/arch/sh/kernel/cpu/sh4a/setup-shx3.c @@ -14,6 +14,7 @@ #include #include #include +#include /* * This intentionally only registers SCIF ports 0, 1, and 3. SCIF 2 diff --git a/arch/sh/kernel/cpu/sh5/setup-sh5.c b/arch/sh/kernel/cpu/sh5/setup-sh5.c index 41c1673afc0b4..584a6aeeca261 100644 --- a/arch/sh/kernel/cpu/sh5/setup-sh5.c +++ b/arch/sh/kernel/cpu/sh5/setup-sh5.c @@ -12,6 +12,7 @@ #include #include #include +#include static struct plat_sci_port scif0_platform_data = { .flags = UPF_IOREMAP, diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index 2c0e0f37a318d..e535507851e1b 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -44,6 +44,7 @@ #include #include #include +#include /* * Initialize loops_per_jiffy as 10000000 (1000MIPS). diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index e16b2cd269a33..8ad9fd5c4ff60 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c @@ -18,6 +18,7 @@ #include #include #include +#include static void __init sh_late_time_init(void) { diff --git a/drivers/base/platform.c b/drivers/base/platform.c index b6c6c7d97d5b2..be1e67ee3af67 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -1264,8 +1264,6 @@ int __init platform_bus_init(void) { int error; - early_platform_cleanup(); - error = device_register(&platform_bus); if (error) { put_device(&platform_bus); @@ -1277,289 +1275,3 @@ int __init platform_bus_init(void) of_platform_register_reconfig_notifier(); return error; } - -static __initdata LIST_HEAD(early_platform_driver_list); -static __initdata LIST_HEAD(early_platform_device_list); - -/** - * early_platform_driver_register - register early platform driver - * @epdrv: early_platform driver structure - * @buf: string passed from early_param() - * - * Helper function for early_platform_init() / early_platform_init_buffer() - */ -int __init early_platform_driver_register(struct early_platform_driver *epdrv, - char *buf) -{ - char *tmp; - int n; - - /* Simply add the driver to the end of the global list. - * Drivers will by default be put on the list in compiled-in order. - */ - if (!epdrv->list.next) { - INIT_LIST_HEAD(&epdrv->list); - list_add_tail(&epdrv->list, &early_platform_driver_list); - } - - /* If the user has specified device then make sure the driver - * gets prioritized. The driver of the last device specified on - * command line will be put first on the list. - */ - n = strlen(epdrv->pdrv->driver.name); - if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) { - list_move(&epdrv->list, &early_platform_driver_list); - - /* Allow passing parameters after device name */ - if (buf[n] == '\0' || buf[n] == ',') - epdrv->requested_id = -1; - else { - epdrv->requested_id = simple_strtoul(&buf[n + 1], - &tmp, 10); - - if (buf[n] != '.' || (tmp == &buf[n + 1])) { - epdrv->requested_id = EARLY_PLATFORM_ID_ERROR; - n = 0; - } else - n += strcspn(&buf[n + 1], ",") + 1; - } - - if (buf[n] == ',') - n++; - - if (epdrv->bufsize) { - memcpy(epdrv->buffer, &buf[n], - min_t(int, epdrv->bufsize, strlen(&buf[n]) + 1)); - epdrv->buffer[epdrv->bufsize - 1] = '\0'; - } - } - - return 0; -} - -/** - * early_platform_add_devices - adds a number of early platform devices - * @devs: array of early platform devices to add - * @num: number of early platform devices in array - * - * Used by early architecture code to register early platform devices and - * their platform data. - */ -void __init early_platform_add_devices(struct platform_device **devs, int num) -{ - struct device *dev; - int i; - - /* simply add the devices to list */ - for (i = 0; i < num; i++) { - dev = &devs[i]->dev; - - if (!dev->devres_head.next) { - pm_runtime_early_init(dev); - INIT_LIST_HEAD(&dev->devres_head); - list_add_tail(&dev->devres_head, - &early_platform_device_list); - } - } -} - -/** - * early_platform_driver_register_all - register early platform drivers - * @class_str: string to identify early platform driver class - * - * Used by architecture code to register all early platform drivers - * for a certain class. If omitted then only early platform drivers - * with matching kernel command line class parameters will be registered. - */ -void __init early_platform_driver_register_all(char *class_str) -{ - /* The "class_str" parameter may or may not be present on the kernel - * command line. If it is present then there may be more than one - * matching parameter. - * - * Since we register our early platform drivers using early_param() - * we need to make sure that they also get registered in the case - * when the parameter is missing from the kernel command line. - * - * We use parse_early_options() to make sure the early_param() gets - * called at least once. The early_param() may be called more than - * once since the name of the preferred device may be specified on - * the kernel command line. early_platform_driver_register() handles - * this case for us. - */ - parse_early_options(class_str); -} - -/** - * early_platform_match - find early platform device matching driver - * @epdrv: early platform driver structure - * @id: id to match against - */ -static struct platform_device * __init -early_platform_match(struct early_platform_driver *epdrv, int id) -{ - struct platform_device *pd; - - list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) - if (platform_match(&pd->dev, &epdrv->pdrv->driver)) - if (pd->id == id) - return pd; - - return NULL; -} - -/** - * early_platform_left - check if early platform driver has matching devices - * @epdrv: early platform driver structure - * @id: return true if id or above exists - */ -static int __init early_platform_left(struct early_platform_driver *epdrv, - int id) -{ - struct platform_device *pd; - - list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) - if (platform_match(&pd->dev, &epdrv->pdrv->driver)) - if (pd->id >= id) - return 1; - - return 0; -} - -/** - * early_platform_driver_probe_id - probe drivers matching class_str and id - * @class_str: string to identify early platform driver class - * @id: id to match against - * @nr_probe: number of platform devices to successfully probe before exiting - */ -static int __init early_platform_driver_probe_id(char *class_str, - int id, - int nr_probe) -{ - struct early_platform_driver *epdrv; - struct platform_device *match; - int match_id; - int n = 0; - int left = 0; - - list_for_each_entry(epdrv, &early_platform_driver_list, list) { - /* only use drivers matching our class_str */ - if (strcmp(class_str, epdrv->class_str)) - continue; - - if (id == -2) { - match_id = epdrv->requested_id; - left = 1; - - } else { - match_id = id; - left += early_platform_left(epdrv, id); - - /* skip requested id */ - switch (epdrv->requested_id) { - case EARLY_PLATFORM_ID_ERROR: - case EARLY_PLATFORM_ID_UNSET: - break; - default: - if (epdrv->requested_id == id) - match_id = EARLY_PLATFORM_ID_UNSET; - } - } - - switch (match_id) { - case EARLY_PLATFORM_ID_ERROR: - pr_warn("%s: unable to parse %s parameter\n", - class_str, epdrv->pdrv->driver.name); - /* fall-through */ - case EARLY_PLATFORM_ID_UNSET: - match = NULL; - break; - default: - match = early_platform_match(epdrv, match_id); - } - - if (match) { - /* - * Set up a sensible init_name to enable - * dev_name() and others to be used before the - * rest of the driver core is initialized. - */ - if (!match->dev.init_name && slab_is_available()) { - if (match->id != -1) - match->dev.init_name = - kasprintf(GFP_KERNEL, "%s.%d", - match->name, - match->id); - else - match->dev.init_name = - kasprintf(GFP_KERNEL, "%s", - match->name); - - if (!match->dev.init_name) - return -ENOMEM; - } - - if (epdrv->pdrv->probe(match)) - pr_warn("%s: unable to probe %s early.\n", - class_str, match->name); - else - n++; - } - - if (n >= nr_probe) - break; - } - - if (left) - return n; - else - return -ENODEV; -} - -/** - * early_platform_driver_probe - probe a class of registered drivers - * @class_str: string to identify early platform driver class - * @nr_probe: number of platform devices to successfully probe before exiting - * @user_only: only probe user specified early platform devices - * - * Used by architecture code to probe registered early platform drivers - * within a certain class. For probe to happen a registered early platform - * device matching a registered early platform driver is needed. - */ -int __init early_platform_driver_probe(char *class_str, - int nr_probe, - int user_only) -{ - int k, n, i; - - n = 0; - for (i = -2; n < nr_probe; i++) { - k = early_platform_driver_probe_id(class_str, i, nr_probe - n); - - if (k < 0) - break; - - n += k; - - if (user_only) - break; - } - - return n; -} - -/** - * early_platform_cleanup - clean up early platform code - */ -void __init early_platform_cleanup(void) -{ - struct platform_device *pd, *pd2; - - /* clean up the devres list used to chain devices */ - list_for_each_entry_safe(pd, pd2, &early_platform_device_list, - dev.devres_head) { - list_del(&pd->dev.devres_head); - memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head)); - } -} - diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index ef773db080e90..3a185485300e4 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -25,6 +25,10 @@ #include #include +#ifdef CONFIG_SUPERH +#include +#endif + struct sh_cmt_device; /* @@ -1109,7 +1113,10 @@ static void __exit sh_cmt_exit(void) platform_driver_unregister(&sh_cmt_device_driver); } +#ifdef CONFIG_SUPERH early_platform_init("earlytimer", &sh_cmt_device_driver); +#endif + subsys_initcall(sh_cmt_init); module_exit(sh_cmt_exit); diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index 354b27d14a19b..d581060d33a26 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -23,6 +23,10 @@ #include #include +#ifdef CONFIG_SUPERH +#include +#endif + struct sh_mtu2_device; struct sh_mtu2_channel { @@ -511,7 +515,10 @@ static void __exit sh_mtu2_exit(void) platform_driver_unregister(&sh_mtu2_device_driver); } +#ifdef CONFIG_SUPERH early_platform_init("earlytimer", &sh_mtu2_device_driver); +#endif + subsys_initcall(sh_mtu2_init); module_exit(sh_mtu2_exit); diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 8c4f3753b36ec..50e30ead050e3 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -24,6 +24,10 @@ #include #include +#ifdef CONFIG_SUPERH +#include +#endif + enum sh_tmu_model { SH_TMU, SH_TMU_SH3, @@ -615,6 +619,7 @@ static int sh_tmu_probe(struct platform_device *pdev) pm_runtime_idle(&pdev->dev); return ret; } + if (is_early_platform_device(pdev)) return 0; @@ -665,7 +670,10 @@ static void __exit sh_tmu_exit(void) platform_driver_unregister(&sh_tmu_device_driver); } +#ifdef CONFIG_SUPERH early_platform_init("earlytimer", &sh_tmu_device_driver); +#endif + subsys_initcall(sh_tmu_init); module_exit(sh_tmu_exit); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 4e754a4850e6d..2a645fd7f07b4 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -54,6 +54,7 @@ #ifdef CONFIG_SUPERH #include +#include #endif #include "serial_mctrl_gpio.h" @@ -3086,6 +3087,7 @@ static struct console serial_console = { .data = &sci_uart_driver, }; +#ifdef CONFIG_SUPERH static struct console early_serial_console = { .name = "early_ttySC", .write = serial_console_write, @@ -3114,6 +3116,7 @@ static int sci_probe_earlyprintk(struct platform_device *pdev) register_console(&early_serial_console); return 0; } +#endif #define SCI_CONSOLE (&serial_console) @@ -3314,8 +3317,10 @@ static int sci_probe(struct platform_device *dev) * the special early probe. We don't have sufficient device state * to make it beyond this yet. */ +#ifdef CONFIG_SUPERH if (is_early_platform_device(dev)) return sci_probe_earlyprintk(dev); +#endif if (dev->dev.of_node) { p = sci_parse_dt(dev, &dev_id); @@ -3410,7 +3415,7 @@ static void __exit sci_exit(void) uart_unregister_driver(&sci_uart_driver); } -#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE +#if defined(CONFIG_SUPERH) && defined(CONFIG_SERIAL_SH_SCI_CONSOLE) early_platform_init_buffer("earlyprintk", &sci_driver, early_serial_buf, ARRAY_SIZE(early_serial_buf)); #endif diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 1b5cec0675335..85aa28a041a9c 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -292,58 +292,6 @@ void platform_unregister_drivers(struct platform_driver * const *drivers, #define platform_register_drivers(drivers, count) \ __platform_register_drivers(drivers, count, THIS_MODULE) -/* early platform driver interface */ -struct early_platform_driver { - const char *class_str; - struct platform_driver *pdrv; - struct list_head list; - int requested_id; - char *buffer; - int bufsize; -}; - -#define EARLY_PLATFORM_ID_UNSET -2 -#define EARLY_PLATFORM_ID_ERROR -3 - -extern int early_platform_driver_register(struct early_platform_driver *epdrv, - char *buf); -extern void early_platform_add_devices(struct platform_device **devs, int num); - -static inline int is_early_platform_device(struct platform_device *pdev) -{ - return !pdev->dev.driver; -} - -extern void early_platform_driver_register_all(char *class_str); -extern int early_platform_driver_probe(char *class_str, - int nr_probe, int user_only); -extern void early_platform_cleanup(void); - -#define early_platform_init(class_string, platdrv) \ - early_platform_init_buffer(class_string, platdrv, NULL, 0) - -#ifndef MODULE -#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ -static __initdata struct early_platform_driver early_driver = { \ - .class_str = class_string, \ - .buffer = buf, \ - .bufsize = bufsiz, \ - .pdrv = platdrv, \ - .requested_id = EARLY_PLATFORM_ID_UNSET, \ -}; \ -static int __init early_platform_driver_setup_func(char *buffer) \ -{ \ - return early_platform_driver_register(&early_driver, buffer); \ -} \ -early_param(class_string, early_platform_driver_setup_func) -#else /* MODULE */ -#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ -static inline char *early_platform_driver_setup_func(void) \ -{ \ - return bufsiz ? buf : NULL; \ -} -#endif /* MODULE */ - #ifdef CONFIG_SUSPEND extern int platform_pm_suspend(struct device *dev); extern int platform_pm_resume(struct device *dev); @@ -378,4 +326,16 @@ extern int platform_dma_configure(struct device *dev); #define USE_PLATFORM_PM_SLEEP_OPS #endif +#ifndef CONFIG_SUPERH +/* + * REVISIT: This stub is needed for all non-SuperH users of early platform + * drivers. It should go away once we introduce the new platform_device-based + * early driver framework. + */ +static inline int is_early_platform_device(struct platform_device *pdev) +{ + return 0; +} +#endif /* CONFIG_SUPERH */ + #endif /* _PLATFORM_DEVICE_H_ */ From 201e91091b1d47047f55580b5474e1239f4d17aa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 3 Oct 2019 11:29:13 +0200 Subject: [PATCH 08/71] sh: add the sh_ prefix to early platform symbols Old early platform device support is now sh-specific. Before moving on to implementing new early platform framework based on real platform devices, prefix all early platform symbols with 'sh_'. Signed-off-by: Bartosz Golaszewski Cc: Rich Felker Link: https://lore.kernel.org/r/20191003092913.10731-3-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- arch/sh/drivers/platform_early.c | 66 +++++++++++++------------- arch/sh/include/asm/platform_early.h | 30 ++++++------ arch/sh/kernel/cpu/sh2/setup-sh7619.c | 2 +- arch/sh/kernel/cpu/sh2a/setup-mxg.c | 2 +- arch/sh/kernel/cpu/sh2a/setup-sh7201.c | 2 +- arch/sh/kernel/cpu/sh2a/setup-sh7203.c | 2 +- arch/sh/kernel/cpu/sh2a/setup-sh7206.c | 2 +- arch/sh/kernel/cpu/sh2a/setup-sh7264.c | 2 +- arch/sh/kernel/cpu/sh2a/setup-sh7269.c | 2 +- arch/sh/kernel/cpu/sh3/setup-sh7705.c | 2 +- arch/sh/kernel/cpu/sh3/setup-sh770x.c | 2 +- arch/sh/kernel/cpu/sh3/setup-sh7710.c | 2 +- arch/sh/kernel/cpu/sh3/setup-sh7720.c | 2 +- arch/sh/kernel/cpu/sh4/setup-sh4-202.c | 2 +- arch/sh/kernel/cpu/sh4/setup-sh7750.c | 8 ++-- arch/sh/kernel/cpu/sh4/setup-sh7760.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7343.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7366.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7722.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7723.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7724.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7734.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7757.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7763.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7770.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7780.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7785.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-sh7786.c | 2 +- arch/sh/kernel/cpu/sh4a/setup-shx3.c | 2 +- arch/sh/kernel/cpu/sh5/setup-sh5.c | 2 +- arch/sh/kernel/setup.c | 2 +- arch/sh/kernel/time.c | 4 +- drivers/clocksource/sh_cmt.c | 6 +-- drivers/clocksource/sh_mtu2.c | 6 +-- drivers/clocksource/sh_tmu.c | 6 +-- drivers/tty/serial/sh-sci.c | 4 +- include/linux/platform_device.h | 2 +- 37 files changed, 94 insertions(+), 94 deletions(-) diff --git a/arch/sh/drivers/platform_early.c b/arch/sh/drivers/platform_early.c index 3dfe9f3541bb7..f6d148451dfc7 100644 --- a/arch/sh/drivers/platform_early.c +++ b/arch/sh/drivers/platform_early.c @@ -4,8 +4,8 @@ #include #include -static __initdata LIST_HEAD(early_platform_driver_list); -static __initdata LIST_HEAD(early_platform_device_list); +static __initdata LIST_HEAD(sh_early_platform_driver_list); +static __initdata LIST_HEAD(sh_early_platform_device_list); static const struct platform_device_id * platform_match_id(const struct platform_device_id *id, @@ -58,13 +58,13 @@ static void pm_runtime_early_init(struct device *dev) {} #endif /** - * early_platform_driver_register - register early platform driver - * @epdrv: early_platform driver structure + * sh_early_platform_driver_register - register early platform driver + * @epdrv: sh_early_platform driver structure * @buf: string passed from early_param() * - * Helper function for early_platform_init() / early_platform_init_buffer() + * Helper function for sh_early_platform_init() / sh_early_platform_init_buffer() */ -int __init early_platform_driver_register(struct early_platform_driver *epdrv, +int __init sh_early_platform_driver_register(struct sh_early_platform_driver *epdrv, char *buf) { char *tmp; @@ -75,7 +75,7 @@ int __init early_platform_driver_register(struct early_platform_driver *epdrv, */ if (!epdrv->list.next) { INIT_LIST_HEAD(&epdrv->list); - list_add_tail(&epdrv->list, &early_platform_driver_list); + list_add_tail(&epdrv->list, &sh_early_platform_driver_list); } /* If the user has specified device then make sure the driver @@ -84,7 +84,7 @@ int __init early_platform_driver_register(struct early_platform_driver *epdrv, */ n = strlen(epdrv->pdrv->driver.name); if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) { - list_move(&epdrv->list, &early_platform_driver_list); + list_move(&epdrv->list, &sh_early_platform_driver_list); /* Allow passing parameters after device name */ if (buf[n] == '\0' || buf[n] == ',') @@ -114,14 +114,14 @@ int __init early_platform_driver_register(struct early_platform_driver *epdrv, } /** - * early_platform_add_devices - adds a number of early platform devices + * sh_early_platform_add_devices - adds a number of early platform devices * @devs: array of early platform devices to add * @num: number of early platform devices in array * * Used by early architecture code to register early platform devices and * their platform data. */ -void __init early_platform_add_devices(struct platform_device **devs, int num) +void __init sh_early_platform_add_devices(struct platform_device **devs, int num) { struct device *dev; int i; @@ -134,20 +134,20 @@ void __init early_platform_add_devices(struct platform_device **devs, int num) pm_runtime_early_init(dev); INIT_LIST_HEAD(&dev->devres_head); list_add_tail(&dev->devres_head, - &early_platform_device_list); + &sh_early_platform_device_list); } } } /** - * early_platform_driver_register_all - register early platform drivers + * sh_early_platform_driver_register_all - register early platform drivers * @class_str: string to identify early platform driver class * * Used by architecture code to register all early platform drivers * for a certain class. If omitted then only early platform drivers * with matching kernel command line class parameters will be registered. */ -void __init early_platform_driver_register_all(char *class_str) +void __init sh_early_platform_driver_register_all(char *class_str) { /* The "class_str" parameter may or may not be present on the kernel * command line. If it is present then there may be more than one @@ -160,23 +160,23 @@ void __init early_platform_driver_register_all(char *class_str) * We use parse_early_options() to make sure the early_param() gets * called at least once. The early_param() may be called more than * once since the name of the preferred device may be specified on - * the kernel command line. early_platform_driver_register() handles + * the kernel command line. sh_early_platform_driver_register() handles * this case for us. */ parse_early_options(class_str); } /** - * early_platform_match - find early platform device matching driver + * sh_early_platform_match - find early platform device matching driver * @epdrv: early platform driver structure * @id: id to match against */ static struct platform_device * __init -early_platform_match(struct early_platform_driver *epdrv, int id) +sh_early_platform_match(struct sh_early_platform_driver *epdrv, int id) { struct platform_device *pd; - list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) + list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head) if (platform_match(&pd->dev, &epdrv->pdrv->driver)) if (pd->id == id) return pd; @@ -185,16 +185,16 @@ early_platform_match(struct early_platform_driver *epdrv, int id) } /** - * early_platform_left - check if early platform driver has matching devices + * sh_early_platform_left - check if early platform driver has matching devices * @epdrv: early platform driver structure * @id: return true if id or above exists */ -static int __init early_platform_left(struct early_platform_driver *epdrv, +static int __init sh_early_platform_left(struct sh_early_platform_driver *epdrv, int id) { struct platform_device *pd; - list_for_each_entry(pd, &early_platform_device_list, dev.devres_head) + list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head) if (platform_match(&pd->dev, &epdrv->pdrv->driver)) if (pd->id >= id) return 1; @@ -203,22 +203,22 @@ static int __init early_platform_left(struct early_platform_driver *epdrv, } /** - * early_platform_driver_probe_id - probe drivers matching class_str and id + * sh_early_platform_driver_probe_id - probe drivers matching class_str and id * @class_str: string to identify early platform driver class * @id: id to match against * @nr_probe: number of platform devices to successfully probe before exiting */ -static int __init early_platform_driver_probe_id(char *class_str, +static int __init sh_early_platform_driver_probe_id(char *class_str, int id, int nr_probe) { - struct early_platform_driver *epdrv; + struct sh_early_platform_driver *epdrv; struct platform_device *match; int match_id; int n = 0; int left = 0; - list_for_each_entry(epdrv, &early_platform_driver_list, list) { + list_for_each_entry(epdrv, &sh_early_platform_driver_list, list) { /* only use drivers matching our class_str */ if (strcmp(class_str, epdrv->class_str)) continue; @@ -229,7 +229,7 @@ static int __init early_platform_driver_probe_id(char *class_str, } else { match_id = id; - left += early_platform_left(epdrv, id); + left += sh_early_platform_left(epdrv, id); /* skip requested id */ switch (epdrv->requested_id) { @@ -251,7 +251,7 @@ static int __init early_platform_driver_probe_id(char *class_str, match = NULL; break; default: - match = early_platform_match(epdrv, match_id); + match = sh_early_platform_match(epdrv, match_id); } if (match) { @@ -293,7 +293,7 @@ static int __init early_platform_driver_probe_id(char *class_str, } /** - * early_platform_driver_probe - probe a class of registered drivers + * sh_early_platform_driver_probe - probe a class of registered drivers * @class_str: string to identify early platform driver class * @nr_probe: number of platform devices to successfully probe before exiting * @user_only: only probe user specified early platform devices @@ -302,7 +302,7 @@ static int __init early_platform_driver_probe_id(char *class_str, * within a certain class. For probe to happen a registered early platform * device matching a registered early platform driver is needed. */ -int __init early_platform_driver_probe(char *class_str, +int __init sh_early_platform_driver_probe(char *class_str, int nr_probe, int user_only) { @@ -310,7 +310,7 @@ int __init early_platform_driver_probe(char *class_str, n = 0; for (i = -2; n < nr_probe; i++) { - k = early_platform_driver_probe_id(class_str, i, nr_probe - n); + k = sh_early_platform_driver_probe_id(class_str, i, nr_probe - n); if (k < 0) break; @@ -325,14 +325,14 @@ int __init early_platform_driver_probe(char *class_str, } /** - * early_platform_cleanup - clean up early platform code + * sh_early_platform_cleanup - clean up early platform code */ -static int __init early_platform_cleanup(void) +static int __init sh_early_platform_cleanup(void) { struct platform_device *pd, *pd2; /* clean up the devres list used to chain devices */ - list_for_each_entry_safe(pd, pd2, &early_platform_device_list, + list_for_each_entry_safe(pd, pd2, &sh_early_platform_device_list, dev.devres_head) { list_del(&pd->dev.devres_head); memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head)); @@ -344,4 +344,4 @@ static int __init early_platform_cleanup(void) * This must happen once after all early devices are probed but before probing * real platform devices. */ -subsys_initcall(early_platform_cleanup); +subsys_initcall(sh_early_platform_cleanup); diff --git a/arch/sh/include/asm/platform_early.h b/arch/sh/include/asm/platform_early.h index 4590ab757d5fa..fc802137c37d4 100644 --- a/arch/sh/include/asm/platform_early.h +++ b/arch/sh/include/asm/platform_early.h @@ -8,7 +8,7 @@ #include #include -struct early_platform_driver { +struct sh_early_platform_driver { const char *class_str; struct platform_driver *pdrv; struct list_head list; @@ -20,39 +20,39 @@ struct early_platform_driver { #define EARLY_PLATFORM_ID_UNSET -2 #define EARLY_PLATFORM_ID_ERROR -3 -extern int early_platform_driver_register(struct early_platform_driver *epdrv, +extern int sh_early_platform_driver_register(struct sh_early_platform_driver *epdrv, char *buf); -extern void early_platform_add_devices(struct platform_device **devs, int num); +extern void sh_early_platform_add_devices(struct platform_device **devs, int num); -static inline int is_early_platform_device(struct platform_device *pdev) +static inline int is_sh_early_platform_device(struct platform_device *pdev) { return !pdev->dev.driver; } -extern void early_platform_driver_register_all(char *class_str); -extern int early_platform_driver_probe(char *class_str, +extern void sh_early_platform_driver_register_all(char *class_str); +extern int sh_early_platform_driver_probe(char *class_str, int nr_probe, int user_only); -#define early_platform_init(class_string, platdrv) \ - early_platform_init_buffer(class_string, platdrv, NULL, 0) +#define sh_early_platform_init(class_string, platdrv) \ + sh_early_platform_init_buffer(class_string, platdrv, NULL, 0) #ifndef MODULE -#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ -static __initdata struct early_platform_driver early_driver = { \ +#define sh_early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ +static __initdata struct sh_early_platform_driver early_driver = { \ .class_str = class_string, \ .buffer = buf, \ .bufsize = bufsiz, \ .pdrv = platdrv, \ .requested_id = EARLY_PLATFORM_ID_UNSET, \ }; \ -static int __init early_platform_driver_setup_func(char *buffer) \ +static int __init sh_early_platform_driver_setup_func(char *buffer) \ { \ - return early_platform_driver_register(&early_driver, buffer); \ + return sh_early_platform_driver_register(&early_driver, buffer); \ } \ -early_param(class_string, early_platform_driver_setup_func) +early_param(class_string, sh_early_platform_driver_setup_func) #else /* MODULE */ -#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ -static inline char *early_platform_driver_setup_func(void) \ +#define sh_early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \ +static inline char *sh_early_platform_driver_setup_func(void) \ { \ return bufsiz ? buf : NULL; \ } diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c index 27d459e8a5981..b1c877b6a4207 100644 --- a/arch/sh/kernel/cpu/sh2/setup-sh7619.c +++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c @@ -200,6 +200,6 @@ void __init plat_early_device_setup(void) /* enable CMT clock */ __raw_writeb(__raw_readb(STBCR3) & ~0x10, STBCR3); - early_platform_add_devices(sh7619_early_devices, + sh_early_platform_add_devices(sh7619_early_devices, ARRAY_SIZE(sh7619_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh2a/setup-mxg.c b/arch/sh/kernel/cpu/sh2a/setup-mxg.c index 8a19c5754978e..cefa07924c16d 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-mxg.c +++ b/arch/sh/kernel/cpu/sh2a/setup-mxg.c @@ -170,6 +170,6 @@ static struct platform_device *mxg_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(mxg_early_devices, + sh_early_platform_add_devices(mxg_early_devices, ARRAY_SIZE(mxg_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c index d975e4d404bda..28f1bebf34056 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7201.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7201.c @@ -413,6 +413,6 @@ void __init plat_early_device_setup(void) /* enable MTU2 clock */ __raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3); - early_platform_add_devices(sh7201_early_devices, + sh_early_platform_add_devices(sh7201_early_devices, ARRAY_SIZE(sh7201_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c index 318b8cd8d5b71..4839f3aaeb4c2 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7203.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7203.c @@ -350,6 +350,6 @@ void __init plat_early_device_setup(void) /* enable MTU2 clock */ __raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3); - early_platform_add_devices(sh7203_early_devices, + sh_early_platform_add_devices(sh7203_early_devices, ARRAY_SIZE(sh7203_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c index 8e6fae60074a3..68add5af4cc56 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c @@ -286,6 +286,6 @@ void __init plat_early_device_setup(void) /* enable MTU2 clock */ __raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3); - early_platform_add_devices(sh7206_early_devices, + sh_early_platform_add_devices(sh7206_early_devices, ARRAY_SIZE(sh7206_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c index 2d95c9b38330c..8a1cb613dd2e0 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7264.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7264.c @@ -547,6 +547,6 @@ static struct platform_device *sh7264_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7264_early_devices, + sh_early_platform_add_devices(sh7264_early_devices, ARRAY_SIZE(sh7264_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c index ef4ff76cbe9e1..8b1ef3028320c 100644 --- a/arch/sh/kernel/cpu/sh2a/setup-sh7269.c +++ b/arch/sh/kernel/cpu/sh2a/setup-sh7269.c @@ -563,6 +563,6 @@ static struct platform_device *sh7269_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7269_early_devices, + sh_early_platform_add_devices(sh7269_early_devices, ARRAY_SIZE(sh7269_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7705.c b/arch/sh/kernel/cpu/sh3/setup-sh7705.c index 9b34d1f4e797a..0544134b3f20c 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7705.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7705.c @@ -179,7 +179,7 @@ static struct platform_device *sh7705_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7705_early_devices, + sh_early_platform_add_devices(sh7705_early_devices, ARRAY_SIZE(sh7705_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh3/setup-sh770x.c b/arch/sh/kernel/cpu/sh3/setup-sh770x.c index 39f09504bd968..4947f57748bc1 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh770x.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh770x.c @@ -231,7 +231,7 @@ static struct platform_device *sh770x_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh770x_early_devices, + sh_early_platform_add_devices(sh770x_early_devices, ARRAY_SIZE(sh770x_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7710.c b/arch/sh/kernel/cpu/sh3/setup-sh7710.c index c006c8fcfa0fe..3819107615794 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7710.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7710.c @@ -178,7 +178,7 @@ static struct platform_device *sh7710_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7710_early_devices, + sh_early_platform_add_devices(sh7710_early_devices, ARRAY_SIZE(sh7710_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh3/setup-sh7720.c b/arch/sh/kernel/cpu/sh3/setup-sh7720.c index cf2eec9f6abd9..425d067dae9ba 100644 --- a/arch/sh/kernel/cpu/sh3/setup-sh7720.c +++ b/arch/sh/kernel/cpu/sh3/setup-sh7720.c @@ -212,7 +212,7 @@ static struct platform_device *sh7720_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7720_early_devices, + sh_early_platform_add_devices(sh7720_early_devices, ARRAY_SIZE(sh7720_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c index 2480ce4a3b1e9..e6737f3d0df25 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh4-202.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh4-202.c @@ -77,7 +77,7 @@ static struct platform_device *sh4202_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh4202_early_devices, + sh_early_platform_add_devices(sh4202_early_devices, ARRAY_SIZE(sh4202_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7750.c b/arch/sh/kernel/cpu/sh4/setup-sh7750.c index 4592d660b9426..19c8f1d69071c 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7750.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7750.c @@ -162,15 +162,15 @@ void __init plat_early_device_setup(void) if (mach_is_rts7751r2d()) { scif_platform_data.scscr |= SCSCR_CKE1; dev[0] = &scif_device; - early_platform_add_devices(dev, 1); + sh_early_platform_add_devices(dev, 1); } else { dev[0] = &sci_device; - early_platform_add_devices(dev, 1); + sh_early_platform_add_devices(dev, 1); dev[0] = &scif_device; - early_platform_add_devices(dev, 1); + sh_early_platform_add_devices(dev, 1); } - early_platform_add_devices(sh7750_early_devices, + sh_early_platform_add_devices(sh7750_early_devices, ARRAY_SIZE(sh7750_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4/setup-sh7760.c b/arch/sh/kernel/cpu/sh4/setup-sh7760.c index e64eb96e35892..14212f5d803cd 100644 --- a/arch/sh/kernel/cpu/sh4/setup-sh7760.c +++ b/arch/sh/kernel/cpu/sh4/setup-sh7760.c @@ -272,7 +272,7 @@ static struct platform_device *sh7760_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7760_early_devices, + sh_early_platform_add_devices(sh7760_early_devices, ARRAY_SIZE(sh7760_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c index 0fa56128bdca0..b6015188fab1f 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7343.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7343.c @@ -297,7 +297,7 @@ static struct platform_device *sh7343_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7343_early_devices, + sh_early_platform_add_devices(sh7343_early_devices, ARRAY_SIZE(sh7343_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c index 9f2f75285ec35..6676beef053e5 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7366.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7366.c @@ -241,7 +241,7 @@ static struct platform_device *sh7366_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7366_early_devices, + sh_early_platform_add_devices(sh7366_early_devices, ARRAY_SIZE(sh7366_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c index 8e5eaac789a73..0c6757ef63f42 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7722.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7722.c @@ -513,7 +513,7 @@ static struct platform_device *sh7722_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7722_early_devices, + sh_early_platform_add_devices(sh7722_early_devices, ARRAY_SIZE(sh7722_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c index b4b54aa0c2677..83ae1ad4a86e8 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c @@ -411,7 +411,7 @@ static struct platform_device *sh7723_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7723_early_devices, + sh_early_platform_add_devices(sh7723_early_devices, ARRAY_SIZE(sh7723_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c index ec237ab112cf7..0d990ab1ba2a9 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7724.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7724.c @@ -831,7 +831,7 @@ static struct platform_device *sh7724_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7724_early_devices, + sh_early_platform_add_devices(sh7724_early_devices, ARRAY_SIZE(sh7724_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c index 7ea9a8b439559..9911da794358d 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7734.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7734.c @@ -281,7 +281,7 @@ static struct platform_device *sh7734_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7734_early_devices, + sh_early_platform_add_devices(sh7734_early_devices, ARRAY_SIZE(sh7734_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c index 922d9886be181..67e330b7ea462 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7757.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7757.c @@ -768,7 +768,7 @@ static struct platform_device *sh7757_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7757_early_devices, + sh_early_platform_add_devices(sh7757_early_devices, ARRAY_SIZE(sh7757_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c index 8725d4751bafe..b0608664785f5 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7763.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7763.c @@ -222,7 +222,7 @@ static struct platform_device *sh7763_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7763_early_devices, + sh_early_platform_add_devices(sh7763_early_devices, ARRAY_SIZE(sh7763_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c index 035a0d4f2a2ec..5efec6ceb04d5 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7770.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7770.c @@ -317,7 +317,7 @@ static struct platform_device *sh7770_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7770_early_devices, + sh_early_platform_add_devices(sh7770_early_devices, ARRAY_SIZE(sh7770_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c index 983be87cb7be4..c818b788ecb05 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7780.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7780.c @@ -286,7 +286,7 @@ void __init plat_early_device_setup(void) scif1_platform_data.scscr &= ~SCSCR_CKE1; } - early_platform_add_devices(sh7780_early_devices, + sh_early_platform_add_devices(sh7780_early_devices, ARRAY_SIZE(sh7780_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c index bb0cb710c16e0..3b4a414d60a91 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7785.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7785.c @@ -354,7 +354,7 @@ static struct platform_device *sh7785_early_devices[] __initdata = { void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7785_early_devices, + sh_early_platform_add_devices(sh7785_early_devices, ARRAY_SIZE(sh7785_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c index b5d94366eed32..4b0db8259e3d7 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7786.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7786.c @@ -835,6 +835,6 @@ arch_initcall(sh7786_devices_setup); void __init plat_early_device_setup(void) { - early_platform_add_devices(sh7786_early_devices, + sh_early_platform_add_devices(sh7786_early_devices, ARRAY_SIZE(sh7786_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh4a/setup-shx3.c b/arch/sh/kernel/cpu/sh4a/setup-shx3.c index e82398d80b74e..7014d6d199b3e 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-shx3.c +++ b/arch/sh/kernel/cpu/sh4a/setup-shx3.c @@ -153,7 +153,7 @@ arch_initcall(shx3_devices_setup); void __init plat_early_device_setup(void) { - early_platform_add_devices(shx3_early_devices, + sh_early_platform_add_devices(shx3_early_devices, ARRAY_SIZE(shx3_early_devices)); } diff --git a/arch/sh/kernel/cpu/sh5/setup-sh5.c b/arch/sh/kernel/cpu/sh5/setup-sh5.c index 584a6aeeca261..dc8476d672440 100644 --- a/arch/sh/kernel/cpu/sh5/setup-sh5.c +++ b/arch/sh/kernel/cpu/sh5/setup-sh5.c @@ -116,6 +116,6 @@ arch_initcall(sh5_devices_setup); void __init plat_early_device_setup(void) { - early_platform_add_devices(sh5_early_devices, + sh_early_platform_add_devices(sh5_early_devices, ARRAY_SIZE(sh5_early_devices)); } diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index e535507851e1b..914174a125a43 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -329,7 +329,7 @@ void __init setup_arch(char **cmdline_p) sh_mv_setup(); /* Let earlyprintk output early console messages */ - early_platform_driver_probe("earlyprintk", 1, 1); + sh_early_platform_driver_probe("earlyprintk", 1, 1); #ifdef CONFIG_OF_FLATTREE #ifdef CONFIG_USE_BUILTIN_DTB diff --git a/arch/sh/kernel/time.c b/arch/sh/kernel/time.c index 8ad9fd5c4ff60..821a09cbd6054 100644 --- a/arch/sh/kernel/time.c +++ b/arch/sh/kernel/time.c @@ -31,8 +31,8 @@ static void __init sh_late_time_init(void) * clocksource and the jiffies clocksource is used transparently * instead. No error handling is necessary here. */ - early_platform_driver_register_all("earlytimer"); - early_platform_driver_probe("earlytimer", 2, 0); + sh_early_platform_driver_register_all("earlytimer"); + sh_early_platform_driver_probe("earlytimer", 2, 0); } void __init time_init(void) diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index 3a185485300e4..9cde50cb32200 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -1056,7 +1056,7 @@ static int sh_cmt_probe(struct platform_device *pdev) struct sh_cmt_device *cmt = platform_get_drvdata(pdev); int ret; - if (!is_early_platform_device(pdev)) { + if (!is_sh_early_platform_device(pdev)) { pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); } @@ -1076,7 +1076,7 @@ static int sh_cmt_probe(struct platform_device *pdev) pm_runtime_idle(&pdev->dev); return ret; } - if (is_early_platform_device(pdev)) + if (is_sh_early_platform_device(pdev)) return 0; out: @@ -1114,7 +1114,7 @@ static void __exit sh_cmt_exit(void) } #ifdef CONFIG_SUPERH -early_platform_init("earlytimer", &sh_cmt_device_driver); +sh_early_platform_init("earlytimer", &sh_cmt_device_driver); #endif subsys_initcall(sh_cmt_init); diff --git a/drivers/clocksource/sh_mtu2.c b/drivers/clocksource/sh_mtu2.c index d581060d33a26..c40eef091a04d 100644 --- a/drivers/clocksource/sh_mtu2.c +++ b/drivers/clocksource/sh_mtu2.c @@ -446,7 +446,7 @@ static int sh_mtu2_probe(struct platform_device *pdev) struct sh_mtu2_device *mtu = platform_get_drvdata(pdev); int ret; - if (!is_early_platform_device(pdev)) { + if (!is_sh_early_platform_device(pdev)) { pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); } @@ -466,7 +466,7 @@ static int sh_mtu2_probe(struct platform_device *pdev) pm_runtime_idle(&pdev->dev); return ret; } - if (is_early_platform_device(pdev)) + if (is_sh_early_platform_device(pdev)) return 0; out: @@ -516,7 +516,7 @@ static void __exit sh_mtu2_exit(void) } #ifdef CONFIG_SUPERH -early_platform_init("earlytimer", &sh_mtu2_device_driver); +sh_early_platform_init("earlytimer", &sh_mtu2_device_driver); #endif subsys_initcall(sh_mtu2_init); diff --git a/drivers/clocksource/sh_tmu.c b/drivers/clocksource/sh_tmu.c index 50e30ead050e3..d49690d155367 100644 --- a/drivers/clocksource/sh_tmu.c +++ b/drivers/clocksource/sh_tmu.c @@ -599,7 +599,7 @@ static int sh_tmu_probe(struct platform_device *pdev) struct sh_tmu_device *tmu = platform_get_drvdata(pdev); int ret; - if (!is_early_platform_device(pdev)) { + if (!is_sh_early_platform_device(pdev)) { pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); } @@ -620,7 +620,7 @@ static int sh_tmu_probe(struct platform_device *pdev) return ret; } - if (is_early_platform_device(pdev)) + if (is_sh_early_platform_device(pdev)) return 0; out: @@ -671,7 +671,7 @@ static void __exit sh_tmu_exit(void) } #ifdef CONFIG_SUPERH -early_platform_init("earlytimer", &sh_tmu_device_driver); +sh_early_platform_init("earlytimer", &sh_tmu_device_driver); #endif subsys_initcall(sh_tmu_init); diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 2a645fd7f07b4..970f1f86b945e 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -3318,7 +3318,7 @@ static int sci_probe(struct platform_device *dev) * to make it beyond this yet. */ #ifdef CONFIG_SUPERH - if (is_early_platform_device(dev)) + if (is_sh_early_platform_device(dev)) return sci_probe_earlyprintk(dev); #endif @@ -3416,7 +3416,7 @@ static void __exit sci_exit(void) } #if defined(CONFIG_SUPERH) && defined(CONFIG_SERIAL_SH_SCI_CONSOLE) -early_platform_init_buffer("earlyprintk", &sci_driver, +sh_early_platform_init_buffer("earlyprintk", &sci_driver, early_serial_buf, ARRAY_SIZE(early_serial_buf)); #endif #ifdef CONFIG_SERIAL_SH_SCI_EARLYCON diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 85aa28a041a9c..f086b6a1bc6b4 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -332,7 +332,7 @@ extern int platform_dma_configure(struct device *dev); * drivers. It should go away once we introduce the new platform_device-based * early driver framework. */ -static inline int is_early_platform_device(struct platform_device *pdev) +static inline int is_sh_early_platform_device(struct platform_device *pdev) { return 0; } From c31e73121f4c1ec45a3e523ac6ce3ce6dafdcec1 Mon Sep 17 00:00:00 2001 From: Murali Nalajala Date: Mon, 7 Oct 2019 13:37:42 -0700 Subject: [PATCH 09/71] base: soc: Handle custom soc information sysfs entries Soc framework exposed sysfs entries are not sufficient for some of the h/w platforms. Currently there is no interface where soc drivers can expose further information about their SoCs via soc framework. This change address this limitation where clients can pass their custom entries as attribute group and soc framework would expose them as sysfs properties. Signed-off-by: Murali Nalajala Reviewed-by: Bjorn Andersson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1570480662-25252-1-git-send-email-mnalajal@codeaurora.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/soc.c | 30 +++++++++++++++++------------- include/linux/sys_soc.h | 1 + 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/drivers/base/soc.c b/drivers/base/soc.c index 7c0c5ca5953d2..4af11a423475e 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -104,15 +104,12 @@ static const struct attribute_group soc_attr_group = { .is_visible = soc_attribute_mode, }; -static const struct attribute_group *soc_attr_groups[] = { - &soc_attr_group, - NULL, -}; - static void soc_release(struct device *dev) { struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); + kfree(soc_dev->dev.groups); kfree(soc_dev); } @@ -121,6 +118,7 @@ static struct soc_device_attribute *early_soc_dev_attr; struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr) { struct soc_device *soc_dev; + const struct attribute_group **soc_attr_groups; int ret; if (!soc_bus_type.p) { @@ -136,10 +134,18 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr goto out1; } + soc_attr_groups = kcalloc(3, sizeof(*soc_attr_groups), GFP_KERNEL); + if (!soc_attr_groups) { + ret = -ENOMEM; + goto out2; + } + soc_attr_groups[0] = &soc_attr_group; + soc_attr_groups[1] = soc_dev_attr->custom_attr_group; + /* Fetch a unique (reclaimable) SOC ID. */ ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL); if (ret < 0) - goto out2; + goto out3; soc_dev->soc_dev_num = ret; soc_dev->attr = soc_dev_attr; @@ -150,15 +156,15 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num); ret = device_register(&soc_dev->dev); - if (ret) - goto out3; + if (ret) { + put_device(&soc_dev->dev); + return ERR_PTR(ret); + } return soc_dev; out3: - ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); - put_device(&soc_dev->dev); - soc_dev = NULL; + kfree(soc_attr_groups); out2: kfree(soc_dev); out1: @@ -169,8 +175,6 @@ EXPORT_SYMBOL_GPL(soc_device_register); /* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */ void soc_device_unregister(struct soc_device *soc_dev) { - ida_simple_remove(&soc_ida, soc_dev->soc_dev_num); - device_unregister(&soc_dev->dev); early_soc_dev_attr = NULL; } diff --git a/include/linux/sys_soc.h b/include/linux/sys_soc.h index 48ceea867dd65..d9b3cf0f410c8 100644 --- a/include/linux/sys_soc.h +++ b/include/linux/sys_soc.h @@ -15,6 +15,7 @@ struct soc_device_attribute { const char *serial_number; const char *soc_id; const void *data; + const struct attribute_group *custom_attr_group; }; /** From ec4e29068839e00ff99ae94ccbb46123f0a0ba9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Wed, 9 Oct 2019 11:37:46 +0200 Subject: [PATCH 10/71] driver core: simplify definitions of platform_get_irq* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit platform_get_irq_optional is just a wrapper for __platform_get_irq. So rename __platform_get_irq to platform_get_irq_optional and drop platform_get_irq_optional's previous implementation. This way there is one function and one indirection less without loss of functionality. Signed-off-by: Uwe Kleine-König Reviewed-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20191009093746.12095-1-uwe@kleine-koenig.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 47 ++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/drivers/base/platform.c b/drivers/base/platform.c index be1e67ee3af67..b0263dcf068f0 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -80,7 +80,24 @@ void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); #endif /* CONFIG_HAS_IOMEM */ -static int __platform_get_irq(struct platform_device *dev, unsigned int num) +/** + * platform_get_irq_optional - get an optional IRQ for a device + * @dev: platform device + * @num: IRQ number index + * + * Gets an IRQ for a platform device. Device drivers should check the return + * value for errors so as to not pass a negative integer value to the + * request_irq() APIs. This is the same as platform_get_irq(), except that it + * does not print an error message if an IRQ can not be obtained. + * + * Example: + * int irq = platform_get_irq_optional(pdev, 0); + * if (irq < 0) + * return irq; + * + * Return: IRQ number on success, negative error number on failure. + */ +int platform_get_irq_optional(struct platform_device *dev, unsigned int num) { #ifdef CONFIG_SPARC /* sparc does not have irqs represented as IORESOURCE_IRQ resources */ @@ -144,6 +161,7 @@ static int __platform_get_irq(struct platform_device *dev, unsigned int num) return -ENXIO; #endif } +EXPORT_SYMBOL_GPL(platform_get_irq_optional); /** * platform_get_irq - get an IRQ for a device @@ -165,7 +183,7 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) { int ret; - ret = __platform_get_irq(dev, num); + ret = platform_get_irq_optional(dev, num); if (ret < 0 && ret != -EPROBE_DEFER) dev_err(&dev->dev, "IRQ index %u not found\n", num); @@ -173,29 +191,6 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) } EXPORT_SYMBOL_GPL(platform_get_irq); -/** - * platform_get_irq_optional - get an optional IRQ for a device - * @dev: platform device - * @num: IRQ number index - * - * Gets an IRQ for a platform device. Device drivers should check the return - * value for errors so as to not pass a negative integer value to the - * request_irq() APIs. This is the same as platform_get_irq(), except that it - * does not print an error message if an IRQ can not be obtained. - * - * Example: - * int irq = platform_get_irq_optional(pdev, 0); - * if (irq < 0) - * return irq; - * - * Return: IRQ number on success, negative error number on failure. - */ -int platform_get_irq_optional(struct platform_device *dev, unsigned int num) -{ - return __platform_get_irq(dev, num); -} -EXPORT_SYMBOL_GPL(platform_get_irq_optional); - /** * platform_irq_count - Count the number of IRQs a platform device uses * @dev: platform device @@ -206,7 +201,7 @@ int platform_irq_count(struct platform_device *dev) { int ret, nr = 0; - while ((ret = __platform_get_irq(dev, nr)) >= 0) + while ((ret = platform_get_irq_optional(dev, nr)) >= 0) nr++; if (ret == -EPROBE_DEFER) From 0c580d83325ea9a126ce6ae2996c4ff97acd2e40 Mon Sep 17 00:00:00 2001 From: Arkadiusz Drabczyk Date: Thu, 12 Sep 2019 22:56:06 +0200 Subject: [PATCH 11/71] firmware: Update pointer to documentation Documentation was revamped in 113ccc but link in firmware_loader/main.c hasn't been updated. Signed-off-by: Arkadiusz Drabczyk Link: https://lore.kernel.org/r/20190912205606.31095-1-arkadiusz@drabczyk.org Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index bf44c79beae92..d5995d78d694e 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -4,7 +4,7 @@ * * Copyright (c) 2003 Manuel Estrada Sainz * - * Please see Documentation/firmware_class/ for more information. + * Please see Documentation/driver-api/firmware/ for more information. * */ From 9655ac4aca20d654769b8f0d6a5be34b7ce7bad1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:24 +0200 Subject: [PATCH 12/71] debugfs: remove return value of debugfs_create_u8() No one checks the return value of debugfs_create_u8(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20191011132931.1186197-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 15 +++------------ include/linux/debugfs.h | 12 ++++-------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 9e27c843d00ea..c146f18eb8ac3 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -68,8 +68,8 @@ actually necessary; the debugfs code provides a number of helper functions for simple situations. Files containing a single integer value can be created with any of: - struct dentry *debugfs_create_u8(const char *name, umode_t mode, - struct dentry *parent, u8 *value); + void debugfs_create_u8(const char *name, umode_t mode, + struct dentry *parent, u8 *value); struct dentry *debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, u16 *value); struct dentry *debugfs_create_u32(const char *name, umode_t mode, diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 87846aad594b7..2d5d9a0a6f576 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -420,20 +420,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n"); * This function creates a file in debugfs with the given name that * contains the value of the variable @value. If the @mode variable is so * set, it can be read from, and written to. - * - * This function will return a pointer to a dentry if it succeeds. This - * pointer must be passed to the debugfs_remove() function when the file is - * to be removed (no automatic cleanup happens if your module is unloaded, - * you are responsible here.) If an error occurs, %ERR_PTR(-ERROR) will be - * returned. - * - * If debugfs is not enabled in the kernel, the value %ERR_PTR(-ENODEV) will - * be returned. */ -struct dentry *debugfs_create_u8(const char *name, umode_t mode, - struct dentry *parent, u8 *value) +void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent, + u8 *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8, + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8, &fops_u8_ro, &fops_u8_wo); } EXPORT_SYMBOL_GPL(debugfs_create_u8); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 58424eb3b3291..8e071f5992452 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -97,8 +97,8 @@ ssize_t debugfs_attr_write(struct file *file, const char __user *buf, struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, struct dentry *new_dir, const char *new_name); -struct dentry *debugfs_create_u8(const char *name, umode_t mode, - struct dentry *parent, u8 *value); +void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent, + u8 *value); struct dentry *debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, u16 *value); struct dentry *debugfs_create_u32(const char *name, umode_t mode, @@ -244,12 +244,8 @@ static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentr return ERR_PTR(-ENODEV); } -static inline struct dentry *debugfs_create_u8(const char *name, umode_t mode, - struct dentry *parent, - u8 *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_u8(const char *name, umode_t mode, + struct dentry *parent, u8 *value) { } static inline struct dentry *debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, From 313f5dbba41d905d59c820bb2d91ee6c661aff99 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:25 +0200 Subject: [PATCH 13/71] debugfs: remove return value of debugfs_create_u16() No one checks the return value of debugfs_create_u16(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20191011132931.1186197-2-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 15 +++------------ include/linux/debugfs.h | 12 ++++-------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index c146f18eb8ac3..6abcf41fbd379 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -70,8 +70,8 @@ created with any of: void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent, u8 *value); - struct dentry *debugfs_create_u16(const char *name, umode_t mode, - struct dentry *parent, u16 *value); + void debugfs_create_u16(const char *name, umode_t mode, + struct dentry *parent, u16 *value); struct dentry *debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent, u32 *value); struct dentry *debugfs_create_u64(const char *name, umode_t mode, diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 2d5d9a0a6f576..8091a7ef35891 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -456,20 +456,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n"); * This function creates a file in debugfs with the given name that * contains the value of the variable @value. If the @mode variable is so * set, it can be read from, and written to. - * - * This function will return a pointer to a dentry if it succeeds. This - * pointer must be passed to the debugfs_remove() function when the file is - * to be removed (no automatic cleanup happens if your module is unloaded, - * you are responsible here.) If an error occurs, %ERR_PTR(-ERROR) will be - * returned. - * - * If debugfs is not enabled in the kernel, the value %ERR_PTR(-ENODEV) will - * be returned. */ -struct dentry *debugfs_create_u16(const char *name, umode_t mode, - struct dentry *parent, u16 *value) +void debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, + u16 *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16, + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16, &fops_u16_ro, &fops_u16_wo); } EXPORT_SYMBOL_GPL(debugfs_create_u16); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 8e071f5992452..c83a33a76b6c9 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -99,8 +99,8 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent, u8 *value); -struct dentry *debugfs_create_u16(const char *name, umode_t mode, - struct dentry *parent, u16 *value); +void debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, + u16 *value); struct dentry *debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent, u32 *value); struct dentry *debugfs_create_u64(const char *name, umode_t mode, @@ -247,12 +247,8 @@ static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentr static inline void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent, u8 *value) { } -static inline struct dentry *debugfs_create_u16(const char *name, umode_t mode, - struct dentry *parent, - u16 *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_u16(const char *name, umode_t mode, + struct dentry *parent, u16 *value) { } static inline struct dentry *debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent, From ad26221fb9e64e69e32a2caf58dba067ca4e815e Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:26 +0200 Subject: [PATCH 14/71] debugfs: remove return value of debugfs_create_u64() No one checks the return value of debugfs_create_u64(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20191011132931.1186197-3-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 15 +++------------ include/linux/debugfs.h | 12 ++++-------- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 6abcf41fbd379..1b299ae4904dc 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -74,8 +74,8 @@ created with any of: struct dentry *parent, u16 *value); struct dentry *debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent, u32 *value); - struct dentry *debugfs_create_u64(const char *name, umode_t mode, - struct dentry *parent, u64 *value); + void debugfs_create_u64(const char *name, umode_t mode, + struct dentry *parent, u64 *value); These files support both reading and writing the given value; if a specific file should not be written to, simply set the mode bits accordingly. The diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 8091a7ef35891..4c8912d7d34ce 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -538,20 +538,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n"); * This function creates a file in debugfs with the given name that * contains the value of the variable @value. If the @mode variable is so * set, it can be read from, and written to. - * - * This function will return a pointer to a dentry if it succeeds. This - * pointer must be passed to the debugfs_remove() function when the file is - * to be removed (no automatic cleanup happens if your module is unloaded, - * you are responsible here.) If an error occurs, %ERR_PTR(-ERROR) will be - * returned. - * - * If debugfs is not enabled in the kernel, the value %ERR_PTR(-ENODEV) will - * be returned. */ -struct dentry *debugfs_create_u64(const char *name, umode_t mode, - struct dentry *parent, u64 *value) +void debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent, + u64 *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64, + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64, &fops_u64_ro, &fops_u64_wo); } EXPORT_SYMBOL_GPL(debugfs_create_u64); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index c83a33a76b6c9..b3bed4d61733a 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -103,8 +103,8 @@ void debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent, u16 *value); struct dentry *debugfs_create_u32(const char *name, umode_t mode, struct dentry *parent, u32 *value); -struct dentry *debugfs_create_u64(const char *name, umode_t mode, - struct dentry *parent, u64 *value); +void debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent, + u64 *value); struct dentry *debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent, unsigned long *value); struct dentry *debugfs_create_x8(const char *name, umode_t mode, @@ -257,12 +257,8 @@ static inline struct dentry *debugfs_create_u32(const char *name, umode_t mode, return ERR_PTR(-ENODEV); } -static inline struct dentry *debugfs_create_u64(const char *name, umode_t mode, - struct dentry *parent, - u64 *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_u64(const char *name, umode_t mode, + struct dentry *parent, u64 *value) { } static inline struct dentry *debugfs_create_ulong(const char *name, umode_t mode, From 8e5802635f0f9f7329ec8ffdec15479946c99fb1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:27 +0200 Subject: [PATCH 15/71] debugfs: remove return value of debugfs_create_size_t() No one checks the return value of debugfs_create_size_t(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20191011132931.1186197-4-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 5 ++--- fs/debugfs/file.c | 9 ++++----- include/linux/debugfs.h | 13 +++++-------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 1b299ae4904dc..55a5da08c71c2 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -96,9 +96,8 @@ value to be exported. Some types can have different widths on different architectures, though, complicating the situation somewhat. There is a function meant to help out in one special case: - struct dentry *debugfs_create_size_t(const char *name, umode_t mode, - struct dentry *parent, - size_t *value); + void debugfs_create_size_t(const char *name, umode_t mode, + struct dentry *parent, size_t *value); As might be expected, this function will create a debugfs file to represent a variable of type size_t. diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 4c8912d7d34ce..dfeda13023b3f 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -721,12 +721,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n"); * @value: a pointer to the variable that the file should read to and write * from. */ -struct dentry *debugfs_create_size_t(const char *name, umode_t mode, - struct dentry *parent, size_t *value) +void debugfs_create_size_t(const char *name, umode_t mode, + struct dentry *parent, size_t *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, - &fops_size_t, &fops_size_t_ro, - &fops_size_t_wo); + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_size_t, + &fops_size_t_ro, &fops_size_t_wo); } EXPORT_SYMBOL_GPL(debugfs_create_size_t); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index b3bed4d61733a..1d859bc657bd3 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -115,8 +115,8 @@ struct dentry *debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, u32 *value); struct dentry *debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, u64 *value); -struct dentry *debugfs_create_size_t(const char *name, umode_t mode, - struct dentry *parent, size_t *value); +void debugfs_create_size_t(const char *name, umode_t mode, + struct dentry *parent, size_t *value); struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode, struct dentry *parent, atomic_t *value); struct dentry *debugfs_create_bool(const char *name, umode_t mode, @@ -296,12 +296,9 @@ static inline struct dentry *debugfs_create_x64(const char *name, umode_t mode, return ERR_PTR(-ENODEV); } -static inline struct dentry *debugfs_create_size_t(const char *name, umode_t mode, - struct dentry *parent, - size_t *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_size_t(const char *name, umode_t mode, + struct dentry *parent, size_t *value) +{ } static inline struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode, struct dentry *parent, atomic_t *value) From be5767341ce56a6a7adf9df3fd07cf8a0611a5d0 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:19:19 +0200 Subject: [PATCH 16/71] ntb: ntb_pingpong: no need to check the return value of debugfs calls There is no need to check the return value of debugfs_create_atomic_t as nothing happens with the error. Also, the code will never return NULL, so this check has never caught anything :) Fix this by removing the check entirely. Cc: Jon Mason Cc: Dave Jiang Cc: Allen Hubbe Cc: linux-ntb@googlegroups.com Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20191011131919.GA1174815@kroah.com Signed-off-by: Greg Kroah-Hartman --- drivers/ntb/test/ntb_pingpong.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/ntb/test/ntb_pingpong.c b/drivers/ntb/test/ntb_pingpong.c index 65865e460ab87..04dd46647db36 100644 --- a/drivers/ntb/test/ntb_pingpong.c +++ b/drivers/ntb/test/ntb_pingpong.c @@ -354,13 +354,10 @@ static void pp_clear_ctx(struct pp_ctx *pp) static void pp_setup_dbgfs(struct pp_ctx *pp) { struct pci_dev *pdev = pp->ntb->pdev; - void *ret; pp->dbgfs_dir = debugfs_create_dir(pci_name(pdev), pp_dbgfs_topdir); - ret = debugfs_create_atomic_t("count", 0600, pp->dbgfs_dir, &pp->count); - if (!ret) - dev_warn(&pp->ntb->dev, "DebugFS unsupported\n"); + debugfs_create_atomic_t("count", 0600, pp->dbgfs_dir, &pp->count); } static void pp_clear_dbgfs(struct pp_ctx *pp) From e40d38f28c10e3010b2828f2c737b50fb81bda8f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:29 +0200 Subject: [PATCH 17/71] debugfs: remove return value of debugfs_create_x16() No one checks the return value of debugfs_create_x16(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Link: https://lore.kernel.org/r/20191011132931.1186197-6-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 6 +++--- include/linux/debugfs.h | 12 ++++-------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 55a5da08c71c2..4967f77a62a02 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -84,8 +84,8 @@ the following functions can be used instead: struct dentry *debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, u8 *value); - struct dentry *debugfs_create_x16(const char *name, umode_t mode, - struct dentry *parent, u16 *value); + void debugfs_create_x16(const char *name, umode_t mode, + struct dentry *parent, u16 *value); struct dentry *debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, u32 *value); struct dentry *debugfs_create_x64(const char *name, umode_t mode, diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index dfeda13023b3f..cc6c76702e659 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -651,10 +651,10 @@ EXPORT_SYMBOL_GPL(debugfs_create_x8); * @value: a pointer to the variable that the file should read to and write * from. */ -struct dentry *debugfs_create_x16(const char *name, umode_t mode, - struct dentry *parent, u16 *value) +void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, + u16 *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16, + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16, &fops_x16_ro, &fops_x16_wo); } EXPORT_SYMBOL_GPL(debugfs_create_x16); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 1d859bc657bd3..e742081c68448 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -109,8 +109,8 @@ struct dentry *debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent, unsigned long *value); struct dentry *debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, u8 *value); -struct dentry *debugfs_create_x16(const char *name, umode_t mode, - struct dentry *parent, u16 *value); +void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, + u16 *value); struct dentry *debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, u32 *value); struct dentry *debugfs_create_x64(const char *name, umode_t mode, @@ -275,12 +275,8 @@ static inline struct dentry *debugfs_create_x8(const char *name, umode_t mode, return ERR_PTR(-ENODEV); } -static inline struct dentry *debugfs_create_x16(const char *name, umode_t mode, - struct dentry *parent, - u16 *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_x16(const char *name, umode_t mode, + struct dentry *parent, u16 *value) { } static inline struct dentry *debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, From f5cb0a7e64f41b6f1c5cacc64a476962f5e97f91 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:30 +0200 Subject: [PATCH 18/71] debugfs: remove return value of debugfs_create_x32() No one checks the return value of debugfs_create_x32(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Link: https://lore.kernel.org/r/20191011132931.1186197-7-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 6 +++--- include/linux/debugfs.h | 12 ++++-------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 4967f77a62a02..94b8f9a0cb5e5 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -86,8 +86,8 @@ the following functions can be used instead: struct dentry *parent, u8 *value); void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value); - struct dentry *debugfs_create_x32(const char *name, umode_t mode, - struct dentry *parent, u32 *value); + void debugfs_create_x32(const char *name, umode_t mode, + struct dentry *parent, u32 *value); struct dentry *debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, u64 *value); diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index cc6c76702e659..9c906251b3c6b 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -669,10 +669,10 @@ EXPORT_SYMBOL_GPL(debugfs_create_x16); * @value: a pointer to the variable that the file should read to and write * from. */ -struct dentry *debugfs_create_x32(const char *name, umode_t mode, - struct dentry *parent, u32 *value) +void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, + u32 *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32, + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32, &fops_x32_ro, &fops_x32_wo); } EXPORT_SYMBOL_GPL(debugfs_create_x32); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index e742081c68448..89d0b02f82f52 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -111,8 +111,8 @@ struct dentry *debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, u8 *value); void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value); -struct dentry *debugfs_create_x32(const char *name, umode_t mode, - struct dentry *parent, u32 *value); +void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, + u32 *value); struct dentry *debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, u64 *value); void debugfs_create_size_t(const char *name, umode_t mode, @@ -278,12 +278,8 @@ static inline struct dentry *debugfs_create_x8(const char *name, umode_t mode, static inline void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value) { } -static inline struct dentry *debugfs_create_x32(const char *name, umode_t mode, - struct dentry *parent, - u32 *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_x32(const char *name, umode_t mode, + struct dentry *parent, u32 *value) { } static inline struct dentry *debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, From 0864c408fb1e5b02d817cc8cd5b794d4cb491d50 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:31 +0200 Subject: [PATCH 19/71] debugfs: remove return value of debugfs_create_x64() No one checks the return value of debugfs_create_x64(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Link: https://lore.kernel.org/r/20191011132931.1186197-8-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 6 +++--- include/linux/debugfs.h | 12 ++++-------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 94b8f9a0cb5e5..9e705026ac103 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -88,8 +88,8 @@ the following functions can be used instead: struct dentry *parent, u16 *value); void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, u32 *value); - struct dentry *debugfs_create_x64(const char *name, umode_t mode, - struct dentry *parent, u64 *value); + void debugfs_create_x64(const char *name, umode_t mode, + struct dentry *parent, u64 *value); These functions are useful as long as the developer knows the size of the value to be exported. Some types can have different widths on different diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 9c906251b3c6b..3ff736f22eb63 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -687,10 +687,10 @@ EXPORT_SYMBOL_GPL(debugfs_create_x32); * @value: a pointer to the variable that the file should read to and write * from. */ -struct dentry *debugfs_create_x64(const char *name, umode_t mode, - struct dentry *parent, u64 *value) +void debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, + u64 *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64, + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64, &fops_x64_ro, &fops_x64_wo); } EXPORT_SYMBOL_GPL(debugfs_create_x64); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 89d0b02f82f52..33690949b45d6 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -113,8 +113,8 @@ void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value); void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, u32 *value); -struct dentry *debugfs_create_x64(const char *name, umode_t mode, - struct dentry *parent, u64 *value); +void debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, + u64 *value); void debugfs_create_size_t(const char *name, umode_t mode, struct dentry *parent, size_t *value); struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode, @@ -281,12 +281,8 @@ static inline void debugfs_create_x16(const char *name, umode_t mode, static inline void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, u32 *value) { } -static inline struct dentry *debugfs_create_x64(const char *name, umode_t mode, - struct dentry *parent, - u64 *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_x64(const char *name, umode_t mode, + struct dentry *parent, u64 *value) { } static inline void debugfs_create_size_t(const char *name, umode_t mode, struct dentry *parent, size_t *value) From af1b967af5ffb94aaed5b9b3259349cc2d398fa7 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 11 Oct 2019 12:15:19 -0700 Subject: [PATCH 20/71] of: property: Minor code formatting/style clean ups Better variable and function names. Remove "," after the sentinel in an array initialization list. Signed-off-by: Saravana Kannan Acked-by: Rob Herring Link: https://lore.kernel.org/r/20191011191521.179614-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/property.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/of/property.c b/drivers/of/property.c index 923d6f88a99cb..6f6e1d9644cfe 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1147,11 +1147,11 @@ struct supplier_bindings { const char *prop_name, int index); }; -static const struct supplier_bindings bindings[] = { +static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_clocks, }, { .parse_prop = parse_interconnects, }, { .parse_prop = parse_regulators, }, - {}, + {} }; /** @@ -1177,7 +1177,7 @@ static int of_link_property(struct device *dev, struct device_node *con_np, const char *prop_name) { struct device_node *phandle; - const struct supplier_bindings *s = bindings; + const struct supplier_bindings *s = of_supplier_bindings; unsigned int i = 0; bool matched = false; int ret = 0; @@ -1196,7 +1196,7 @@ static int of_link_property(struct device *dev, struct device_node *con_np, return ret; } -static int __of_link_to_suppliers(struct device *dev, +static int of_link_to_suppliers(struct device *dev, struct device_node *con_np) { struct device_node *child; @@ -1208,7 +1208,7 @@ static int __of_link_to_suppliers(struct device *dev, ret = -EAGAIN; for_each_child_of_node(con_np, child) - if (__of_link_to_suppliers(dev, child)) + if (of_link_to_suppliers(dev, child)) ret = -EAGAIN; return ret; @@ -1226,7 +1226,7 @@ static int of_fwnode_add_links(const struct fwnode_handle *fwnode, if (unlikely(!is_of_node(fwnode))) return 0; - return __of_link_to_suppliers(dev, to_of_node(fwnode)); + return of_link_to_suppliers(dev, to_of_node(fwnode)); } const struct fwnode_operations of_fwnode_ops = { From 92df01e3601fe29eb3727a82705eafa6209053f5 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 11 Oct 2019 12:15:20 -0700 Subject: [PATCH 21/71] driver: core: Improve documentation for fwnode_operations.add_links() The add_links() ops shouldn't return on the first failed device link add. It needs to continue trying to add device links to other suppliers that are available. The documentation didn't explain WHY this behavior is necessary. So, update the documentation with an example that explains why this is necessary. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191011191521.179614-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- include/linux/fwnode.h | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 6ae05b9ce3597..97223e2410bd2 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -71,8 +71,25 @@ struct fwnode_reference_args { * links to all the suppliers of the device that are available at * the time this function is called. The function must NOT stop * at the first failed device link if other unlinked supplier - * devices are present in the system. If some suppliers are not - * yet available, this function will be called again when other + * devices are present in the system. This is necessary for the + * driver/bus sync_state() callbacks to work correctly. + * + * For example, say Device-C depends on suppliers Device-S1 and + * Device-S2 and the dependency is listed in that order in the + * firmware. Say, S1 gets populated from the firmware after + * late_initcall_sync(). Say S2 is populated and probed way + * before that in device_initcall(). When C is populated, if this + * add_links() function doesn't continue past a "failed linking to + * S1" and continue linking C to S2, then S2 will get a + * sync_state() callback before C is probed. This is because from + * the perspective of S2, C was never a consumer when its + * sync_state() evaluation is done. To avoid this, the add_links() + * function has to go through all available suppliers of the + * device (that corresponds to this fwnode) and link to them + * before returning. + * + * If some suppliers are not yet available (indicated by an error + * return value), this function will be called again when other * devices are added to allow creating device links to any newly * available suppliers. * From a3caeb8ffe5d2bbe01da66081f0ef28c26302d99 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Fri, 11 Oct 2019 12:15:21 -0700 Subject: [PATCH 22/71] docs: driver-model: Add documentation for sync_state The sync_state() driver callback was added recently, but the documentation was missing. Adding it now. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191011191521.179614-4-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- .../driver-api/driver-model/driver.rst | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/Documentation/driver-api/driver-model/driver.rst b/Documentation/driver-api/driver-model/driver.rst index 11d281506a041..baa6a85c82871 100644 --- a/Documentation/driver-api/driver-model/driver.rst +++ b/Documentation/driver-api/driver-model/driver.rst @@ -169,6 +169,49 @@ A driver's probe() may return a negative errno value to indicate that the driver did not bind to this device, in which case it should have released all resources it allocated:: + void (*sync_state)(struct device *dev); + +sync_state is called only once for a device. It's called when all the consumer +devices of the device have successfully probed. The list of consumers of the +device is obtained by looking at the device links connecting that device to its +consumer devices. + +The first attempt to call sync_state() is made during late_initcall_sync() to +give firmware and drivers time to link devices to each other. During the first +attempt at calling sync_state(), if all the consumers of the device at that +point in time have already probed successfully, sync_state() is called right +away. If there are no consumers of the device during the first attempt, that +too is considered as "all consumers of the device have probed" and sync_state() +is called right away. + +If during the first attempt at calling sync_state() for a device, there are +still consumers that haven't probed successfully, the sync_state() call is +postponed and reattempted in the future only when one or more consumers of the +device probe successfully. If during the reattempt, the driver core finds that +there are one or more consumers of the device that haven't probed yet, then +sync_state() call is postponed again. + +A typical use case for sync_state() is to have the kernel cleanly take over +management of devices from the bootloader. For example, if a device is left on +and at a particular hardware configuration by the bootloader, the device's +driver might need to keep the device in the boot configuration until all the +consumers of the device have probed. Once all the consumers of the device have +probed, the device's driver can synchronize the hardware state of the device to +match the aggregated software state requested by all the consumers. Hence the +name sync_state(). + +While obvious examples of resources that can benefit from sync_state() include +resources such as regulator, sync_state() can also be useful for complex +resources like IOMMUs. For example, IOMMUs with multiple consumers (devices +whose addresses are remapped by the IOMMU) might need to keep their mappings +fixed at (or additive to) the boot configuration until all its consumers have +probed. + +While the typical use case for sync_state() is to have the kernel cleanly take +over management of devices from the bootloader, the usage of sync_state() is +not restricted to that. Use it whenever it makes sense to take an action after +all the consumers of a device have probed. + int (*remove) (struct device *dev); remove is called to unbind a driver from a device. This may be From 4a6a6f5c4aeedb72db871d60bfcca89835f317aa Mon Sep 17 00:00:00 2001 From: Shuah Khan Date: Thu, 26 Sep 2019 19:16:41 -0600 Subject: [PATCH 23/71] tools: gpio: Use !building_out_of_srctree to determine srctree make TARGETS=gpio kselftest fails with: Makefile:23: tools/build/Makefile.include: No such file or directory When the gpio tool make is invoked from tools Makefile, srctree is cleared and the current logic check for srctree equals to empty string to determine srctree location from CURDIR. When the build in invoked from selftests/gpio Makefile, the srctree is set to "." and the same logic used for srctree equals to empty is needed to determine srctree. Check building_out_of_srctree undefined as the condition for both cases to fix "make TARGETS=gpio kselftest" build failure. Cc: stable@vger.kernel.org Signed-off-by: Shuah Khan Signed-off-by: Bartosz Golaszewski --- tools/gpio/Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/gpio/Makefile b/tools/gpio/Makefile index 6ecdd10678260..1178d302757ea 100644 --- a/tools/gpio/Makefile +++ b/tools/gpio/Makefile @@ -3,7 +3,11 @@ include ../scripts/Makefile.include bindir ?= /usr/bin -ifeq ($(srctree),) +# This will work when gpio is built in tools env. where srctree +# isn't set and when invoked from selftests build, where srctree +# is set to ".". building_out_of_srctree is undefined for in srctree +# builds +ifndef building_out_of_srctree srctree := $(patsubst %/,%,$(dir $(CURDIR))) srctree := $(patsubst %/,%,$(dir $(srctree))) endif From 05ef983e0d65a31b370a4e1b93c1efd490ae778f Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 28 Oct 2019 15:00:22 -0700 Subject: [PATCH 24/71] driver core: Add device link support for SYNC_STATE_ONLY flag Parent devices might need to create "proxy" device links from themselves to supplier devices to make sure the supplier devices don't get a sync_state() before the child consumer devices get a chance to add device links to the supplier devices. However, the parent device has no real dependency on the supplier device and probing, suspend/resume or runtime PM don't need to be affected by the supplier device. To capture these cases, create a SYNC_STATE_ONLY device link flag that only affects sync_state() behavior and doesn't affect probing, suspend/resume or runtime PM. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191028220027.251605-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 50 ++++++++++++++++++++++++++++++++++-------- include/linux/device.h | 2 ++ 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 7ea665a97da2d..17ed054c41325 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -131,6 +131,9 @@ static 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)) + continue; + if (link->consumer == target) return 1; @@ -200,8 +203,11 @@ static int device_reorder_to_tail(struct device *dev, void *not_used) device_pm_move_last(dev); device_for_each_child(dev, NULL, device_reorder_to_tail); - list_for_each_entry(link, &dev->links.consumers, s_node) + list_for_each_entry(link, &dev->links.consumers, s_node) { + if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED)) + continue; device_reorder_to_tail(link->consumer, NULL); + } return 0; } @@ -228,7 +234,8 @@ void device_pm_move_to_tail(struct device *dev) #define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \ DL_FLAG_AUTOREMOVE_SUPPLIER | \ - DL_FLAG_AUTOPROBE_CONSUMER) + DL_FLAG_AUTOPROBE_CONSUMER | \ + DL_FLAG_SYNC_STATE_ONLY) #define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \ DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE) @@ -296,6 +303,8 @@ 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_AUTOPROBE_CONSUMER && flags & (DL_FLAG_AUTOREMOVE_CONSUMER | DL_FLAG_AUTOREMOVE_SUPPLIER))) @@ -316,11 +325,14 @@ struct device_link *device_link_add(struct device *consumer, /* * If the supplier has not been fully registered yet or there is a - * reverse dependency between the consumer and the supplier already in - * the graph, return NULL. + * reverse (non-SYNC_STATE_ONLY) dependency between the consumer and + * the supplier already in the graph, return NULL. If the link is a + * SYNC_STATE_ONLY link, we don't check for reverse dependencies + * because it only affects sync_state() callbacks. */ if (!device_pm_initialized(supplier) - || device_is_dependent(consumer, supplier)) { + || (!(flags & DL_FLAG_SYNC_STATE_ONLY) && + device_is_dependent(consumer, supplier))) { link = NULL; goto out; } @@ -347,9 +359,14 @@ struct device_link *device_link_add(struct device *consumer, } if (flags & DL_FLAG_STATELESS) { - link->flags |= DL_FLAG_STATELESS; kref_get(&link->kref); - goto out; + if (link->flags & DL_FLAG_SYNC_STATE_ONLY && + !(link->flags & DL_FLAG_STATELESS)) { + link->flags |= DL_FLAG_STATELESS; + goto reorder; + } else { + goto out; + } } /* @@ -371,6 +388,12 @@ struct device_link *device_link_add(struct device *consumer, link->flags |= DL_FLAG_MANAGED; device_link_init_status(link, consumer, supplier); } + if (link->flags & DL_FLAG_SYNC_STATE_ONLY && + !(flags & DL_FLAG_SYNC_STATE_ONLY)) { + link->flags &= ~DL_FLAG_SYNC_STATE_ONLY; + goto reorder; + } + goto out; } @@ -410,6 +433,13 @@ struct device_link *device_link_add(struct device *consumer, flags & DL_FLAG_PM_RUNTIME) pm_runtime_resume(supplier); + if (flags & DL_FLAG_SYNC_STATE_ONLY) { + dev_dbg(consumer, + "Linked as a sync state only consumer to %s\n", + dev_name(supplier)); + goto out; + } +reorder: /* * Move the consumer and all of the devices depending on it to the end * of dpm_list and the devices_kset list. @@ -635,7 +665,8 @@ int device_links_check_suppliers(struct device *dev) device_links_write_lock(); list_for_each_entry(link, &dev->links.suppliers, c_node) { - if (!(link->flags & DL_FLAG_MANAGED)) + if (!(link->flags & DL_FLAG_MANAGED) || + link->flags & DL_FLAG_SYNC_STATE_ONLY) continue; if (link->status != DL_STATE_AVAILABLE) { @@ -949,7 +980,8 @@ void device_links_unbind_consumers(struct device *dev) list_for_each_entry(link, &dev->links.consumers, s_node) { enum device_link_state status; - if (!(link->flags & DL_FLAG_MANAGED)) + if (!(link->flags & DL_FLAG_MANAGED) || + link->flags & DL_FLAG_SYNC_STATE_ONLY) continue; status = link->status; diff --git a/include/linux/device.h b/include/linux/device.h index 6978bb471567a..82890e1b8f08d 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1096,6 +1096,7 @@ enum device_link_state { * AUTOREMOVE_SUPPLIER: Remove the link automatically on supplier driver unbind. * 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. */ #define DL_FLAG_STATELESS BIT(0) #define DL_FLAG_AUTOREMOVE_CONSUMER BIT(1) @@ -1104,6 +1105,7 @@ enum device_link_state { #define DL_FLAG_AUTOREMOVE_SUPPLIER BIT(4) #define DL_FLAG_AUTOPROBE_CONSUMER BIT(5) #define DL_FLAG_MANAGED BIT(6) +#define DL_FLAG_SYNC_STATE_ONLY BIT(7) /** * struct device_link - Device link representation. From bcbbcfd57247f4c2976055995e5760fb576aae1e Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 28 Oct 2019 15:00:23 -0700 Subject: [PATCH 25/71] driver core: Allow a device to wait on optional suppliers Before this change, if a device is waiting on suppliers, it's assumed that all those suppliers are needed for the device to probe successfully. This change allows marking a devices as waiting only on optional suppliers. This allows a device to wait on suppliers (and link to them as soon as they are available) without preventing the device from being probed. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191028220027.251605-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 28 +++++++++++++++++++++++++--- include/linux/device.h | 3 +++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 17ed054c41325..48cd43a91ce6a 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -480,13 +480,25 @@ EXPORT_SYMBOL_GPL(device_link_add); * This function is NOT meant to be called from the probe function of the * consumer but rather from code that creates/adds the consumer device. */ -static void device_link_wait_for_supplier(struct device *consumer) +static void device_link_wait_for_supplier(struct device *consumer, + bool need_for_probe) { mutex_lock(&wfs_lock); list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); + consumer->links.need_for_probe = need_for_probe; mutex_unlock(&wfs_lock); } +static void device_link_wait_for_mandatory_supplier(struct device *consumer) +{ + device_link_wait_for_supplier(consumer, true); +} + +static void device_link_wait_for_optional_supplier(struct device *consumer) +{ + device_link_wait_for_supplier(consumer, false); +} + /** * device_link_add_missing_supplier_links - Add links from consumer devices to * supplier devices, leaving any @@ -656,7 +668,8 @@ int device_links_check_suppliers(struct device *dev) * probe. */ mutex_lock(&wfs_lock); - if (!list_empty(&dev->links.needs_suppliers)) { + if (!list_empty(&dev->links.needs_suppliers) && + dev->links.need_for_probe) { mutex_unlock(&wfs_lock); return -EPROBE_DEFER; } @@ -760,6 +773,15 @@ void device_links_driver_bound(struct device *dev) { struct device_link *link; + /* + * If a device probes 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. + */ + mutex_lock(&wfs_lock); + list_del_init(&dev->links.needs_suppliers); + mutex_unlock(&wfs_lock); + device_links_write_lock(); list_for_each_entry(link, &dev->links.consumers, s_node) { @@ -2393,7 +2415,7 @@ int device_add(struct device *dev) if (fwnode_has_op(dev->fwnode, add_links) && fwnode_call_int_op(dev->fwnode, add_links, dev)) - device_link_wait_for_supplier(dev); + device_link_wait_for_mandatory_supplier(dev, true); bus_probe_device(dev); if (parent) diff --git a/include/linux/device.h b/include/linux/device.h index 82890e1b8f08d..d1bcc8f122f69 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1155,6 +1155,8 @@ enum dl_dev_state { * @consumers: List of links to consumer devices. * @needs_suppliers: Hook to global list of devices waiting for suppliers. * @defer_sync: Hook to global list of devices that have deferred sync_state. + * @need_for_probe: If needs_suppliers is on a list, this indicates if the + * suppliers are needed for probe or not. * @status: Driver status information. */ struct dev_links_info { @@ -1162,6 +1164,7 @@ struct dev_links_info { struct list_head consumers; struct list_head needs_suppliers; struct list_head defer_sync; + bool need_for_probe; enum dl_dev_state status; }; From 03324507e66c7664c754b1ef92c5c3be24c78aa2 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 28 Oct 2019 15:00:24 -0700 Subject: [PATCH 26/71] driver core: Allow fwnode_operations.add_links to differentiate errors When add_links() still has suppliers that it needs to link to in the future, this patch allows it to differentiate between suppliers that are needed for probing vs suppliers that are needed for sync_state() correctness. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191028220027.251605-4-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 12 ++++++++---- include/linux/fwnode.h | 13 +++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 48cd43a91ce6a..e6d3e6d485daa 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2297,7 +2297,7 @@ int device_add(struct device *dev) struct device *parent; struct kobject *kobj; struct class_interface *class_intf; - int error = -EINVAL; + int error = -EINVAL, fw_ret; struct kobject *glue_dir = NULL; dev = get_device(dev); @@ -2413,9 +2413,13 @@ int device_add(struct device *dev) */ device_link_add_missing_supplier_links(); - if (fwnode_has_op(dev->fwnode, add_links) - && fwnode_call_int_op(dev->fwnode, add_links, dev)) - device_link_wait_for_mandatory_supplier(dev, true); + if (fwnode_has_op(dev->fwnode, add_links)) { + fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev); + if (fw_ret == -ENODEV) + device_link_wait_for_mandatory_supplier(dev); + else if (fw_ret) + device_link_wait_for_optional_supplier(dev); + } bus_probe_device(dev); if (parent) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 97223e2410bd2..766ff9bb58760 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -94,10 +94,15 @@ struct fwnode_reference_args { * available suppliers. * * Return 0 if device links have been successfully created to all - * the suppliers of this device or if the supplier information is - * not known. Return an error if and only if the supplier - * information is known but some of the suppliers are not yet - * available to create device links to. + * the suppliers this device needs to create device links to or if + * the supplier information is not known. + * + * Return -ENODEV if and only if the suppliers needed for probing + * the device are not yet available to create device links to. + * + * Return -EAGAIN if there are suppliers that need to be linked to + * that are not yet available but none of those suppliers are + * necessary for probing this device. */ struct fwnode_operations { struct fwnode_handle *(*get)(struct fwnode_handle *fwnode); From 0ff5cc1ec33b4b7540c4bc09f50123befc6ed947 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 28 Oct 2019 15:00:25 -0700 Subject: [PATCH 27/71] of: property: Make sure child dependencies don't block probing of parent When creating device links to proxy the sync_state() needs of child dependencies, create SYNC_STATE_ONLY device links so that children dependencies don't block probing of the parent. Also, differentiate between missing suppliers of parent device vs missing suppliers of child devices so that driver core doesn't block parent device probing when only child supplier dependencies are missing. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191028220027.251605-5-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/property.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/of/property.c b/drivers/of/property.c index 6f6e1d9644cfe..69a6ec8711bd0 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1018,10 +1018,10 @@ static bool of_is_ancestor_of(struct device_node *test_ancestor, * - -EINVAL if the supplier link is invalid and should not be created * - -ENODEV if there is no device that corresponds to the supplier phandle */ -static int of_link_to_phandle(struct device *dev, struct device_node *sup_np) +static int of_link_to_phandle(struct device *dev, struct device_node *sup_np, + u32 dl_flags) { struct device *sup_dev; - u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER; int ret = 0; struct device_node *tmp_np = sup_np; @@ -1181,13 +1181,20 @@ static int of_link_property(struct device *dev, struct device_node *con_np, unsigned int i = 0; bool matched = false; int ret = 0; + u32 dl_flags; + + if (dev->of_node == con_np) + dl_flags = DL_FLAG_AUTOPROBE_CONSUMER; + else + dl_flags = DL_FLAG_SYNC_STATE_ONLY; /* Do not stop at first failed link, link all available suppliers. */ while (!matched && s->parse_prop) { while ((phandle = s->parse_prop(con_np, prop_name, i))) { matched = true; i++; - if (of_link_to_phandle(dev, phandle) == -EAGAIN) + if (of_link_to_phandle(dev, phandle, dl_flags) + == -EAGAIN) ret = -EAGAIN; of_node_put(phandle); } @@ -1205,10 +1212,10 @@ static int of_link_to_suppliers(struct device *dev, for_each_property_of_node(con_np, p) if (of_link_property(dev, con_np, p->name)) - ret = -EAGAIN; + ret = -ENODEV; for_each_child_of_node(con_np, child) - if (of_link_to_suppliers(dev, child)) + if (of_link_to_suppliers(dev, child) && !ret) ret = -EAGAIN; return ret; From 15956dad5c1016155c82d094f8c1273a30f79c3d Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 28 Oct 2019 15:00:26 -0700 Subject: [PATCH 28/71] of: property: Skip adding device links to suppliers that aren't devices Some devices need to be initialized really early and can't wait for driver core or drivers to be functional. These devices are typically initialized without creating a struct device for their device nodes. If a supplier ends up being one of these devices, skip trying to add device links to them. Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20191028220027.251605-6-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/property.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/of/property.c b/drivers/of/property.c index 69a6ec8711bd0..e225ab17f5985 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1024,6 +1024,7 @@ static int of_link_to_phandle(struct device *dev, struct device_node *sup_np, struct device *sup_dev; int ret = 0; struct device_node *tmp_np = sup_np; + int is_populated; of_node_get(sup_np); /* @@ -1048,9 +1049,10 @@ static int of_link_to_phandle(struct device *dev, struct device_node *sup_np, return -EINVAL; } sup_dev = get_dev_from_fwnode(&sup_np->fwnode); + is_populated = of_node_check_flag(sup_np, OF_POPULATED); of_node_put(sup_np); if (!sup_dev) - return -EAGAIN; + return is_populated ? 0 : -EAGAIN; if (!device_link_add(dev, sup_dev, dl_flags)) ret = -EAGAIN; put_device(sup_dev); From ff229319f436f8e54345d832996b69db63bfc2d9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 14 Oct 2019 12:16:42 +0200 Subject: [PATCH 29/71] powerpc: pseries: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Benjamin Herrenschmidt Cc: Paul Mackerras Cc: Michael Ellerman Cc: "Naveen N. Rao" Cc: linuxppc-dev@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Link: https://lore.kernel.org/r/20191014101642.GA30179@kroah.com Signed-off-by: Greg Kroah-Hartman --- arch/powerpc/platforms/pseries/dtl.c | 38 ++++---------------- arch/powerpc/platforms/pseries/hvCall_inst.c | 12 ++----- arch/powerpc/platforms/pseries/lpar.c | 15 +------- 3 files changed, 10 insertions(+), 55 deletions(-) diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c index 2b87480f2837a..eab8aa293743b 100644 --- a/arch/powerpc/platforms/pseries/dtl.c +++ b/arch/powerpc/platforms/pseries/dtl.c @@ -19,7 +19,6 @@ struct dtl { struct dtl_entry *buf; - struct dentry *file; int cpu; int buf_entries; u64 last_idx; @@ -320,46 +319,28 @@ static const struct file_operations dtl_fops = { static struct dentry *dtl_dir; -static int dtl_setup_file(struct dtl *dtl) +static void dtl_setup_file(struct dtl *dtl) { char name[10]; sprintf(name, "cpu-%d", dtl->cpu); - dtl->file = debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops); - if (!dtl->file) - return -ENOMEM; - - return 0; + debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops); } static int dtl_init(void) { - struct dentry *event_mask_file, *buf_entries_file; - int rc, i; + int i; if (!firmware_has_feature(FW_FEATURE_SPLPAR)) return -ENODEV; /* set up common debugfs structure */ - rc = -ENOMEM; dtl_dir = debugfs_create_dir("dtl", powerpc_debugfs_root); - if (!dtl_dir) { - printk(KERN_WARNING "%s: can't create dtl root dir\n", - __func__); - goto err; - } - event_mask_file = debugfs_create_x8("dtl_event_mask", 0600, - dtl_dir, &dtl_event_mask); - buf_entries_file = debugfs_create_u32("dtl_buf_entries", 0400, - dtl_dir, &dtl_buf_entries); - - if (!event_mask_file || !buf_entries_file) { - printk(KERN_WARNING "%s: can't create dtl files\n", __func__); - goto err_remove_dir; - } + debugfs_create_x8("dtl_event_mask", 0600, dtl_dir, &dtl_event_mask); + debugfs_create_u32("dtl_buf_entries", 0400, dtl_dir, &dtl_buf_entries); /* set up the per-cpu log structures */ for_each_possible_cpu(i) { @@ -367,16 +348,9 @@ static int dtl_init(void) spin_lock_init(&dtl->lock); dtl->cpu = i; - rc = dtl_setup_file(dtl); - if (rc) - goto err_remove_dir; + dtl_setup_file(dtl); } return 0; - -err_remove_dir: - debugfs_remove_recursive(dtl_dir); -err: - return rc; } machine_arch_initcall(pseries, dtl_init); diff --git a/arch/powerpc/platforms/pseries/hvCall_inst.c b/arch/powerpc/platforms/pseries/hvCall_inst.c index bcc1b67417a84..c40c62ec432e2 100644 --- a/arch/powerpc/platforms/pseries/hvCall_inst.c +++ b/arch/powerpc/platforms/pseries/hvCall_inst.c @@ -129,7 +129,6 @@ static void probe_hcall_exit(void *ignored, unsigned long opcode, long retval, static int __init hcall_inst_init(void) { struct dentry *hcall_root; - struct dentry *hcall_file; char cpu_name_buf[CPU_NAME_BUF_SIZE]; int cpu; @@ -145,17 +144,12 @@ static int __init hcall_inst_init(void) } hcall_root = debugfs_create_dir(HCALL_ROOT_DIR, NULL); - if (!hcall_root) - return -ENOMEM; for_each_possible_cpu(cpu) { snprintf(cpu_name_buf, CPU_NAME_BUF_SIZE, "cpu%d", cpu); - hcall_file = debugfs_create_file(cpu_name_buf, 0444, - hcall_root, - per_cpu(hcall_stats, cpu), - &hcall_inst_seq_fops); - if (!hcall_file) - return -ENOMEM; + debugfs_create_file(cpu_name_buf, 0444, hcall_root, + per_cpu(hcall_stats, cpu), + &hcall_inst_seq_fops); } return 0; diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c index f87a5c64e24dc..f9f57c55655ec 100644 --- a/arch/powerpc/platforms/pseries/lpar.c +++ b/arch/powerpc/platforms/pseries/lpar.c @@ -1998,24 +1998,11 @@ static int __init vpa_debugfs_init(void) return 0; vpa_dir = debugfs_create_dir("vpa", powerpc_debugfs_root); - if (!vpa_dir) { - pr_warn("%s: can't create vpa root dir\n", __func__); - return -ENOMEM; - } /* set up the per-cpu vpa file*/ for_each_possible_cpu(i) { - struct dentry *d; - sprintf(name, "cpu-%ld", i); - - d = debugfs_create_file(name, 0400, vpa_dir, (void *)i, - &vpa_fops); - if (!d) { - pr_warn("%s: can't create per-cpu vpa file\n", - __func__); - return -ENOMEM; - } + debugfs_create_file(name, 0400, vpa_dir, (void *)i, &vpa_fops); } return 0; From c7c1168909410e692be6df17d0092363a00f33a9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 11 Oct 2019 15:29:28 +0200 Subject: [PATCH 30/71] debugfs: remove return value of debugfs_create_x8() No one checks the return value of debugfs_create_x8(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Link: https://lore.kernel.org/r/20191011132931.1186197-5-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 6 +++--- include/linux/debugfs.h | 12 ++++-------- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 9e705026ac103..1ec4a94b6e935 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -82,8 +82,8 @@ file should not be written to, simply set the mode bits accordingly. The values in these files are in decimal; if hexadecimal is more appropriate, the following functions can be used instead: - struct dentry *debugfs_create_x8(const char *name, umode_t mode, - struct dentry *parent, u8 *value); + void debugfs_create_x8(const char *name, umode_t mode, + struct dentry *parent, u8 *value); void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value); void debugfs_create_x32(const char *name, umode_t mode, diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 3ff736f22eb63..8aac1a9007c9d 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -633,10 +633,10 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n"); * @value: a pointer to the variable that the file should read to and write * from. */ -struct dentry *debugfs_create_x8(const char *name, umode_t mode, - struct dentry *parent, u8 *value) +void debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, + u8 *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8, + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8, &fops_x8_ro, &fops_x8_wo); } EXPORT_SYMBOL_GPL(debugfs_create_x8); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 33690949b45d6..c127c159d10ae 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -107,8 +107,8 @@ void debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent, u64 *value); struct dentry *debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent, unsigned long *value); -struct dentry *debugfs_create_x8(const char *name, umode_t mode, - struct dentry *parent, u8 *value); +void debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent, + u8 *value); void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value); void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent, @@ -268,12 +268,8 @@ static inline struct dentry *debugfs_create_ulong(const char *name, return ERR_PTR(-ENODEV); } -static inline struct dentry *debugfs_create_x8(const char *name, umode_t mode, - struct dentry *parent, - u8 *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_x8(const char *name, umode_t mode, + struct dentry *parent, u8 *value) { } static inline void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent, u16 *value) { } From 9927c6fa3e1d941c9b89f807f5d0480390eb0471 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 16 Oct 2019 06:03:32 -0700 Subject: [PATCH 31/71] debugfs: remove return value of debugfs_create_atomic_t() No one checks the return value of debugfs_create_atomic_t(), as it's not needed, so make the return value void, so that no one tries to do so in the future. Link: https://lore.kernel.org/r/20191016130332.GA28240@kroah.com Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 4 ++-- fs/debugfs/file.c | 9 ++++----- include/linux/debugfs.h | 13 ++++++------- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 1ec4a94b6e935..ac6067a02f748 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -113,8 +113,8 @@ lower-case values, or 1 or 0. Any other input will be silently ignored. Also, atomic_t values can be placed in debugfs with: - struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode, - struct dentry *parent, atomic_t *value) + void debugfs_create_atomic_t(const char *name, umode_t mode, + struct dentry *parent, atomic_t *value) A read of this file will get atomic_t values, and a write of this file will set atomic_t values. diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 8aac1a9007c9d..dede25247b81f 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -757,12 +757,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set, * @value: a pointer to the variable that the file should read to and write * from. */ -struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode, - struct dentry *parent, atomic_t *value) +void debugfs_create_atomic_t(const char *name, umode_t mode, + struct dentry *parent, atomic_t *value) { - return debugfs_create_mode_unsafe(name, mode, parent, value, - &fops_atomic_t, &fops_atomic_t_ro, - &fops_atomic_t_wo); + debugfs_create_mode_unsafe(name, mode, parent, value, &fops_atomic_t, + &fops_atomic_t_ro, &fops_atomic_t_wo); } EXPORT_SYMBOL_GPL(debugfs_create_atomic_t); diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index c127c159d10ae..19231e618c161 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -117,8 +117,8 @@ void debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent, u64 *value); void debugfs_create_size_t(const char *name, umode_t mode, struct dentry *parent, size_t *value); -struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode, - struct dentry *parent, atomic_t *value); +void debugfs_create_atomic_t(const char *name, umode_t mode, + struct dentry *parent, atomic_t *value); struct dentry *debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, bool *value); @@ -284,11 +284,10 @@ static inline void debugfs_create_size_t(const char *name, umode_t mode, struct dentry *parent, size_t *value) { } -static inline struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode, - struct dentry *parent, atomic_t *value) -{ - return ERR_PTR(-ENODEV); -} +static inline void debugfs_create_atomic_t(const char *name, umode_t mode, + struct dentry *parent, + atomic_t *value) +{ } static inline struct dentry *debugfs_create_bool(const char *name, umode_t mode, struct dentry *parent, From 726ce477285dc77c824ea7273f2be9dd48ef1267 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 21 Oct 2019 17:06:45 +0200 Subject: [PATCH 32/71] Documentation: debugfs: Document debugfs helper for unsigned long values When debugfs_create_ulong() was added, it was not documented. Fixes: c23fe83138ed7b11 ("debugfs: Add debugfs_create_ulong()") Signed-off-by: Geert Uytterhoeven Acked-by: Viresh Kumar Link: https://lore.kernel.org/r/20191021150645.32440-1-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index ac6067a02f748..36dac49e859fa 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -93,8 +93,8 @@ the following functions can be used instead: These functions are useful as long as the developer knows the size of the value to be exported. Some types can have different widths on different -architectures, though, complicating the situation somewhat. There is a -function meant to help out in one special case: +architectures, though, complicating the situation somewhat. There are +functions meant to help out in such special cases: void debugfs_create_size_t(const char *name, umode_t mode, struct dentry *parent, size_t *value); @@ -102,6 +102,12 @@ function meant to help out in one special case: As might be expected, this function will create a debugfs file to represent a variable of type size_t. +Similarly, there is a helper for variables of type unsigned long: + + struct dentry *debugfs_create_ulong(const char *name, umode_t mode, + struct dentry *parent, + unsigned long *value); + Boolean values can be placed in debugfs with: struct dentry *debugfs_create_bool(const char *name, umode_t mode, From d3504757f3f049b553ba0eda8bd17cd1f2651285 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Oct 2019 11:41:24 +0200 Subject: [PATCH 33/71] debugfs: Add debugfs_create_xul() for hexadecimal unsigned long The existing debugfs_create_ulong() function supports objects of type "unsigned long", which are 32-bit or 64-bit depending on the platform, in decimal form. To format objects in hexadecimal, various debugfs_create_x*() functions exist, but all of them take fixed-size types. Add a debugfs helper for "unsigned long" objects in hexadecimal format. This avoids the need for users to open-code the same, or introduce bugs when casting the value pointer to "u32 *" or "u64 *" to call debugfs_create_x{32,64}(). Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20191025094130.26033-2-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- Documentation/filesystems/debugfs.txt | 5 ++++- include/linux/debugfs.h | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt index 36dac49e859fa..dc497b96fa4ff 100644 --- a/Documentation/filesystems/debugfs.txt +++ b/Documentation/filesystems/debugfs.txt @@ -102,11 +102,14 @@ functions meant to help out in such special cases: As might be expected, this function will create a debugfs file to represent a variable of type size_t. -Similarly, there is a helper for variables of type unsigned long: +Similarly, there are helpers for variables of type unsigned long, in decimal +and hexadecimal: struct dentry *debugfs_create_ulong(const char *name, umode_t mode, struct dentry *parent, unsigned long *value); + void debugfs_create_xul(const char *name, umode_t mode, + struct dentry *parent, unsigned long *value); Boolean values can be placed in debugfs with: diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 19231e618c161..0e8f2e0cb91f1 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -351,4 +351,25 @@ static inline ssize_t debugfs_write_file_bool(struct file *file, #endif +/** + * debugfs_create_xul - create a debugfs file that is used to read and write an + * unsigned long value, formatted in hexadecimal + * @name: a pointer to a string containing the name of the file to create. + * @mode: the permission that the file should have + * @parent: a pointer to the parent dentry for this file. This should be a + * directory dentry if set. If this parameter is %NULL, then the + * file will be created in the root of the debugfs filesystem. + * @value: a pointer to the variable that the file should read to and write + * from. + */ +static inline void debugfs_create_xul(const char *name, umode_t mode, + struct dentry *parent, + unsigned long *value) +{ + if (sizeof(*value) == sizeof(u32)) + debugfs_create_x32(name, mode, parent, (u32 *)value); + else + debugfs_create_x64(name, mode, parent, (u64 *)value); +} + #endif From 785bbb804a70eb5ee752643919c860155b6b56c6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Oct 2019 11:41:27 +0200 Subject: [PATCH 34/71] mmc: atmel-mci: Fix debugfs on 64-bit platforms "atmel_mci.pending_events" and "atmel_mci.completed_events" are "unsigned long", i.e. 32-bit or 64-bit, depending on the platform. Hence casting their addresses to "u32 *", and calling debugfs_create_x32() breaks operation on 64-bit platforms. Fix this by using the new debugfs_create_xul() helper instead. Fixes: deec9ae31e607955 ("atmel-mci: debugfs support") Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Link: https://lore.kernel.org/r/20191025094130.26033-5-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/atmel-mci.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index c26fbe5f22221..ef2eb9e7c75a3 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -584,10 +584,10 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot) debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops); debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops); debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state); - debugfs_create_x32("pending_events", S_IRUSR, root, - (u32 *)&host->pending_events); - debugfs_create_x32("completed_events", S_IRUSR, root, - (u32 *)&host->completed_events); + debugfs_create_xul("pending_events", S_IRUSR, root, + &host->pending_events); + debugfs_create_xul("completed_events", S_IRUSR, root, + &host->completed_events); } #if defined(CONFIG_OF) From f1dfe70b7ff73e1469fd6ea25e89d8a4d2dd1c39 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Oct 2019 11:41:28 +0200 Subject: [PATCH 35/71] mmc: atmel-mci: Remove superfluous cast in debugfs_create_u32() call "atmel_mci.state" is an enum, which is compatible with u32, so there is no need to cast its address, preventing further compiler checks. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Link: https://lore.kernel.org/r/20191025094130.26033-6-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/atmel-mci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index ef2eb9e7c75a3..34c992d875298 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -583,7 +583,7 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot) debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops); debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops); - debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state); + debugfs_create_u32("state", S_IRUSR, root, &host->state); debugfs_create_xul("pending_events", S_IRUSR, root, &host->pending_events); debugfs_create_xul("completed_events", S_IRUSR, root, From 0c40c1be2512abc99ea27df83f882dd61b5437bc Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Oct 2019 11:41:29 +0200 Subject: [PATCH 36/71] mmc: dw_mmc: Fix debugfs on 64-bit platforms "dw_mci.pending_events" and "dw_mci.completed_events" are "unsigned long", i.e. 32-bit or 64-bit, depending on the platform. Hence casting their addresses to "u32 *", and calling debugfs_create_x32() breaks operation on 64-bit platforms. Fix this by using the new debugfs_create_xul() helper instead. Fixes: f95f3850f7a9e1d4 ("mmc: dw_mmc: Add Synopsys DesignWare mmc host driver.") Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Link: https://lore.kernel.org/r/20191025094130.26033-7-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/dw_mmc.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 79c55c7b4afd9..0b796f31bb043 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -177,10 +177,10 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot) debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops); debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops); debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state); - debugfs_create_x32("pending_events", S_IRUSR, root, - (u32 *)&host->pending_events); - debugfs_create_x32("completed_events", S_IRUSR, root, - (u32 *)&host->completed_events); + debugfs_create_xul("pending_events", S_IRUSR, root, + &host->pending_events); + debugfs_create_xul("completed_events", S_IRUSR, root, + &host->completed_events); } #endif /* defined(CONFIG_DEBUG_FS) */ From 118e11182a8168515a11b27c4c6d80fbb233b7f8 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Oct 2019 11:41:30 +0200 Subject: [PATCH 37/71] mmc: dw_mmc: Remove superfluous cast in debugfs_create_u32() call "dw_mci.state" is an enum, which is compatible with u32, so there is no need to cast its address, preventing further compiler checks. Signed-off-by: Geert Uytterhoeven Acked-by: Ulf Hansson Link: https://lore.kernel.org/r/20191025094130.26033-8-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/mmc/host/dw_mmc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 0b796f31bb043..709de34beca88 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -176,7 +176,7 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot) debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops); debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops); - debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state); + debugfs_create_u32("state", S_IRUSR, root, &host->state); debugfs_create_xul("pending_events", S_IRUSR, root, &host->pending_events); debugfs_create_xul("completed_events", S_IRUSR, root, From b889b3b07c9d9b7964f88d21b15899b142e10dc0 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 3 Nov 2019 13:06:46 -0500 Subject: [PATCH 38/71] firmware loader: log path to loaded firmwares This is useful for users who are trying to identify the firmwares in use on their system. Signed-off-by: Drew DeVault Link: https://lore.kernel.org/r/20191103180646.34880-1-sir@cmpwn.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index d5995d78d694e..7302e6bc419b5 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -504,6 +504,7 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, path); continue; } + dev_dbg(device, "Loading firmware from %s\n", path); if (decompress) { dev_dbg(device, "f/w decompressing %s\n", fw_priv->fw_name); From 806766af3909258ccab74265e33ce8afd21af952 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 3 Nov 2019 23:38:39 +0100 Subject: [PATCH 39/71] Revert "gpio: merrifield: Move hardware initialization to callback" This reverts commit 4c87540940cbc7ddbe9674087919c605fd5c2ef1. This revert is a prerequisite for the later revert of commit 8f86a5b4ad679e4836733b47414226074eee4e4d. Reported-by: Hans de Goede Acked-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-merrifield.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 2f1e9da81c1e4..9596024c9161f 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -362,9 +362,8 @@ static void mrfld_irq_handler(struct irq_desc *desc) chained_irq_exit(irqchip, desc); } -static int mrfld_irq_init_hw(struct gpio_chip *chip) +static void mrfld_irq_init_hw(struct mrfld_gpio *priv) { - struct mrfld_gpio *priv = gpiochip_get_data(chip); void __iomem *reg; unsigned int base; @@ -376,8 +375,6 @@ static int mrfld_irq_init_hw(struct gpio_chip *chip) reg = gpio_reg(&priv->chip, base, GFER); writel(0, reg); } - - return 0; } static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv) @@ -450,7 +447,6 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id girq = &priv->chip.irq; girq->chip = &mrfld_irqchip; - girq->init_hw = mrfld_irq_init_hw; girq->parent_handler = mrfld_irq_handler; girq->num_parents = 1; girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, @@ -463,6 +459,8 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; + mrfld_irq_init_hw(priv); + pci_set_drvdata(pdev, priv); retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); if (retval) { From 52c75f56703e1c40a2bc3f64a140602112dfa302 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 3 Nov 2019 23:40:48 +0100 Subject: [PATCH 40/71] Revert "gpio: merrifield: Restore use of irq_base" This reverts commit 6658f87f219427ee776c498e07c878eb5cad1be2. This revert is a prerequisite for the later revert of commit 8f86a5b4ad679e4836733b47414226074eee4e4d. Reported-by: Hans de Goede Acked-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-merrifield.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 9596024c9161f..4f27ddfe1e2f0 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -455,7 +455,6 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id if (!girq->parents) return -ENOMEM; girq->parents[0] = pdev->irq; - girq->first = irq_base; girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_bad_irq; From 1173c3c28abfc3d7b7665db502280ba9322320e6 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 3 Nov 2019 23:41:11 +0100 Subject: [PATCH 41/71] Revert "gpio: merrifield: Pass irqchip when adding gpiochip" This reverts commit 8f86a5b4ad679e4836733b47414226074eee4e4d. It has been established that this causes a boot regression on both Baytrail and Cherrytrail SoCs, and we can't have that in the final kernel release, so we need to revert it. Reported-by: Hans de Goede Acked-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/gpio/gpio-merrifield.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpio-merrifield.c b/drivers/gpio/gpio-merrifield.c index 4f27ddfe1e2f0..3302125e52651 100644 --- a/drivers/gpio/gpio-merrifield.c +++ b/drivers/gpio/gpio-merrifield.c @@ -397,7 +397,6 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id { const struct mrfld_gpio_pinrange *range; const char *pinctrl_dev_name; - struct gpio_irq_chip *girq; struct mrfld_gpio *priv; u32 gpio_base, irq_base; void __iomem *base; @@ -445,21 +444,6 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id raw_spin_lock_init(&priv->lock); - girq = &priv->chip.irq; - girq->chip = &mrfld_irqchip; - girq->parent_handler = mrfld_irq_handler; - girq->num_parents = 1; - girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, - sizeof(*girq->parents), - GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; - girq->parents[0] = pdev->irq; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_bad_irq; - - mrfld_irq_init_hw(priv); - pci_set_drvdata(pdev, priv); retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv); if (retval) { @@ -481,6 +465,18 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id } } + retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base, + handle_bad_irq, IRQ_TYPE_NONE); + if (retval) { + dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n"); + return retval; + } + + mrfld_irq_init_hw(priv); + + gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq, + mrfld_irq_handler); + return 0; } From 09b0965ee8cced20a4791ff54506f1a6600996fe Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 4 Nov 2019 08:38:07 +0100 Subject: [PATCH 42/71] IB: mlx5: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Leon Romanovsky Cc: Doug Ledford Cc: Jason Gunthorpe Cc: linux-rdma@vger.kernel.org Cc: linux-kernel@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/infiniband/hw/mlx5/main.c | 62 +++++++--------------------- drivers/infiniband/hw/mlx5/mlx5_ib.h | 9 +--- 2 files changed, 16 insertions(+), 55 deletions(-) diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index 831539419c301..059db06104450 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -5710,11 +5710,10 @@ static int mlx5_ib_rn_get_params(struct ib_device *device, u8 port_num, static void delay_drop_debugfs_cleanup(struct mlx5_ib_dev *dev) { - if (!dev->delay_drop.dbg) + if (!dev->delay_drop.dir_debugfs) return; - debugfs_remove_recursive(dev->delay_drop.dbg->dir_debugfs); - kfree(dev->delay_drop.dbg); - dev->delay_drop.dbg = NULL; + debugfs_remove_recursive(dev->delay_drop.dir_debugfs); + dev->delay_drop.dir_debugfs = NULL; } static void cancel_delay_drop(struct mlx5_ib_dev *dev) @@ -5765,52 +5764,22 @@ static const struct file_operations fops_delay_drop_timeout = { .read = delay_drop_timeout_read, }; -static int delay_drop_debugfs_init(struct mlx5_ib_dev *dev) +static void delay_drop_debugfs_init(struct mlx5_ib_dev *dev) { - struct mlx5_ib_dbg_delay_drop *dbg; + struct dentry *root; if (!mlx5_debugfs_root) - return 0; - - dbg = kzalloc(sizeof(*dbg), GFP_KERNEL); - if (!dbg) - return -ENOMEM; - - dev->delay_drop.dbg = dbg; - - dbg->dir_debugfs = - debugfs_create_dir("delay_drop", - dev->mdev->priv.dbg_root); - if (!dbg->dir_debugfs) - goto out_debugfs; - - dbg->events_cnt_debugfs = - debugfs_create_atomic_t("num_timeout_events", 0400, - dbg->dir_debugfs, - &dev->delay_drop.events_cnt); - if (!dbg->events_cnt_debugfs) - goto out_debugfs; - - dbg->rqs_cnt_debugfs = - debugfs_create_atomic_t("num_rqs", 0400, - dbg->dir_debugfs, - &dev->delay_drop.rqs_cnt); - if (!dbg->rqs_cnt_debugfs) - goto out_debugfs; - - dbg->timeout_debugfs = - debugfs_create_file("timeout", 0600, - dbg->dir_debugfs, - &dev->delay_drop, - &fops_delay_drop_timeout); - if (!dbg->timeout_debugfs) - goto out_debugfs; + return; - return 0; + root = debugfs_create_dir("delay_drop", dev->mdev->priv.dbg_root); + dev->delay_drop.dir_debugfs = root; -out_debugfs: - delay_drop_debugfs_cleanup(dev); - return -ENOMEM; + debugfs_create_atomic_t("num_timeout_events", 0400, root, + &dev->delay_drop.events_cnt); + debugfs_create_atomic_t("num_rqs", 0400, root, + &dev->delay_drop.rqs_cnt); + debugfs_create_file("timeout", 0600, root, &dev->delay_drop, + &fops_delay_drop_timeout); } static void init_delay_drop(struct mlx5_ib_dev *dev) @@ -5826,8 +5795,7 @@ static void init_delay_drop(struct mlx5_ib_dev *dev) atomic_set(&dev->delay_drop.rqs_cnt, 0); atomic_set(&dev->delay_drop.events_cnt, 0); - if (delay_drop_debugfs_init(dev)) - mlx5_ib_warn(dev, "Failed to init delay drop debugfs\n"); + delay_drop_debugfs_init(dev); } static void mlx5_ib_unbind_slave_port(struct mlx5_ib_dev *ibdev, diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 1a98ee2e01c4b..55ce599db803d 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -792,13 +792,6 @@ enum { MLX5_MAX_DELAY_DROP_TIMEOUT_MS = 100, }; -struct mlx5_ib_dbg_delay_drop { - struct dentry *dir_debugfs; - struct dentry *rqs_cnt_debugfs; - struct dentry *events_cnt_debugfs; - struct dentry *timeout_debugfs; -}; - struct mlx5_ib_delay_drop { struct mlx5_ib_dev *dev; struct work_struct delay_drop_work; @@ -808,7 +801,7 @@ struct mlx5_ib_delay_drop { bool activate; atomic_t events_cnt; atomic_t rqs_cnt; - struct mlx5_ib_dbg_delay_drop *dbg; + struct dentry *dir_debugfs; }; enum mlx5_ib_stages { From 4154abca299e0f27dfe362733bb5296c7bb4d63f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:11 +0200 Subject: [PATCH 43/71] Documentation: devres: add missing entry for devm_platform_ioremap_resource() devm_platform_ioremap_resource() should be documented in devres.rst. Add the missing entry. Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191022084318.22256-2-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/driver-model/devres.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index a100bef549528..8e3087662dafd 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -316,6 +316,7 @@ IOMAP devm_ioremap_nocache() devm_ioremap_wc() devm_ioremap_resource() : checks resource, requests memory region, ioremaps + devm_platform_ioremap_resource() : calls devm_ioremap_resource() for platform device devm_iounmap() pcim_iomap() pcim_iomap_regions() : do request_region() and iomap() on multiple BARs From 6e924822752cb1664fa8f76cb9c0927490bca139 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:12 +0200 Subject: [PATCH 44/71] lib: devres: prepare devm_ioremap_resource() for more variants We want to add the write-combined variant of devm_ioremap_resource(). Let's first implement __devm_ioremap_resource() which takes an additional argument type. The types are the same as for __devm_ioremap(). The existing devm_ioremap_resource() now simply calls __devm_ioremap_resource() with regular DEVM_IOREMAP type. Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191022084318.22256-3-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- lib/devres.c | 47 +++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/lib/devres.c b/lib/devres.c index 6a0e9bd6524ae..a14c727128c1c 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -114,25 +114,9 @@ void devm_iounmap(struct device *dev, void __iomem *addr) } EXPORT_SYMBOL(devm_iounmap); -/** - * devm_ioremap_resource() - check, request region, and ioremap resource - * @dev: generic device to handle the resource for - * @res: resource to be handled - * - * Checks that a resource is a valid memory region, requests the memory - * region and ioremaps it. All operations are managed and will be undone - * on driver detach. - * - * Returns a pointer to the remapped memory or an ERR_PTR() encoded error code - * on failure. Usage example: - * - * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - * base = devm_ioremap_resource(&pdev->dev, res); - * if (IS_ERR(base)) - * return PTR_ERR(base); - */ -void __iomem *devm_ioremap_resource(struct device *dev, - const struct resource *res) +static void __iomem * +__devm_ioremap_resource(struct device *dev, const struct resource *res, + enum devm_ioremap_type type) { resource_size_t size; void __iomem *dest_ptr; @@ -151,7 +135,7 @@ void __iomem *devm_ioremap_resource(struct device *dev, return IOMEM_ERR_PTR(-EBUSY); } - dest_ptr = devm_ioremap(dev, res->start, size); + dest_ptr = __devm_ioremap(dev, res->start, size, type); if (!dest_ptr) { dev_err(dev, "ioremap failed for resource %pR\n", res); devm_release_mem_region(dev, res->start, size); @@ -160,6 +144,29 @@ void __iomem *devm_ioremap_resource(struct device *dev, return dest_ptr; } + +/** + * devm_ioremap_resource() - check, request region, and ioremap resource + * @dev: generic device to handle the resource for + * @res: resource to be handled + * + * Checks that a resource is a valid memory region, requests the memory + * region and ioremaps it. All operations are managed and will be undone + * on driver detach. + * + * Returns a pointer to the remapped memory or an ERR_PTR() encoded error code + * on failure. Usage example: + * + * res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + * base = devm_ioremap_resource(&pdev->dev, res); + * if (IS_ERR(base)) + * return PTR_ERR(base); + */ +void __iomem *devm_ioremap_resource(struct device *dev, + const struct resource *res) +{ + return __devm_ioremap_resource(dev, res, DEVM_IOREMAP); +} EXPORT_SYMBOL(devm_ioremap_resource); /* From b873af620e58863b70ae9cf97f6fab4cf4c544af Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:13 +0200 Subject: [PATCH 45/71] lib: devres: provide devm_ioremap_resource_wc() Provide a variant of devm_ioremap_resource() for write-combined ioremap. Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191022084318.22256-4-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/driver-model/devres.rst | 1 + include/linux/device.h | 2 ++ lib/devres.c | 15 +++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 8e3087662dafd..e605bb9df6e17 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -316,6 +316,7 @@ IOMAP devm_ioremap_nocache() devm_ioremap_wc() devm_ioremap_resource() : checks resource, requests memory region, ioremaps + devm_ioremap_resource_wc() devm_platform_ioremap_resource() : calls devm_ioremap_resource() for platform device devm_iounmap() pcim_iomap() diff --git a/include/linux/device.h b/include/linux/device.h index d1bcc8f122f69..f05c5b92e61f5 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -962,6 +962,8 @@ extern void devm_free_pages(struct device *dev, unsigned long addr); void __iomem *devm_ioremap_resource(struct device *dev, const struct resource *res); +void __iomem *devm_ioremap_resource_wc(struct device *dev, + const struct resource *res); void __iomem *devm_of_iomap(struct device *dev, struct device_node *node, int index, diff --git a/lib/devres.c b/lib/devres.c index a14c727128c1c..97fb44e5b4d6d 100644 --- a/lib/devres.c +++ b/lib/devres.c @@ -169,6 +169,21 @@ void __iomem *devm_ioremap_resource(struct device *dev, } EXPORT_SYMBOL(devm_ioremap_resource); +/** + * devm_ioremap_resource_wc() - write-combined variant of + * devm_ioremap_resource() + * @dev: generic device to handle the resource for + * @res: resource to be handled + * + * Returns a pointer to the remapped memory or an ERR_PTR() encoded error code + * on failure. Usage example: + */ +void __iomem *devm_ioremap_resource_wc(struct device *dev, + const struct resource *res) +{ + return __devm_ioremap_resource(dev, res, DEVM_IOREMAP_WC); +} + /* * devm_of_iomap - Requests a resource and maps the memory mapped IO * for a given device_node managed by a given device From bb6243b4f73d29f8c8faf8f805a2042ac3973b71 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:14 +0200 Subject: [PATCH 46/71] drivers: platform: provide devm_platform_ioremap_resource_wc() Provide a write-combined variant of devm_platform_ioremap_resource(). Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191022084318.22256-5-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- .../driver-api/driver-model/devres.rst | 1 + drivers/base/platform.c | 19 ++++++++++++++++++- include/linux/platform_device.h | 3 +++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index e605bb9df6e17..480b78ca38715 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -318,6 +318,7 @@ IOMAP devm_ioremap_resource() : checks resource, requests memory region, ioremaps devm_ioremap_resource_wc() devm_platform_ioremap_resource() : calls devm_ioremap_resource() for platform device + devm_platform_ioremap_resource_wc() devm_iounmap() pcim_iomap() pcim_iomap_regions() : do request_region() and iomap() on multiple BARs diff --git a/drivers/base/platform.c b/drivers/base/platform.c index e0ca682a756da..28eb71967f171 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -60,6 +60,7 @@ struct resource *platform_get_resource(struct platform_device *dev, } EXPORT_SYMBOL_GPL(platform_get_resource); +#ifdef CONFIG_HAS_IOMEM /** * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform * device @@ -68,7 +69,6 @@ EXPORT_SYMBOL_GPL(platform_get_resource); * resource management * @index: resource index */ -#ifdef CONFIG_HAS_IOMEM void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, unsigned int index) { @@ -78,6 +78,23 @@ void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev, return devm_ioremap_resource(&pdev->dev, res); } EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource); + +/** + * devm_platform_ioremap_resource_wc - write-combined variant of + * devm_platform_ioremap_resource() + * + * @pdev: platform device to use both for memory resource lookup as well as + * resource management + * @index: resource index + */ +void __iomem *devm_platform_ioremap_resource_wc(struct platform_device *pdev, + unsigned int index) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, index); + return devm_ioremap_resource_wc(&pdev->dev, res); +} #endif /* CONFIG_HAS_IOMEM */ /** diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 51fb7fc28587f..91fcdbbae89d9 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -57,6 +57,9 @@ platform_find_device_by_driver(struct device *start, extern void __iomem * devm_platform_ioremap_resource(struct platform_device *pdev, unsigned int index); +extern void __iomem * +devm_platform_ioremap_resource_wc(struct platform_device *pdev, + unsigned int index); extern int platform_get_irq(struct platform_device *, unsigned int); extern int platform_get_irq_optional(struct platform_device *, unsigned int); extern int platform_irq_count(struct platform_device *); From 444b0111f3bc8f3b2bc83dc15f85123034a79bc2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:15 +0200 Subject: [PATCH 47/71] misc: sram: use devm_platform_ioremap_resource_wc() Use the new devm_platform_ioremap_resource_wc() helper instead of devm_ioremap_wc() combinded with a call to platform_get_resource(). Also use devm_platform_ioremap_resource() where applicable. Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191022084318.22256-6-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index f30448bf3a631..6c1a23cb3e8c0 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -340,8 +340,6 @@ static const struct of_device_id sram_dt_ids[] = { static int sram_probe(struct platform_device *pdev) { struct sram_dev *sram; - struct resource *res; - size_t size; int ret; int (*init_func)(void); @@ -351,25 +349,14 @@ static int sram_probe(struct platform_device *pdev) sram->dev = &pdev->dev; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(sram->dev, "found no memory resource\n"); - return -EINVAL; - } - - size = resource_size(res); - - if (!devm_request_mem_region(sram->dev, res->start, size, pdev->name)) { - dev_err(sram->dev, "could not request region for resource\n"); - return -EBUSY; - } - if (of_property_read_bool(pdev->dev.of_node, "no-memory-wc")) - sram->virt_base = devm_ioremap(sram->dev, res->start, size); + sram->virt_base = devm_platform_ioremap_resource(pdev, 0); else - sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size); - if (!sram->virt_base) - return -ENOMEM; + sram->virt_base = devm_platform_ioremap_resource_wc(pdev, 0); + if (IS_ERR(sram->virt_base)) { + dev_err(&pdev->dev, "could not map SRAM registers\n"); + return PTR_ERR(sram->virt_base); + } sram->pool = devm_gen_pool_create(sram->dev, ilog2(SRAM_GRANULARITY), NUMA_NO_NODE, NULL); @@ -382,7 +369,8 @@ static int sram_probe(struct platform_device *pdev) else clk_prepare_enable(sram->clk); - ret = sram_reserve_regions(sram, res); + ret = sram_reserve_regions(sram, + platform_get_resource(pdev, IORESOURCE_MEM, 0)); if (ret) goto err_disable_clk; From c9c8641d3ebd79274af75f7df3e6a9c6cc8a66e9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:16 +0200 Subject: [PATCH 48/71] drivers: provide devm_platform_ioremap_resource_byname() Provide a variant of devm_platform_ioremap_resource() that allows to lookup resources from platform devices by name rather than by index. Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Link: https://lore.kernel.org/r/20191022084318.22256-7-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- .../driver-api/driver-model/devres.rst | 1 + drivers/base/platform.c | 20 +++++++++++++++++++ include/linux/platform_device.h | 3 +++ 3 files changed, 24 insertions(+) diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 480b78ca38715..4ab193319d8c6 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -319,6 +319,7 @@ IOMAP devm_ioremap_resource_wc() devm_platform_ioremap_resource() : calls devm_ioremap_resource() for platform device devm_platform_ioremap_resource_wc() + devm_platform_ioremap_resource_byname() devm_iounmap() pcim_iomap() pcim_iomap_regions() : do request_region() and iomap() on multiple BARs diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 28eb71967f171..1b8a20466eef2 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -95,6 +95,26 @@ void __iomem *devm_platform_ioremap_resource_wc(struct platform_device *pdev, res = platform_get_resource(pdev, IORESOURCE_MEM, index); return devm_ioremap_resource_wc(&pdev->dev, res); } + +/** + * devm_platform_ioremap_resource_byname - call devm_ioremap_resource for + * a platform device, retrieve the + * resource by name + * + * @pdev: platform device to use both for memory resource lookup as well as + * resource management + * @name: name of the resource + */ +void __iomem * +devm_platform_ioremap_resource_byname(struct platform_device *pdev, + const char *name) +{ + struct resource *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); + return devm_ioremap_resource(&pdev->dev, res); +} +EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname); #endif /* CONFIG_HAS_IOMEM */ /** diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 91fcdbbae89d9..276a03c246919 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -60,6 +60,9 @@ devm_platform_ioremap_resource(struct platform_device *pdev, extern void __iomem * devm_platform_ioremap_resource_wc(struct platform_device *pdev, unsigned int index); +extern void __iomem * +devm_platform_ioremap_resource_byname(struct platform_device *pdev, + const char *name); extern int platform_get_irq(struct platform_device *, unsigned int); extern int platform_get_irq_optional(struct platform_device *, unsigned int); extern int platform_irq_count(struct platform_device *); From f51b18d92b6668dde131247928233e8a7de6f7b2 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:17 +0200 Subject: [PATCH 49/71] gpio: mvebu: use devm_platform_ioremap_resource_byname() Use devm_platform_ioremap_resource_byname() instead of calling platform_get_resource_byname() and devm_ioremap_resource() separately. Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20191022084318.22256-8-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-mvebu.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 6c06876943412..2f0f50336b9a1 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -773,23 +773,12 @@ static int mvebu_pwm_probe(struct platform_device *pdev, { struct device *dev = &pdev->dev; struct mvebu_pwm *mvpwm; - struct resource *res; u32 set; if (!of_device_is_compatible(mvchip->chip.of_node, "marvell,armada-370-gpio")) return 0; - /* - * There are only two sets of PWM configuration registers for - * all the GPIO lines on those SoCs which this driver reserves - * for the first two GPIO chips. So if the resource is missing - * we can't treat it as an error. - */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm"); - if (!res) - return 0; - if (IS_ERR(mvchip->clk)) return PTR_ERR(mvchip->clk); @@ -812,7 +801,13 @@ static int mvebu_pwm_probe(struct platform_device *pdev, mvchip->mvpwm = mvpwm; mvpwm->mvchip = mvchip; - mvpwm->membase = devm_ioremap_resource(dev, res); + /* + * There are only two sets of PWM configuration registers for + * all the GPIO lines on those SoCs which this driver reserves + * for the first two GPIO chips. So if the resource is missing + * we can't treat it as an error. + */ + mvpwm->membase = devm_platform_ioremap_resource_byname(pdev, "pwm"); if (IS_ERR(mvpwm->membase)) return PTR_ERR(mvpwm->membase); From cc4c831811c2901b4b896c76ecb97212d73ad5aa Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 22 Oct 2019 10:43:18 +0200 Subject: [PATCH 50/71] gpio: tegra186: use devm_platform_ioremap_resource_byname() Use the devm_platform_ioremap_resource_byname() helper instead of calling platform_get_resource_byname() and devm_ioremap_resource() separately. Signed-off-by: Bartosz Golaszewski Reviewed-by: Arnd Bergmann Acked-by: Linus Walleij Link: https://lore.kernel.org/r/20191022084318.22256-9-brgl@bgdev.pl Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpio-tegra186.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index a9058fda187e3..ef40fbe923cf3 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -407,7 +407,6 @@ static int tegra186_gpio_probe(struct platform_device *pdev) unsigned int i, j, offset; struct gpio_irq_chip *irq; struct tegra_gpio *gpio; - struct resource *res; char **names; int err; @@ -417,8 +416,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->soc = of_device_get_match_data(&pdev->dev); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio"); - gpio->base = devm_ioremap_resource(&pdev->dev, res); + gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio"); if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base); From ba861f8e07bf38c457b98e1abf6ecd9fd8c4ee92 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 4 Nov 2019 22:49:58 -0800 Subject: [PATCH 51/71] of: property: Minor style clean up of of_link_to_phandle() Adding a debug log instead of silently ignoring a phandle for an early device. Also, return the right error code instead of 0 even though the actual execution flow won't change. Signed-off-by: Saravana Kannan Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20191105065000.50407-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/property.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/of/property.c b/drivers/of/property.c index e225ab17f5985..fbc201330ba04 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1051,8 +1051,14 @@ static int of_link_to_phandle(struct device *dev, struct device_node *sup_np, sup_dev = get_dev_from_fwnode(&sup_np->fwnode); is_populated = of_node_check_flag(sup_np, OF_POPULATED); of_node_put(sup_np); - if (!sup_dev) - return is_populated ? 0 : -EAGAIN; + if (!sup_dev && is_populated) { + /* Early device without struct device. */ + dev_dbg(dev, "Not linking to %pOFP - No struct device\n", + sup_np); + return -ENODEV; + } else if (!sup_dev) { + return -EAGAIN; + } if (!device_link_add(dev, sup_dev, dl_flags)) ret = -EAGAIN; put_device(sup_dev); From a436ef4aba1f011fa25f35fe7922d577ecde6c7c Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 4 Nov 2019 22:49:59 -0800 Subject: [PATCH 52/71] of: property: Make it easy to add device links from DT properties Add a DEFINE_SIMPLE_PROP macro to make it easy to add support for simple properties with fixed names that just list phandles and phandle args. Add a DEFINE_SUFFIX_PROP macro to make it easy to add support for properties with fixes suffix that just list phandles and phandle args. Signed-off-by: Saravana Kannan Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20191105065000.50407-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/property.c | 62 ++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 15 deletions(-) diff --git a/drivers/of/property.c b/drivers/of/property.c index fbc201330ba04..812b69a029d18 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1101,17 +1101,11 @@ static struct device_node *parse_prop_cells(struct device_node *np, return sup_args.np; } -static struct device_node *parse_clocks(struct device_node *np, - const char *prop_name, int index) -{ - return parse_prop_cells(np, prop_name, index, "clocks", "#clock-cells"); -} - -static struct device_node *parse_interconnects(struct device_node *np, - const char *prop_name, int index) -{ - return parse_prop_cells(np, prop_name, index, "interconnects", - "#interconnect-cells"); +#define DEFINE_SIMPLE_PROP(fname, name, cells) \ +static struct device_node *parse_##fname(struct device_node *np, \ + const char *prop_name, int index) \ +{ \ + return parse_prop_cells(np, prop_name, index, name, cells); \ } static int strcmp_suffix(const char *str, const char *suffix) @@ -1125,13 +1119,47 @@ static int strcmp_suffix(const char *str, const char *suffix) return strcmp(str + len - suffix_len, suffix); } -static struct device_node *parse_regulators(struct device_node *np, - const char *prop_name, int index) +/** + * parse_suffix_prop_cells - Suffix property parsing function for suppliers + * + * @np: Pointer to device tree node containing a list + * @prop_name: Name of property to be parsed. Expected to hold phandle values + * @index: For properties holding a list of phandles, this is the index + * into the list. + * @suffix: Property suffix that is known to contain list of phandle(s) to + * supplier(s) + * @cells_name: property name that specifies phandles' arguments count + * + * This is a helper function to parse properties that have a known fixed suffix + * and are a list of phandles and phandle arguments. + * + * Returns: + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +static struct device_node *parse_suffix_prop_cells(struct device_node *np, + const char *prop_name, int index, + const char *suffix, + const char *cells_name) { - if (index || strcmp_suffix(prop_name, "-supply")) + struct of_phandle_args sup_args; + + if (strcmp_suffix(prop_name, suffix)) return NULL; - return of_parse_phandle(np, prop_name, 0); + if (of_parse_phandle_with_args(np, prop_name, cells_name, index, + &sup_args)) + return NULL; + + return sup_args.np; +} + +#define DEFINE_SUFFIX_PROP(fname, suffix, cells) \ +static struct device_node *parse_##fname(struct device_node *np, \ + const char *prop_name, int index) \ +{ \ + return parse_suffix_prop_cells(np, prop_name, index, suffix, cells); \ } /** @@ -1155,6 +1183,10 @@ struct supplier_bindings { const char *prop_name, int index); }; +DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells") +DEFINE_SIMPLE_PROP(interconnects, "interconnects", "#interconnect-cells") +DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) + static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_clocks, }, { .parse_prop = parse_interconnects, }, From 8e12257dead76131701c90a3555b0967727efc3f Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Mon, 4 Nov 2019 22:50:00 -0800 Subject: [PATCH 53/71] of: property: Add device link support for iommus, mboxes and io-channels Add support for creating device links out of more DT properties. Signed-off-by: Saravana Kannan Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20191105065000.50407-4-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/of/property.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/of/property.c b/drivers/of/property.c index 812b69a029d18..0fa04692e3ccf 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1185,11 +1185,17 @@ struct supplier_bindings { DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells") DEFINE_SIMPLE_PROP(interconnects, "interconnects", "#interconnect-cells") +DEFINE_SIMPLE_PROP(iommus, "iommus", "#iommu-cells") +DEFINE_SIMPLE_PROP(mboxes, "mboxes", "#mbox-cells") +DEFINE_SIMPLE_PROP(io_channels, "io-channel", "#io-channel-cells") DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_clocks, }, { .parse_prop = parse_interconnects, }, + { .parse_prop = parse_iommus, }, + { .parse_prop = parse_mboxes, }, + { .parse_prop = parse_io_channels, }, { .parse_prop = parse_regulators, }, {} }; From d0dd61a2774fa50304eccfb2b8d67265b29f3bd8 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Thu, 7 Nov 2019 09:52:38 +0100 Subject: [PATCH 54/71] media: c8sectpfe: no need to check return value of debugfs_create functions When calling debugfs functions, there is no need to ever check the return value. The function can work or not, but the code logic should never do something different based on this. Cc: Mauro Carvalho Chehab Cc: linux-arm-kernel@lists.infradead.org Cc: linux-media@vger.kernel.org Cc: linux-kernel@vger.kernel.org Reviewed-by: Patrice Chotard Signed-off-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20191107085238.GA1285658@kroah.com Signed-off-by: Greg Kroah-Hartman --- .../sti/c8sectpfe/c8sectpfe-debugfs.c | 26 +++---------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c index 8f0ddcbeed9df..301fa10f419b6 100644 --- a/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c +++ b/drivers/media/platform/sti/c8sectpfe/c8sectpfe-debugfs.c @@ -225,36 +225,16 @@ static const struct debugfs_reg32 fei_sys_regs[] = { void c8sectpfe_debugfs_init(struct c8sectpfei *fei) { - struct dentry *root; - struct dentry *file; - - root = debugfs_create_dir("c8sectpfe", NULL); - if (!root) - goto err; - - fei->root = root; - fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL); if (!fei->regset) - goto err; + return; fei->regset->regs = fei_sys_regs; fei->regset->nregs = ARRAY_SIZE(fei_sys_regs); fei->regset->base = fei->io; - file = debugfs_create_regset32("registers", S_IRUGO, root, - fei->regset); - if (!file) { - dev_err(fei->dev, - "%s not able to create 'registers' debugfs\n" - , __func__); - goto err; - } - - return; - -err: - debugfs_remove_recursive(root); + fei->root = debugfs_create_dir("c8sectpfe", NULL); + debugfs_create_regset32("registers", S_IRUGO, fei->root, fei->regset); } void c8sectpfe_debugfs_exit(struct c8sectpfei *fei) From 0628cda318df6baec439ca6e6e274007492f1ccd Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Oct 2019 11:41:25 +0200 Subject: [PATCH 55/71] mac80211: Use debugfs_create_xul() helper Use the new debugfs_create_xul() helper instead of open-coding the same operation. Signed-off-by: Geert Uytterhoeven Acked-by: Johannes Berg Link: https://lore.kernel.org/r/20191025094130.26033-3-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- net/mac80211/debugfs_sta.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/net/mac80211/debugfs_sta.c b/net/mac80211/debugfs_sta.c index c8ad20c28c438..ca34dcdac8c0b 100644 --- a/net/mac80211/debugfs_sta.c +++ b/net/mac80211/debugfs_sta.c @@ -928,12 +928,7 @@ STA_OPS(he_capa); sta->debugfs_dir, sta, &sta_ ##name## _ops); #define DEBUGFS_ADD_COUNTER(name, field) \ - if (sizeof(sta->field) == sizeof(u32)) \ - debugfs_create_u32(#name, 0400, sta->debugfs_dir, \ - (u32 *) &sta->field); \ - else \ - debugfs_create_u64(#name, 0400, sta->debugfs_dir, \ - (u64 *) &sta->field); + debugfs_create_ulong(#name, 0400, sta->debugfs_dir, &sta->field); void ieee80211_sta_debugfs_add(struct sta_info *sta) { @@ -978,14 +973,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta) NL80211_EXT_FEATURE_AIRTIME_FAIRNESS)) DEBUGFS_ADD(airtime); - if (sizeof(sta->driver_buffered_tids) == sizeof(u32)) - debugfs_create_x32("driver_buffered_tids", 0400, - sta->debugfs_dir, - (u32 *)&sta->driver_buffered_tids); - else - debugfs_create_x64("driver_buffered_tids", 0400, - sta->debugfs_dir, - (u64 *)&sta->driver_buffered_tids); + debugfs_create_xul("driver_buffered_tids", 0400, sta->debugfs_dir, + &sta->driver_buffered_tids); drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs_dir); } From b52517e456f8bb6ae720feab891af6d5b8a8f217 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 25 Oct 2019 11:41:26 +0200 Subject: [PATCH 56/71] net: caif: Fix debugfs on 64-bit platforms "ser_device.state" is "unsigned long", i.e. 32-bit or 64-bit, depending on the platform. Hence casting its address to "u32 *", and calling debugfs_create_x32() breaks operation on 64-bit platforms. Fix this by using the new debugfs_create_xul() helper instead. Fixes: 9b27105b4a44c54b ("net-caif-driver: add CAIF serial driver (ldisc)") Signed-off-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20191025094130.26033-4-geert+renesas@glider.be Signed-off-by: Greg Kroah-Hartman --- drivers/net/caif/caif_serial.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/caif/caif_serial.c b/drivers/net/caif/caif_serial.c index 40b079162804f..bd40b114d6cd7 100644 --- a/drivers/net/caif/caif_serial.c +++ b/drivers/net/caif/caif_serial.c @@ -102,8 +102,8 @@ static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty) debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir, &ser->rx_blob); - debugfs_create_x32("ser_state", 0400, ser->debugfs_tty_dir, - (u32 *)&ser->state); + debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir, + &ser->state); debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir, &ser->tty_status); From bd84f2881a8d4c720bb4acaab371578f4f96b3de Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 23 Oct 2019 15:21:50 +0300 Subject: [PATCH 57/71] gpio: bd70528: Add MODULE ALIAS to autoload module The bd70528 GPIO driver is probed by MFD driver. Add MODULE_ALIAS in order to allow udev to load the module when MFD sub-device cell for GPIO is added. Signed-off-by: Matti Vaittinen Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-bd70528.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-bd70528.c b/drivers/gpio/gpio-bd70528.c index 734be6b752d0d..88ab3bc85aac4 100644 --- a/drivers/gpio/gpio-bd70528.c +++ b/drivers/gpio/gpio-bd70528.c @@ -232,3 +232,4 @@ module_platform_driver(bd70528_gpio); MODULE_AUTHOR("Matti Vaittinen "); MODULE_DESCRIPTION("BD70528 voltage regulator driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:bd70528-gpio"); From b74f0456c120289b026701f849e0fd7de56fd2d7 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 24 Oct 2019 14:22:23 +0200 Subject: [PATCH 58/71] gpio: em: Use proper irq_chip name The irq_chip .name field should contain the device's class name, not the instance's name. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-em.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 674ebebaf90b6..adc281daacff4 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -322,7 +322,7 @@ static int em_gio_probe(struct platform_device *pdev) gpio_chip->ngpio = ngpios; irq_chip = &p->irq_chip; - irq_chip->name = name; + irq_chip->name = "gpio-em"; irq_chip->irq_mask = em_gio_irq_disable; irq_chip->irq_unmask = em_gio_irq_enable; irq_chip->irq_set_type = em_gio_irq_set_type; From f932a68695e4c7d92e721d2c23580d9f35494662 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 24 Oct 2019 14:22:24 +0200 Subject: [PATCH 59/71] gpio: rcar: Use proper irq_chip name The irq_chip .name field should contain the device's class name, not the instance's name. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index d7e6e68c25aff..f800b250971c7 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -486,7 +486,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) gpio_chip->ngpio = npins; irq_chip = &p->irq_chip; - irq_chip->name = name; + irq_chip->name = "gpio-rcar"; irq_chip->parent_device = dev; irq_chip->irq_mask = gpio_rcar_irq_disable; irq_chip->irq_unmask = gpio_rcar_irq_enable; From 9225d5169d110734099a82c39c073a11e399cb3f Mon Sep 17 00:00:00 2001 From: Drew Fustini Date: Tue, 5 Nov 2019 10:04:23 +0800 Subject: [PATCH 60/71] gpio: expose pull-up/pull-down line flags to userspace Add pull-up/pull-down flags to the gpio line get and set ioctl() calls. Use cases include a push button that does not have an external resistor. Addition use cases described by Limor Fried (ladyada) of Adafruit in this PR for Adafruit_Blinka Python lib: https://github.com/adafruit/Adafruit_Blinka/pull/59 Signed-off-by: Drew Fustini [Kent: added BIAS to GPIO flag names and restrict application to input lines] Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 18 ++++++++++++++++++ include/uapi/linux/gpio.h | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 731d732cdc2b7..53086724c0512 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -422,6 +422,8 @@ struct linehandle_state { (GPIOHANDLE_REQUEST_INPUT | \ GPIOHANDLE_REQUEST_OUTPUT | \ GPIOHANDLE_REQUEST_ACTIVE_LOW | \ + GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ + GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ GPIOHANDLE_REQUEST_OPEN_DRAIN | \ GPIOHANDLE_REQUEST_OPEN_SOURCE) @@ -553,6 +555,12 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) return -EINVAL; + /* PULL_UP and PULL_DOWN flags only make sense for input mode. */ + if (!(lflags & GPIOHANDLE_REQUEST_INPUT) && + ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) + return -EINVAL; + lh = kzalloc(sizeof(*lh), GFP_KERNEL); if (!lh) return -ENOMEM; @@ -593,6 +601,10 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); ret = gpiod_set_transitory(desc, false); if (ret < 0) @@ -1092,6 +1104,10 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE | GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; + if (test_bit(FLAG_PULL_UP, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_UP; if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) return -EFAULT; @@ -2785,6 +2801,8 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_REQUESTED, &desc->flags); clear_bit(FLAG_OPEN_DRAIN, &desc->flags); clear_bit(FLAG_OPEN_SOURCE, &desc->flags); + clear_bit(FLAG_PULL_UP, &desc->flags); + clear_bit(FLAG_PULL_DOWN, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = true; } diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 4ebfe0ac6c5b9..39e6c7854d63d 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -33,6 +33,8 @@ struct gpiochip_info { #define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2) #define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3) #define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4) +#define GPIOLINE_FLAG_BIAS_PULL_UP (1UL << 5) +#define GPIOLINE_FLAG_BIAS_PULL_DOWN (1UL << 6) /** * struct gpioline_info - Information about a certain GPIO line @@ -62,6 +64,8 @@ struct gpioline_info { #define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2) #define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3) #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) +#define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5) +#define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6) /** * struct gpiohandle_request - Information about a GPIO handle request From 7b479a8448c2e5ced36ca6a2d03178b920298ee5 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 5 Nov 2019 10:04:24 +0800 Subject: [PATCH 61/71] gpiolib: add support for pull up/down to lineevent_create Add support for pull up/down to lineevent_create. Use cases include receiving asynchronous presses from a push button without an external pull up/down. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 53086724c0512..b7d7bb8bd20dd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -951,6 +951,10 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) + set_bit(FLAG_PULL_DOWN, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) + set_bit(FLAG_PULL_UP, &desc->flags); ret = gpiod_direction_input(desc); if (ret) From 2148ad7790ea4f1f0081e6404fbb776bdbc793bb Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 5 Nov 2019 10:04:25 +0800 Subject: [PATCH 62/71] gpiolib: add support for disabling line bias Allow pull up/down bias to be disabled, allowing the line to float or to be biased only by external circuitry. Use case is for where the bias has been applied previously, either by default or by the user, but that setting may conflict with the current use of the line. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 61 ++++++++++++++++++++++++++++++++------- drivers/gpio/gpiolib.h | 1 + include/uapi/linux/gpio.h | 2 ++ 3 files changed, 54 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b7d7bb8bd20dd..9969116603066 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -424,6 +424,7 @@ struct linehandle_state { GPIOHANDLE_REQUEST_ACTIVE_LOW | \ GPIOHANDLE_REQUEST_BIAS_PULL_UP | \ GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \ + GPIOHANDLE_REQUEST_BIAS_DISABLE | \ GPIOHANDLE_REQUEST_OPEN_DRAIN | \ GPIOHANDLE_REQUEST_OPEN_SOURCE) @@ -555,12 +556,21 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) return -EINVAL; - /* PULL_UP and PULL_DOWN flags only make sense for input mode. */ + /* Bias flags only allowed for input mode. */ if (!(lflags & GPIOHANDLE_REQUEST_INPUT) && - ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || + ((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) return -EINVAL; + /* Only one bias flag can be set. */ + if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + lh = kzalloc(sizeof(*lh), GFP_KERNEL); if (!lh) return -ENOMEM; @@ -601,6 +611,8 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) set_bit(FLAG_OPEN_DRAIN, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) set_bit(FLAG_PULL_DOWN, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) @@ -925,6 +937,14 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) return -EINVAL; + /* Only one bias flag can be set. */ + if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + le = kzalloc(sizeof(*le), GFP_KERNEL); if (!le) return -ENOMEM; @@ -951,6 +971,8 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) if (lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW) set_bit(FLAG_ACTIVE_LOW, &desc->flags); + if (lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) set_bit(FLAG_PULL_DOWN, &desc->flags); if (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) @@ -1108,6 +1130,8 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE | GPIOLINE_FLAG_IS_OUT); + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + lineinfo.flags |= GPIOLINE_FLAG_BIAS_DISABLE; if (test_bit(FLAG_PULL_DOWN, &desc->flags)) lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN; if (test_bit(FLAG_PULL_UP, &desc->flags)) @@ -2807,6 +2831,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_OPEN_SOURCE, &desc->flags); clear_bit(FLAG_PULL_UP, &desc->flags); clear_bit(FLAG_PULL_DOWN, &desc->flags); + clear_bit(FLAG_BIAS_DISABLE, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = true; } @@ -2933,6 +2958,7 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset, unsigned arg; switch (mode) { + case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_PULL_UP: arg = 1; @@ -2946,6 +2972,26 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset, return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP; } +static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc) +{ + int bias = 0; + int ret = 0; + + if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + bias = PIN_CONFIG_BIAS_DISABLE; + else if (test_bit(FLAG_PULL_UP, &desc->flags)) + bias = PIN_CONFIG_BIAS_PULL_UP; + else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + bias = PIN_CONFIG_BIAS_PULL_DOWN; + + if (bias) { + ret = gpio_set_config(chip, gpio_chip_hwgpio(desc), bias); + if (ret != -ENOTSUPP) + return ret; + } + return 0; +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input @@ -2990,15 +3036,10 @@ int gpiod_direction_input(struct gpio_desc *desc) __func__); return -EIO; } - if (ret == 0) + if (ret == 0) { clear_bit(FLAG_IS_OUT, &desc->flags); - - if (test_bit(FLAG_PULL_UP, &desc->flags)) - gpio_set_config(chip, gpio_chip_hwgpio(desc), - PIN_CONFIG_BIAS_PULL_UP); - else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) - gpio_set_config(chip, gpio_chip_hwgpio(desc), - PIN_CONFIG_BIAS_PULL_DOWN); + ret = gpio_set_bias(chip, desc); + } trace_gpio_direction(desc_to_gpio(desc), 1, ret); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b8b10a409c7b9..ca9bc1e4803c2 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -110,6 +110,7 @@ struct gpio_desc { #define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */ #define FLAG_PULL_UP 13 /* GPIO has pull up enabled */ #define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */ +#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ /* Connection label */ const char *label; diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 39e6c7854d63d..7cc21c3b08397 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -35,6 +35,7 @@ struct gpiochip_info { #define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4) #define GPIOLINE_FLAG_BIAS_PULL_UP (1UL << 5) #define GPIOLINE_FLAG_BIAS_PULL_DOWN (1UL << 6) +#define GPIOLINE_FLAG_BIAS_DISABLE (1UL << 7) /** * struct gpioline_info - Information about a certain GPIO line @@ -66,6 +67,7 @@ struct gpioline_info { #define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4) #define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5) #define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6) +#define GPIOHANDLE_REQUEST_BIAS_DISABLE (1UL << 7) /** * struct gpiohandle_request - Information about a GPIO handle request From 2821ae5f3033fb4140855ffd728c28fb42ba2bb9 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 5 Nov 2019 10:04:26 +0800 Subject: [PATCH 63/71] gpiolib: add support for biasing output lines Allow pull up/down bias to be set on output lines. Use case is for open source or open drain applications where internal pull up/down may conflict with external biasing. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9969116603066..be159f56ff452 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -556,8 +556,9 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) return -EINVAL; - /* Bias flags only allowed for input mode. */ - if (!(lflags & GPIOHANDLE_REQUEST_INPUT) && + /* Bias flags only allowed for input or output mode. */ + if (!((lflags & GPIOHANDLE_REQUEST_INPUT) || + (lflags & GPIOHANDLE_REQUEST_OUTPUT)) && ((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) @@ -3169,6 +3170,9 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) } set_output_value: + ret = gpio_set_bias(gc, desc); + if (ret) + return ret; return gpiod_direction_output_raw_commit(desc, value); set_output_flag: From 64e7112ee307834680eed578fdc0cfee63263fa7 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 5 Nov 2019 10:04:27 +0800 Subject: [PATCH 64/71] gpio: mockup: add set_config to support pull up/down Add support for the pull up/down state set via gpiolib line requests to be reflected in the state of the mockup. Use case is for testing of the GPIO uAPI, specifically the pull up/down flags. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mockup.c | 94 ++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 47c172b2f5ad6..56d647a30e3ea 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -141,6 +141,61 @@ static void gpio_mockup_set_multiple(struct gpio_chip *gc, mutex_unlock(&chip->lock); } +static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip, + unsigned int offset, int value) +{ + struct gpio_desc *desc; + struct gpio_chip *gc; + struct irq_sim *sim; + int curr, irq, irq_type; + + gc = &chip->gc; + desc = &gc->gpiodev->descs[offset]; + sim = &chip->irqsim; + + mutex_lock(&chip->lock); + + if (test_bit(FLAG_REQUESTED, &desc->flags) && + !test_bit(FLAG_IS_OUT, &desc->flags)) { + curr = __gpio_mockup_get(chip, offset); + if (curr == value) + goto out; + + irq = irq_sim_irqnum(sim, offset); + irq_type = irq_get_trigger_type(irq); + + if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) || + (value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) + irq_sim_fire(sim, offset); + } + + /* Change the value unless we're actively driving the line. */ + if (!test_bit(FLAG_REQUESTED, &desc->flags) || + !test_bit(FLAG_IS_OUT, &desc->flags)) + __gpio_mockup_set(chip, offset, value); + +out: + chip->lines[offset].pull = value; + mutex_unlock(&chip->lock); + return 0; +} + +static int gpio_mockup_set_config(struct gpio_chip *gc, + unsigned int offset, unsigned long config) +{ + struct gpio_mockup_chip *chip = gpiochip_get_data(gc); + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_PULL_UP: + return gpio_mockup_apply_pull(chip, offset, 1); + case PIN_CONFIG_BIAS_PULL_DOWN: + return gpio_mockup_apply_pull(chip, offset, 0); + default: + break; + } + return -ENOTSUPP; +} + static int gpio_mockup_dirout(struct gpio_chip *gc, unsigned int offset, int value) { @@ -221,12 +276,8 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file, size_t size, loff_t *ppos) { struct gpio_mockup_dbgfs_private *priv; - int rv, val, curr, irq, irq_type; - struct gpio_mockup_chip *chip; + int rv, val; struct seq_file *sfile; - struct gpio_desc *desc; - struct gpio_chip *gc; - struct irq_sim *sim; if (*ppos != 0) return -EINVAL; @@ -239,35 +290,9 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file, sfile = file->private_data; priv = sfile->private; - chip = priv->chip; - gc = &chip->gc; - desc = &gc->gpiodev->descs[priv->offset]; - sim = &chip->irqsim; - - mutex_lock(&chip->lock); - - if (test_bit(FLAG_REQUESTED, &desc->flags) && - !test_bit(FLAG_IS_OUT, &desc->flags)) { - curr = __gpio_mockup_get(chip, priv->offset); - if (curr == val) - goto out; - - irq = irq_sim_irqnum(sim, priv->offset); - irq_type = irq_get_trigger_type(irq); - - if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) || - (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) - irq_sim_fire(sim, priv->offset); - } - - /* Change the value unless we're actively driving the line. */ - if (!test_bit(FLAG_REQUESTED, &desc->flags) || - !test_bit(FLAG_IS_OUT, &desc->flags)) - __gpio_mockup_set(chip, priv->offset, val); - -out: - chip->lines[priv->offset].pull = val; - mutex_unlock(&chip->lock); + rv = gpio_mockup_apply_pull(priv->chip, priv->offset, val); + if (rv) + return rv; return size; } @@ -413,6 +438,7 @@ static int gpio_mockup_probe(struct platform_device *pdev) gc->direction_output = gpio_mockup_dirout; gc->direction_input = gpio_mockup_dirin; gc->get_direction = gpio_mockup_get_direction; + gc->set_config = gpio_mockup_set_config; gc->to_irq = gpio_mockup_to_irq; gc->free = gpio_mockup_free; From b043ed7ef0b358d7f32dc57c218f925fd2802aba Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 5 Nov 2019 10:04:28 +0800 Subject: [PATCH 65/71] gpiolib: move validation of line handle flags into helper function Move validation of line handle flags into helper function. This reduces the size and complexity of linehandle_create and allows the validation to be reused elsewhere. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 93 +++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 42 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index be159f56ff452..a9826627f3475 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -428,6 +428,54 @@ struct linehandle_state { GPIOHANDLE_REQUEST_OPEN_DRAIN | \ GPIOHANDLE_REQUEST_OPEN_SOURCE) +static int linehandle_validate_flags(u32 flags) +{ + /* Return an error if an unknown flag is set */ + if (flags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) + return -EINVAL; + + /* + * Do not allow both INPUT & OUTPUT flags to be set as they are + * contradictory. + */ + if ((flags & GPIOHANDLE_REQUEST_INPUT) && + (flags & GPIOHANDLE_REQUEST_OUTPUT)) + return -EINVAL; + + /* + * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If + * the hardware actually supports enabling both at the same time the + * electrical result would be disastrous. + */ + if ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) + return -EINVAL; + + /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ + if (!(flags & GPIOHANDLE_REQUEST_OUTPUT) && + ((flags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || + (flags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) + return -EINVAL; + + /* Bias flags only allowed for input or output mode. */ + if (!((flags & GPIOHANDLE_REQUEST_INPUT) || + (flags & GPIOHANDLE_REQUEST_OUTPUT)) && + ((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) + return -EINVAL; + + /* Only one bias flag can be set. */ + if (((flags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && + (flags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | + GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || + ((flags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && + (flags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) + return -EINVAL; + + return 0; +} + static long linehandle_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { @@ -529,48 +577,9 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) lflags = handlereq.flags; - /* Return an error if an unknown flag is set */ - if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS) - return -EINVAL; - - /* - * Do not allow both INPUT & OUTPUT flags to be set as they are - * contradictory. - */ - if ((lflags & GPIOHANDLE_REQUEST_INPUT) && - (lflags & GPIOHANDLE_REQUEST_OUTPUT)) - return -EINVAL; - - /* - * Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If - * the hardware actually supports enabling both at the same time the - * electrical result would be disastrous. - */ - if ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) && - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)) - return -EINVAL; - - /* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */ - if (!(lflags & GPIOHANDLE_REQUEST_OUTPUT) && - ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) || - (lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))) - return -EINVAL; - - /* Bias flags only allowed for input or output mode. */ - if (!((lflags & GPIOHANDLE_REQUEST_INPUT) || - (lflags & GPIOHANDLE_REQUEST_OUTPUT)) && - ((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) || - (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP) || - (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN))) - return -EINVAL; - - /* Only one bias flag can be set. */ - if (((lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE) && - (lflags & (GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | - GPIOHANDLE_REQUEST_BIAS_PULL_UP))) || - ((lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN) && - (lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP))) - return -EINVAL; + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; lh = kzalloc(sizeof(*lh), GFP_KERNEL); if (!lh) From e588bb1eae31be73fbec2b731be986a7c09635a4 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Tue, 5 Nov 2019 10:04:29 +0800 Subject: [PATCH 66/71] gpio: add new SET_CONFIG ioctl() to gpio chardev Add the GPIOHANDLE_SET_CONFIG_IOCTL to the gpio chardev. The ioctl allows some of the configuration of a requested handle to be changed without having to release the line. The primary use case is the changing of direction for bi-directional lines. Based on initial work by Bartosz Golaszewski Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 69 +++++++++++++++++++++++++++++++++++++++ include/uapi/linux/gpio.h | 18 ++++++++++ 2 files changed, 87 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a9826627f3475..dba5f08f308c4 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -476,6 +476,73 @@ static int linehandle_validate_flags(u32 flags) return 0; } +static void linehandle_configure_flag(unsigned long *flagsp, + u32 bit, bool active) +{ + if (active) + set_bit(bit, flagsp); + else + clear_bit(bit, flagsp); +} + +static long linehandle_set_config(struct linehandle_state *lh, + void __user *ip) +{ + struct gpiohandle_config gcnf; + struct gpio_desc *desc; + int i, ret; + u32 lflags; + unsigned long *flagsp; + + if (copy_from_user(&gcnf, ip, sizeof(gcnf))) + return -EFAULT; + + lflags = gcnf.flags; + ret = linehandle_validate_flags(lflags); + if (ret) + return ret; + + for (i = 0; i < lh->numdescs; i++) { + desc = lh->descs[i]; + flagsp = &desc->flags; + + linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW, + lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW); + + linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN, + lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN); + + linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE, + lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE); + + linehandle_configure_flag(flagsp, FLAG_PULL_UP, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP); + + linehandle_configure_flag(flagsp, FLAG_PULL_DOWN, + lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN); + + linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE, + lflags & GPIOHANDLE_REQUEST_BIAS_DISABLE); + + /* + * Lines have to be requested explicitly for input + * or output, else the line will be treated "as is". + */ + if (lflags & GPIOHANDLE_REQUEST_OUTPUT) { + int val = !!gcnf.default_values[i]; + + ret = gpiod_direction_output(desc, val); + if (ret) + return ret; + } else if (lflags & GPIOHANDLE_REQUEST_INPUT) { + ret = gpiod_direction_input(desc); + if (ret) + return ret; + } + } + return 0; +} + static long linehandle_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { @@ -526,6 +593,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd, lh->descs, NULL, vals); + } else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) { + return linehandle_set_config(lh, ip); } return -EINVAL; } diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 7cc21c3b08397..799cf823d493a 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -100,6 +100,24 @@ struct gpiohandle_request { int fd; }; +/** + * struct gpiohandle_config - Configuration for a GPIO handle request + * @flags: updated flags for the requested GPIO lines, such as + * GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed + * together + * @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set in flags, + * this specifies the default output value, should be 0 (low) or + * 1 (high), anything else than 0 or 1 will be interpreted as 1 (high) + * @padding: reserved for future use and should be zero filled + */ +struct gpiohandle_config { + __u32 flags; + __u8 default_values[GPIOHANDLES_MAX]; + __u32 padding[4]; /* padding for future use */ +}; + +#define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0a, struct gpiohandle_config) + /** * struct gpiohandle_data - Information of values on a GPIO handle * @values: when getting the state of lines this contains the current From 13a62a56aa4cdab94e696bc0a3e4fb6dc4200b0f Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 8 Nov 2019 16:33:51 +0100 Subject: [PATCH 67/71] gpio: tegra186: Derive register offsets from bank/port The register offsets for a given bank and port can be easily derived from the bank and port indices. Update the port descriptors to list only the bank and port numbers to simplify this. Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 195 ++++++++++++++++++----------------- 1 file changed, 100 insertions(+), 95 deletions(-) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 934ab36058491..1e9993143eb4e 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -44,9 +44,9 @@ struct tegra_gpio_port { const char *name; - unsigned int offset; + unsigned int bank; + unsigned int port; unsigned int pins; - unsigned int irq; }; struct tegra_gpio_soc { @@ -90,12 +90,15 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio, unsigned int pin) { const struct tegra_gpio_port *port; + unsigned int offset; port = tegra186_gpio_get_port(gpio, &pin); if (!port) return NULL; - return gpio->base + port->offset + pin * 0x20; + offset = port->bank * 0x1000 + port->port * 0x200; + + return gpio->base + offset + pin * 0x20; } static int tegra186_gpio_get_direction(struct gpio_chip *chip, @@ -343,12 +346,14 @@ static void tegra186_gpio_irq(struct irq_desc *desc) for (i = 0; i < gpio->soc->num_ports; i++) { const struct tegra_gpio_port *port = &gpio->soc->ports[i]; - void __iomem *base = gpio->base + port->offset; unsigned int pin, irq; unsigned long value; + void __iomem *base; + + base = gpio->base + port->bank * 0x1000 + port->port * 0x200; - /* skip ports that are not associated with this controller */ - if (parent != gpio->irq[port->irq]) + /* skip ports that are not associated with this bank */ + if (parent != gpio->irq[port->bank]) goto skip; value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1)); @@ -562,7 +567,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) const struct tegra_gpio_port *port = &gpio->soc->ports[i]; for (j = 0; j < port->pins; j++) - irq->map[offset + j] = irq->parents[port->irq]; + irq->map[offset + j] = irq->parents[port->bank]; offset += port->pins; } @@ -581,38 +586,38 @@ static int tegra186_gpio_remove(struct platform_device *pdev) return 0; } -#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \ - [TEGRA186_MAIN_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA186_MAIN_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra186_main_ports[] = { - TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2), - TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3), - TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3), - TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3), - TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2), - TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2), - TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4), - TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1), - TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0), - TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5), - TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5), - TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1), - TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5), - TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0), - TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0), - TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4), - TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0), - TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0), - TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0), - TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1), - TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1), - TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2), - TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5), + TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7), + TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7), + TEGRA186_MAIN_GPIO_PORT( C, 3, 1, 7), + TEGRA186_MAIN_GPIO_PORT( D, 3, 2, 6), + TEGRA186_MAIN_GPIO_PORT( E, 2, 1, 8), + TEGRA186_MAIN_GPIO_PORT( F, 2, 2, 6), + TEGRA186_MAIN_GPIO_PORT( G, 4, 1, 6), + TEGRA186_MAIN_GPIO_PORT( H, 1, 0, 7), + TEGRA186_MAIN_GPIO_PORT( I, 0, 4, 8), + TEGRA186_MAIN_GPIO_PORT( J, 5, 0, 8), + TEGRA186_MAIN_GPIO_PORT( K, 5, 1, 1), + TEGRA186_MAIN_GPIO_PORT( L, 1, 1, 8), + TEGRA186_MAIN_GPIO_PORT( M, 5, 3, 6), + TEGRA186_MAIN_GPIO_PORT( N, 0, 0, 7), + TEGRA186_MAIN_GPIO_PORT( O, 0, 1, 4), + TEGRA186_MAIN_GPIO_PORT( P, 4, 0, 7), + TEGRA186_MAIN_GPIO_PORT( Q, 0, 2, 6), + TEGRA186_MAIN_GPIO_PORT( R, 0, 5, 6), + TEGRA186_MAIN_GPIO_PORT( T, 0, 3, 4), + TEGRA186_MAIN_GPIO_PORT( X, 1, 2, 8), + TEGRA186_MAIN_GPIO_PORT( Y, 1, 3, 7), + TEGRA186_MAIN_GPIO_PORT(BB, 2, 3, 2), + TEGRA186_MAIN_GPIO_PORT(CC, 5, 2, 4), }; static const struct tegra_gpio_soc tegra186_main_soc = { @@ -622,23 +627,23 @@ static const struct tegra_gpio_soc tegra186_main_soc = { .instance = 0, }; -#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \ - [TEGRA186_AON_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA186_AON_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra186_aon_ports[] = { - TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0), - TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0), - TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0), - TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0), - TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0), - TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0), - TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0), - TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0), + TEGRA186_AON_GPIO_PORT( S, 0, 1, 5), + TEGRA186_AON_GPIO_PORT( U, 0, 2, 6), + TEGRA186_AON_GPIO_PORT( V, 0, 4, 8), + TEGRA186_AON_GPIO_PORT( W, 0, 5, 8), + TEGRA186_AON_GPIO_PORT( Z, 0, 7, 4), + TEGRA186_AON_GPIO_PORT(AA, 0, 6, 8), + TEGRA186_AON_GPIO_PORT(EE, 0, 3, 3), + TEGRA186_AON_GPIO_PORT(FF, 0, 0, 5), }; static const struct tegra_gpio_soc tegra186_aon_soc = { @@ -648,43 +653,43 @@ static const struct tegra_gpio_soc tegra186_aon_soc = { .instance = 1, }; -#define TEGRA194_MAIN_GPIO_PORT(port, base, count, controller) \ - [TEGRA194_MAIN_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA194_MAIN_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra194_main_ports[] = { - TEGRA194_MAIN_GPIO_PORT( A, 0x1400, 8, 1), - TEGRA194_MAIN_GPIO_PORT( B, 0x4e00, 2, 4), - TEGRA194_MAIN_GPIO_PORT( C, 0x4600, 8, 4), - TEGRA194_MAIN_GPIO_PORT( D, 0x4800, 4, 4), - TEGRA194_MAIN_GPIO_PORT( E, 0x4a00, 8, 4), - TEGRA194_MAIN_GPIO_PORT( F, 0x4c00, 6, 4), - TEGRA194_MAIN_GPIO_PORT( G, 0x4000, 8, 4), - TEGRA194_MAIN_GPIO_PORT( H, 0x4200, 8, 4), - TEGRA194_MAIN_GPIO_PORT( I, 0x4400, 5, 4), - TEGRA194_MAIN_GPIO_PORT( J, 0x5200, 6, 5), - TEGRA194_MAIN_GPIO_PORT( K, 0x3000, 8, 3), - TEGRA194_MAIN_GPIO_PORT( L, 0x3200, 4, 3), - TEGRA194_MAIN_GPIO_PORT( M, 0x2600, 8, 2), - TEGRA194_MAIN_GPIO_PORT( N, 0x2800, 3, 2), - TEGRA194_MAIN_GPIO_PORT( O, 0x5000, 6, 5), - TEGRA194_MAIN_GPIO_PORT( P, 0x2a00, 8, 2), - TEGRA194_MAIN_GPIO_PORT( Q, 0x2c00, 8, 2), - TEGRA194_MAIN_GPIO_PORT( R, 0x2e00, 6, 2), - TEGRA194_MAIN_GPIO_PORT( S, 0x3600, 8, 3), - TEGRA194_MAIN_GPIO_PORT( T, 0x3800, 8, 3), - TEGRA194_MAIN_GPIO_PORT( U, 0x3a00, 1, 3), - TEGRA194_MAIN_GPIO_PORT( V, 0x1000, 8, 1), - TEGRA194_MAIN_GPIO_PORT( W, 0x1200, 2, 1), - TEGRA194_MAIN_GPIO_PORT( X, 0x2000, 8, 2), - TEGRA194_MAIN_GPIO_PORT( Y, 0x2200, 8, 2), - TEGRA194_MAIN_GPIO_PORT( Z, 0x2400, 8, 2), - TEGRA194_MAIN_GPIO_PORT(FF, 0x3400, 2, 3), - TEGRA194_MAIN_GPIO_PORT(GG, 0x0000, 2, 0) + TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8), + TEGRA194_MAIN_GPIO_PORT( B, 4, 7, 2), + TEGRA194_MAIN_GPIO_PORT( C, 4, 3, 8), + TEGRA194_MAIN_GPIO_PORT( D, 4, 4, 4), + TEGRA194_MAIN_GPIO_PORT( E, 4, 5, 8), + TEGRA194_MAIN_GPIO_PORT( F, 4, 6, 6), + TEGRA194_MAIN_GPIO_PORT( G, 4, 0, 8), + TEGRA194_MAIN_GPIO_PORT( H, 4, 1, 8), + TEGRA194_MAIN_GPIO_PORT( I, 4, 2, 5), + TEGRA194_MAIN_GPIO_PORT( J, 5, 1, 6), + TEGRA194_MAIN_GPIO_PORT( K, 3, 0, 8), + TEGRA194_MAIN_GPIO_PORT( L, 3, 1, 4), + TEGRA194_MAIN_GPIO_PORT( M, 2, 3, 8), + TEGRA194_MAIN_GPIO_PORT( N, 2, 4, 3), + TEGRA194_MAIN_GPIO_PORT( O, 5, 0, 6), + TEGRA194_MAIN_GPIO_PORT( P, 2, 5, 8), + TEGRA194_MAIN_GPIO_PORT( Q, 2, 6, 8), + TEGRA194_MAIN_GPIO_PORT( R, 2, 7, 6), + TEGRA194_MAIN_GPIO_PORT( S, 3, 3, 8), + TEGRA194_MAIN_GPIO_PORT( T, 3, 4, 8), + TEGRA194_MAIN_GPIO_PORT( U, 3, 5, 1), + TEGRA194_MAIN_GPIO_PORT( V, 1, 0, 8), + TEGRA194_MAIN_GPIO_PORT( W, 1, 1, 2), + TEGRA194_MAIN_GPIO_PORT( X, 2, 0, 8), + TEGRA194_MAIN_GPIO_PORT( Y, 2, 1, 8), + TEGRA194_MAIN_GPIO_PORT( Z, 2, 2, 8), + TEGRA194_MAIN_GPIO_PORT(FF, 3, 2, 2), + TEGRA194_MAIN_GPIO_PORT(GG, 0, 0, 2) }; static const struct tegra_gpio_soc tegra194_main_soc = { @@ -694,20 +699,20 @@ static const struct tegra_gpio_soc tegra194_main_soc = { .instance = 0, }; -#define TEGRA194_AON_GPIO_PORT(port, base, count, controller) \ - [TEGRA194_AON_GPIO_PORT_##port] = { \ - .name = #port, \ - .offset = base, \ - .pins = count, \ - .irq = controller, \ +#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \ + [TEGRA194_AON_GPIO_PORT_##_name] = { \ + .name = #_name, \ + .bank = _bank, \ + .port = _port, \ + .pins = _pins, \ } static const struct tegra_gpio_port tegra194_aon_ports[] = { - TEGRA194_AON_GPIO_PORT(AA, 0x0600, 8, 0), - TEGRA194_AON_GPIO_PORT(BB, 0x0800, 4, 0), - TEGRA194_AON_GPIO_PORT(CC, 0x0200, 8, 0), - TEGRA194_AON_GPIO_PORT(DD, 0x0400, 3, 0), - TEGRA194_AON_GPIO_PORT(EE, 0x0000, 7, 0) + TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8), + TEGRA194_AON_GPIO_PORT(BB, 0, 4, 4), + TEGRA194_AON_GPIO_PORT(CC, 0, 1, 8), + TEGRA194_AON_GPIO_PORT(DD, 0, 2, 3), + TEGRA194_AON_GPIO_PORT(EE, 0, 0, 7) }; static const struct tegra_gpio_soc tegra194_aon_soc = { From 22635ed8a20d47ae3a590171aee90ccecfb62241 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 8 Nov 2019 16:33:52 +0100 Subject: [PATCH 68/71] gpio: tegra186: Program interrupt route mapping The controls for the GG port on Tegra194 resides in the power partition of the C5 PCIe controller and its interrupt route mapping can therefore not be programmed by early boot firmware along with that of the other ports. Detect this generically by looking at which controls have already been locked down using the security registers and fill in default values for controls that are unlocked. Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 46 ++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 1e9993143eb4e..cb5efb83bad42 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -15,6 +15,14 @@ #include #include +/* security registers */ +#define TEGRA186_GPIO_CTL_SCR 0x0c +#define TEGRA186_GPIO_CTL_SCR_SEC_WEN BIT(28) +#define TEGRA186_GPIO_CTL_SCR_SEC_REN BIT(27) + +#define TEGRA186_GPIO_INT_ROUTE_MAPPING(p, x) (0x14 + (p) * 0x20 + (x) * 4) + +/* control registers */ #define TEGRA186_GPIO_ENABLE_CONFIG 0x00 #define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0) #define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1) @@ -64,6 +72,7 @@ struct tegra_gpio { const struct tegra_gpio_soc *soc; + void __iomem *secure; void __iomem *base; }; @@ -449,6 +458,37 @@ static const struct of_device_id tegra186_pmc_of_match[] = { { /* sentinel */ } }; +static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio) +{ + unsigned int i, j; + u32 value; + + for (i = 0; i < gpio->soc->num_ports; i++) { + const struct tegra_gpio_port *port = &gpio->soc->ports[i]; + unsigned int offset, p = port->port; + void __iomem *base; + + base = gpio->secure + port->bank * 0x1000 + 0x800; + + value = readl(base + TEGRA186_GPIO_CTL_SCR); + + /* + * For controllers that haven't been locked down yet, make + * sure to program the default interrupt route mapping. + */ + if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 && + (value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 0) { + for (j = 0; j < 8; j++) { + offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j); + + value = readl(base + offset); + value = BIT(port->pins) - 1; + writel(value, base + offset); + } + } + } +} + static int tegra186_gpio_probe(struct platform_device *pdev) { unsigned int i, j, offset; @@ -464,6 +504,10 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->soc = of_device_get_match_data(&pdev->dev); + gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security"); + if (IS_ERR(gpio->secure)) + return PTR_ERR(gpio->secure); + gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio"); if (IS_ERR(gpio->base)) return PTR_ERR(gpio->base); @@ -558,6 +602,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev) return -EPROBE_DEFER; } + tegra186_gpio_init_route_mapping(gpio); + irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio, sizeof(*irq->map), GFP_KERNEL); if (!irq->map) From adce1183932265e94bbaf92138b9c98d1c5359b9 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 8 Nov 2019 16:33:53 +0100 Subject: [PATCH 69/71] gpio: tegra186: Add debounce support The GPIO controller found on Tegra186 and later supports debouncing for inputs for up to 255 ms. Signed-off-by: Thierry Reding Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra186.c | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index cb5efb83bad42..55b43b7ce88db 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -32,6 +32,7 @@ #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE (0x3 << 2) #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK (0x3 << 2) #define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL BIT(4) +#define TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE BIT(5) #define TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT BIT(6) #define TEGRA186_GPIO_DEBOUNCE_CONTROL 0x04 @@ -217,6 +218,42 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset, writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE); } +static int tegra186_gpio_set_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct tegra_gpio *gpio = gpiochip_get_data(chip); + u32 debounce, value; + void __iomem *base; + + base = tegra186_gpio_get_base(gpio, offset); + if (base == NULL) + return -ENXIO; + + if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) + return -ENOTSUPP; + + debounce = pinconf_to_config_argument(config); + + /* + * The Tegra186 GPIO controller supports a maximum of 255 ms debounce + * time. + */ + if (debounce > 255000) + return -EINVAL; + + debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC); + + value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce); + writel(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL); + + value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG); + value |= TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE; + writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG); + + return 0; +} + static int tegra186_gpio_of_xlate(struct gpio_chip *chip, const struct of_phandle_args *spec, u32 *flags) @@ -539,6 +576,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->gpio.direction_output = tegra186_gpio_direction_output; gpio->gpio.get = tegra186_gpio_get, gpio->gpio.set = tegra186_gpio_set; + gpio->gpio.set_config = tegra186_gpio_set_config; gpio->gpio.base = -1; From be053b2dc91c1a02881db0c90e9438b56ac1a019 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 13 Nov 2019 11:11:03 +0100 Subject: [PATCH 70/71] gpio: em: Use platform_get_irq() to obtain interrupts Use the platform_get_irq() helper instead of handling resources directly. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-em.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index adc281daacff4..17a243c528ade 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -269,13 +269,12 @@ static void em_gio_irq_domain_remove(void *data) static int em_gio_probe(struct platform_device *pdev) { struct em_gio_priv *p; - struct resource *irq[2]; struct gpio_chip *gpio_chip; struct irq_chip *irq_chip; struct device *dev = &pdev->dev; const char *name = dev_name(dev); unsigned int ngpios; - int ret; + int irq[2], ret; p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL); if (!p) @@ -285,13 +284,13 @@ static int em_gio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, p); spin_lock_init(&p->sense_lock); - irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1); + irq[0] = platform_get_irq(pdev, 0); + if (irq[0] < 0) + return irq[0]; - if (!irq[0] || !irq[1]) { - dev_err(dev, "missing IRQ or IOMEM\n"); - return -EINVAL; - } + irq[1] = platform_get_irq(pdev, 1); + if (irq[1] < 0) + return irq[1]; p->base0 = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p->base0)) @@ -342,14 +341,12 @@ static int em_gio_probe(struct platform_device *pdev) if (ret) return ret; - if (devm_request_irq(dev, irq[0]->start, - em_gio_irq_handler, 0, name, p)) { + if (devm_request_irq(dev, irq[0], em_gio_irq_handler, 0, name, p)) { dev_err(dev, "failed to request low IRQ\n"); return -ENOENT; } - if (devm_request_irq(dev, irq[1]->start, - em_gio_irq_handler, 0, name, p)) { + if (devm_request_irq(dev, irq[1], em_gio_irq_handler, 0, name, p)) { dev_err(dev, "failed to request high IRQ\n"); return -ENOENT; } From 0f67f16a6e88749fc3bf88da7515d3fff472a1cc Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Wed, 13 Nov 2019 10:43:52 +0200 Subject: [PATCH 71/71] gpio: mmio: remove untrue leftover comment The comment should have been removed when new GPIO direction definitions were taken in use as the function logic was changed. It is now perfectly valid and Ok to hit the return from the bottom of the direction getting function. Signed-off-by: Matti Vaittinen Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mmio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c index cd07c948649fe..f729e3e9e9837 100644 --- a/drivers/gpio/gpio-mmio.c +++ b/drivers/gpio/gpio-mmio.c @@ -386,7 +386,6 @@ static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio) if (!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio))) return GPIO_LINE_DIRECTION_OUT; - /* This should not happen */ return GPIO_LINE_DIRECTION_IN; }