diff options
Diffstat (limited to 'drivers/base')
27 files changed, 571 insertions, 237 deletions
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index de8587cc119e..c1179edc0f3b 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -21,17 +21,94 @@ #include <linux/sched.h> #include <linux/smp.h> +static DEFINE_PER_CPU(struct scale_freq_data *, sft_data); +static struct cpumask scale_freq_counters_mask; +static bool scale_freq_invariant; + +static bool supports_scale_freq_counters(const struct cpumask *cpus) +{ + return cpumask_subset(cpus, &scale_freq_counters_mask); +} + bool topology_scale_freq_invariant(void) { return cpufreq_supports_freq_invariance() || - arch_freq_counters_available(cpu_online_mask); + supports_scale_freq_counters(cpu_online_mask); } -__weak bool arch_freq_counters_available(const struct cpumask *cpus) +static void update_scale_freq_invariant(bool status) { - return false; + if (scale_freq_invariant == status) + return; + + /* + * Task scheduler behavior depends on frequency invariance support, + * either cpufreq or counter driven. If the support status changes as + * a result of counter initialisation and use, retrigger the build of + * scheduling domains to ensure the information is propagated properly. + */ + if (topology_scale_freq_invariant() == status) { + scale_freq_invariant = status; + rebuild_sched_domains_energy(); + } } -DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE; + +void topology_set_scale_freq_source(struct scale_freq_data *data, + const struct cpumask *cpus) +{ + struct scale_freq_data *sfd; + int cpu; + + /* + * Avoid calling rebuild_sched_domains() unnecessarily if FIE is + * supported by cpufreq. + */ + if (cpumask_empty(&scale_freq_counters_mask)) + scale_freq_invariant = topology_scale_freq_invariant(); + + for_each_cpu(cpu, cpus) { + sfd = per_cpu(sft_data, cpu); + + /* Use ARCH provided counters whenever possible */ + if (!sfd || sfd->source != SCALE_FREQ_SOURCE_ARCH) { + per_cpu(sft_data, cpu) = data; + cpumask_set_cpu(cpu, &scale_freq_counters_mask); + } + } + + update_scale_freq_invariant(true); +} +EXPORT_SYMBOL_GPL(topology_set_scale_freq_source); + +void topology_clear_scale_freq_source(enum scale_freq_source source, + const struct cpumask *cpus) +{ + struct scale_freq_data *sfd; + int cpu; + + for_each_cpu(cpu, cpus) { + sfd = per_cpu(sft_data, cpu); + + if (sfd && sfd->source == source) { + per_cpu(sft_data, cpu) = NULL; + cpumask_clear_cpu(cpu, &scale_freq_counters_mask); + } + } + + update_scale_freq_invariant(false); +} +EXPORT_SYMBOL_GPL(topology_clear_scale_freq_source); + +void topology_scale_freq_tick(void) +{ + struct scale_freq_data *sfd = *this_cpu_ptr(&sft_data); + + if (sfd) + sfd->set_freq_scale(); +} + +DEFINE_PER_CPU(unsigned long, arch_freq_scale) = SCHED_CAPACITY_SCALE; +EXPORT_PER_CPU_SYMBOL_GPL(arch_freq_scale); void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, unsigned long max_freq) @@ -47,13 +124,13 @@ void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq, * want to update the scale factor with information from CPUFREQ. * Instead the scale factor will be updated from arch_scale_freq_tick. */ - if (arch_freq_counters_available(cpus)) + if (supports_scale_freq_counters(cpus)) return; scale = (cur_freq << SCHED_CAPACITY_SHIFT) / max_freq; for_each_cpu(i, cpus) - per_cpu(freq_scale, i) = scale; + per_cpu(arch_freq_scale, i) = scale; } DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE; diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index f7bd0f4db13d..9c00d203d61e 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -461,6 +461,10 @@ attribute_container_add_class_device(struct device *classdev) /** * attribute_container_add_class_device_adapter - simple adapter for triggers * + * @cont: the container to register. + * @dev: the generic device to activate the trigger for + * @classdev: the class device to add + * * This function is identical to attribute_container_add_class_device except * that it is designed to be called from the triggers */ diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index d8b314e7d0fd..adc199dfba3c 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -265,8 +265,3 @@ void __init auxiliary_bus_init(void) { WARN_ON(bus_register(&auxiliary_bus_type)); } - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Auxiliary Bus"); -MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>"); -MODULE_AUTHOR("Kiran Patil <kiran.patil@intel.com>"); diff --git a/drivers/base/base.h b/drivers/base/base.h index 52b3d7b75c27..e5f9b7e656c3 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -185,11 +185,13 @@ extern int device_links_read_lock(void); extern void device_links_read_unlock(int idx); extern int device_links_read_lock_held(void); extern int device_links_check_suppliers(struct device *dev); +extern void device_links_force_bind(struct device *dev); extern void device_links_driver_bound(struct device *dev); extern void device_links_driver_cleanup(struct device *dev); extern void device_links_no_driver(struct device *dev); extern bool device_links_busy(struct device *dev); extern void device_links_unbind_consumers(struct device *dev); +extern void fw_devlink_drivers_done(void); /* device pm support */ void device_pm_move_to_tail(struct device *dev); diff --git a/drivers/base/component.c b/drivers/base/component.c index dcfbe7251dc4..272ba42392f0 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -65,7 +65,6 @@ struct master { const struct component_master_ops *ops; struct device *dev; struct component_match *match; - struct dentry *dentry; }; struct component { @@ -125,15 +124,13 @@ core_initcall(component_debug_init); static void component_master_debugfs_add(struct master *m) { - m->dentry = debugfs_create_file(dev_name(m->dev), 0444, - component_debugfs_dir, - m, &component_devices_fops); + debugfs_create_file(dev_name(m->dev), 0444, component_debugfs_dir, m, + &component_devices_fops); } static void component_master_debugfs_del(struct master *m) { - debugfs_remove(m->dentry); - m->dentry = NULL; + debugfs_remove(debugfs_lookup(dev_name(m->dev), component_debugfs_dir)); } #else diff --git a/drivers/base/core.c b/drivers/base/core.c index f29839382f81..4a8bf8cda52b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -51,6 +51,7 @@ static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; static DEFINE_MUTEX(fwnode_link_lock); static bool fw_devlink_is_permissive(void); +static bool fw_devlink_drv_reg_done; /** * fwnode_link_add - Create a link between two fwnode_handles. @@ -1154,6 +1155,41 @@ static ssize_t waiting_for_supplier_show(struct device *dev, static DEVICE_ATTR_RO(waiting_for_supplier); /** + * device_links_force_bind - Prepares device to be force bound + * @dev: Consumer device. + * + * device_bind_driver() force binds a device to a driver without calling any + * driver probe functions. So the consumer really isn't going to wait for any + * supplier before it's bound to the driver. We still want the device link + * states to be sensible when this happens. + * + * In preparation for device_bind_driver(), this function goes through each + * supplier device links and checks if the supplier is bound. If it is, then + * the device link status is set to CONSUMER_PROBE. Otherwise, the device link + * is dropped. Links without the DL_FLAG_MANAGED flag set are ignored. + */ +void device_links_force_bind(struct device *dev) +{ + struct device_link *link, *ln; + + device_links_write_lock(); + + list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) { + if (!(link->flags & DL_FLAG_MANAGED)) + continue; + + if (link->status != DL_STATE_AVAILABLE) { + device_link_drop_managed(link); + continue; + } + WRITE_ONCE(link->status, DL_STATE_CONSUMER_PROBE); + } + dev->links.status = DL_DEV_PROBING; + + device_links_write_unlock(); +} + +/** * device_links_driver_bound - Update device links after probing its driver. * @dev: Device to update the links for. * @@ -1503,7 +1539,7 @@ static void device_links_purge(struct device *dev) #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \ DL_FLAG_PM_RUNTIME) -static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE; +static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON; static int __init fw_devlink_setup(char *arg) { if (!arg) @@ -1563,6 +1599,52 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) fw_devlink_parse_fwtree(child); } +static void fw_devlink_relax_link(struct device_link *link) +{ + if (!(link->flags & DL_FLAG_INFERRED)) + return; + + if (link->flags == (DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE)) + return; + + pm_runtime_drop_link(link); + link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE; + dev_dbg(link->consumer, "Relaxing link with %s\n", + dev_name(link->supplier)); +} + +static int fw_devlink_no_driver(struct device *dev, void *data) +{ + struct device_link *link = to_devlink(dev); + + if (!link->supplier->can_match) + fw_devlink_relax_link(link); + + return 0; +} + +void fw_devlink_drivers_done(void) +{ + fw_devlink_drv_reg_done = true; + device_links_write_lock(); + class_for_each_device(&devlink_class, NULL, NULL, + fw_devlink_no_driver); + device_links_write_unlock(); +} + +static void fw_devlink_unblock_consumers(struct device *dev) +{ + struct device_link *link; + + if (!fw_devlink_flags || fw_devlink_is_permissive()) + return; + + device_links_write_lock(); + list_for_each_entry(link, &dev->links.consumers, s_node) + fw_devlink_relax_link(link); + device_links_write_unlock(); +} + /** * fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links * @con: Device to check dependencies for. @@ -1599,21 +1681,16 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup) ret = 1; - if (!(link->flags & DL_FLAG_INFERRED)) - continue; - - pm_runtime_drop_link(link); - link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE; - dev_dbg(link->consumer, "Relaxing link with %s\n", - dev_name(link->supplier)); + fw_devlink_relax_link(link); } return ret; } /** * fw_devlink_create_devlink - Create a device link from a consumer to fwnode - * @con - Consumer device for the device link - * @sup_handle - fwnode handle of supplier + * @con: consumer device for the device link + * @sup_handle: fwnode handle of supplier + * @flags: devlink flags * * This function will try to create a device link between the consumer device * @con and the supplier device represented by @sup_handle. @@ -1709,7 +1786,7 @@ out: /** * __fw_devlink_link_to_consumers - Create device links to consumers of a device - * @dev - Device that needs to be linked to its consumers + * @dev: Device that needs to be linked to its consumers * * This function looks at all the consumer fwnodes of @dev and creates device * links between the consumer device and @dev (supplier). @@ -1779,8 +1856,8 @@ static void __fw_devlink_link_to_consumers(struct device *dev) /** * __fw_devlink_link_to_suppliers - Create device links to suppliers of a device - * @dev - The consumer device that needs to be linked to its suppliers - * @fwnode - Root of the fwnode tree that is used to create device links + * @dev: The consumer device that needs to be linked to its suppliers + * @fwnode: Root of the fwnode tree that is used to create device links * * This function looks at all the supplier fwnodes of fwnode tree rooted at * @fwnode and creates device links between @dev (consumer) and all the @@ -3240,6 +3317,15 @@ int device_add(struct device *dev) } bus_probe_device(dev); + + /* + * If all driver registration is done and a newly added device doesn't + * match with any driver, don't block its consumers from probing in + * case the consumer device is able to operate without this supplier. + */ + if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match) + fw_devlink_unblock_consumers(dev); + if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 8f1d6569564c..2b9e41377a07 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -409,13 +409,11 @@ __cpu_device_create(struct device *parent, void *drvdata, const char *fmt, va_list args) { struct device *dev = NULL; - int retval = -ENODEV; + int retval = -ENOMEM; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - retval = -ENOMEM; + if (!dev) goto error; - } device_initialize(dev); dev->parent = parent; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 37a5e5f8b221..ecd7cf848daf 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -55,7 +55,6 @@ static DEFINE_MUTEX(deferred_probe_mutex); static LIST_HEAD(deferred_probe_pending_list); static LIST_HEAD(deferred_probe_active_list); static atomic_t deferred_trigger_count = ATOMIC_INIT(0); -static struct dentry *deferred_devices; static bool initcalls_done; /* Save the async probe drivers' name from kernel cmdline */ @@ -69,6 +68,12 @@ static char async_probe_drv_names[ASYNC_DRV_NAMES_MAX_LEN]; */ static bool defer_all_probes; +static void __device_set_deferred_probe_reason(const struct device *dev, char *reason) +{ + kfree(dev->p->deferred_probe_reason); + dev->p->deferred_probe_reason = reason; +} + /* * deferred_probe_work_func() - Retry probing devices in the active list. */ @@ -97,8 +102,7 @@ static void deferred_probe_work_func(struct work_struct *work) get_device(dev); - kfree(dev->p->deferred_probe_reason); - dev->p->deferred_probe_reason = NULL; + __device_set_deferred_probe_reason(dev, NULL); /* * Drop the mutex while probing each device; the probe path may @@ -126,6 +130,9 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); void driver_deferred_probe_add(struct device *dev) { + if (!dev->can_match) + return; + mutex_lock(&deferred_probe_mutex); if (list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Added to deferred list\n"); @@ -140,8 +147,7 @@ void driver_deferred_probe_del(struct device *dev) if (!list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Removed from deferred list\n"); list_del_init(&dev->p->deferred_probe); - kfree(dev->p->deferred_probe_reason); - dev->p->deferred_probe_reason = NULL; + __device_set_deferred_probe_reason(dev, NULL); } mutex_unlock(&deferred_probe_mutex); } @@ -185,7 +191,7 @@ static void driver_deferred_probe_trigger(void) * Kick the re-probe thread. It may already be scheduled, but it is * safe to kick it again. */ - schedule_work(&deferred_probe_work); + queue_work(system_unbound_wq, &deferred_probe_work); } /** @@ -220,11 +226,12 @@ void device_unblock_probing(void) void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf) { const char *drv = dev_driver_string(dev); + char *reason; mutex_lock(&deferred_probe_mutex); - kfree(dev->p->deferred_probe_reason); - dev->p->deferred_probe_reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf); + reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf); + __device_set_deferred_probe_reason(dev, reason); mutex_unlock(&deferred_probe_mutex); } @@ -294,6 +301,8 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) { struct device_private *p; + fw_devlink_drivers_done(); + driver_deferred_probe_timeout = 0; driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); @@ -315,8 +324,8 @@ static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_ */ static int deferred_probe_initcall(void) { - deferred_devices = debugfs_create_file("devices_deferred", 0444, NULL, - NULL, &deferred_devs_fops); + debugfs_create_file("devices_deferred", 0444, NULL, NULL, + &deferred_devs_fops); driver_deferred_probe_enable = true; driver_deferred_probe_trigger(); @@ -324,6 +333,9 @@ static int deferred_probe_initcall(void) flush_work(&deferred_probe_work); initcalls_done = true; + if (!IS_ENABLED(CONFIG_MODULES)) + fw_devlink_drivers_done(); + /* * Trigger deferred probe again, this time we won't defer anything * that is optional @@ -341,7 +353,7 @@ late_initcall(deferred_probe_initcall); static void __exit deferred_probe_exit(void) { - debugfs_remove_recursive(deferred_devices); + debugfs_remove_recursive(debugfs_lookup("devices_deferred", NULL)); } __exitcall(deferred_probe_exit); @@ -418,8 +430,11 @@ static int driver_sysfs_add(struct device *dev) if (ret) goto rm_dev; - if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump || - !device_create_file(dev, &dev_attr_coredump)) + if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump) + return 0; + + ret = device_create_file(dev, &dev_attr_coredump); + if (!ret) return 0; sysfs_remove_link(&dev->kobj, "driver"); @@ -462,8 +477,10 @@ int device_bind_driver(struct device *dev) int ret; ret = driver_sysfs_add(dev); - if (!ret) + if (!ret) { + device_links_force_bind(dev); driver_bound(dev); + } else if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); @@ -731,6 +748,7 @@ static int driver_probe_device(struct device_driver *drv, struct device *dev) if (!device_is_registered(dev)) return -ENODEV; + dev->can_match = true; pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); @@ -834,6 +852,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); + dev->can_match = true; driver_deferred_probe_add(dev); } else if (ret < 0) { dev_dbg(dev, "Bus failed to match device: %d\n", ret); @@ -1069,6 +1088,7 @@ static int __driver_attach(struct device *dev, void *data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); + dev->can_match = true; driver_deferred_probe_add(dev); } else if (ret < 0) { dev_dbg(dev, "Bus failed to match device: %d\n", ret); diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 9243468e2c99..8eec0e0ddff7 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -202,7 +202,7 @@ static int devcd_match_failing(struct device *dev, const void *failing) * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained * using the sg_chain function then that function should be called only once * on the chained table - * @table: pointer to sg_table to free + * @data: pointer to sg_table to free */ static void devcd_free_sgtable(void *data) { @@ -210,7 +210,7 @@ static void devcd_free_sgtable(void *data) } /** - * devcd_read_from_table - copy data from sg_table to a given buffer + * devcd_read_from_sgtable - copy data from sg_table to a given buffer * and return the number of bytes read * @buffer: the buffer to copy the data to it * @buf_len: the length of the buffer @@ -292,13 +292,16 @@ void dev_coredumpm(struct device *dev, struct module *owner, if (device_add(&devcd->devcd_dev)) goto put_device; + /* + * These should normally not fail, but there is no problem + * continuing without the links, so just warn instead of + * failing. + */ if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj, - "failing_device")) - /* nothing - symlink will be missing */; - - if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj, - "devcoredump")) - /* nothing - symlink will be missing */; + "failing_device") || + sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj, + "devcoredump")) + dev_warn(dev, "devcoredump create_link failed\n"); INIT_DELAYED_WORK(&devcd->del_wk, devcd_del); schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT); diff --git a/drivers/base/devres.c b/drivers/base/devres.c index fb9d5289a620..8746f2212781 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -58,8 +58,8 @@ static void devres_log(struct device *dev, struct devres_node *node, const char *op) { if (unlikely(log_devres)) - dev_err(dev, "DEVRES %3s %p %s (%lu bytes)\n", - op, node, node->name, (unsigned long)node->size); + dev_err(dev, "DEVRES %3s %p %s (%zu bytes)\n", + op, node, node->name, node->size); } #else /* CONFIG_DEBUG_DEVRES */ #define set_node_dbginfo(node, n, s) do {} while (0) @@ -1228,6 +1228,6 @@ EXPORT_SYMBOL_GPL(__devm_alloc_percpu); void devm_free_percpu(struct device *dev, void __percpu *pdata) { WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match, - (void *)pdata)); + (__force void *)pdata)); } EXPORT_SYMBOL_GPL(devm_free_percpu); diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 653c8c6ac7a7..8be352ab4ddb 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -371,7 +371,7 @@ int __init devtmpfs_mount(void) return err; } -static DECLARE_COMPLETION(setup_done); +static __initdata DECLARE_COMPLETION(setup_done); static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) @@ -405,7 +405,7 @@ static void __noreturn devtmpfs_work_loop(void) } } -static int __init devtmpfs_setup(void *p) +static noinline int __init devtmpfs_setup(void *p) { int err; @@ -419,7 +419,6 @@ static int __init devtmpfs_setup(void *p) init_chroot("."); out: *(int *)p = err; - complete(&setup_done); return err; } @@ -432,6 +431,7 @@ static int __ref devtmpfsd(void *p) { int err = devtmpfs_setup(p); + complete(&setup_done); if (err) return err; devtmpfs_work_loop(); diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 78355095e00d..4fdb8219cd08 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -15,6 +15,7 @@ #include <linux/kernel_read_file.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/initrd.h> #include <linux/timer.h> #include <linux/vmalloc.h> #include <linux/interrupt.h> @@ -504,6 +505,7 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, if (!path) return -ENOMEM; + wait_for_initramfs(); for (i = 0; i < ARRAY_SIZE(fw_path); i++) { size_t file_size = 0; size_t *file_size_ptr = NULL; diff --git a/drivers/base/memory.c b/drivers/base/memory.c index f35298425575..b31b3af5c490 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -169,30 +169,98 @@ int memory_notify(unsigned long val, void *v) return blocking_notifier_call_chain(&memory_chain, val, v); } +static int memory_block_online(struct memory_block *mem) +{ + unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr); + unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; + unsigned long nr_vmemmap_pages = mem->nr_vmemmap_pages; + struct zone *zone; + int ret; + + zone = zone_for_pfn_range(mem->online_type, mem->nid, start_pfn, nr_pages); + + /* + * Although vmemmap pages have a different lifecycle than the pages + * they describe (they remain until the memory is unplugged), doing + * their initialization and accounting at memory onlining/offlining + * stage helps to keep accounting easier to follow - e.g vmemmaps + * belong to the same zone as the memory they backed. + */ + if (nr_vmemmap_pages) { + ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone); + if (ret) + return ret; + } + + ret = online_pages(start_pfn + nr_vmemmap_pages, + nr_pages - nr_vmemmap_pages, zone); + if (ret) { + if (nr_vmemmap_pages) + mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages); + return ret; + } + + /* + * Account once onlining succeeded. If the zone was unpopulated, it is + * now already properly populated. + */ + if (nr_vmemmap_pages) + adjust_present_page_count(zone, nr_vmemmap_pages); + + return ret; +} + +static int memory_block_offline(struct memory_block *mem) +{ + unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr); + unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; + unsigned long nr_vmemmap_pages = mem->nr_vmemmap_pages; + struct zone *zone; + int ret; + + zone = page_zone(pfn_to_page(start_pfn)); + + /* + * Unaccount before offlining, such that unpopulated zone and kthreads + * can properly be torn down in offline_pages(). + */ + if (nr_vmemmap_pages) + adjust_present_page_count(zone, -nr_vmemmap_pages); + + ret = offline_pages(start_pfn + nr_vmemmap_pages, + nr_pages - nr_vmemmap_pages); + if (ret) { + /* offline_pages() failed. Account back. */ + if (nr_vmemmap_pages) + adjust_present_page_count(zone, nr_vmemmap_pages); + return ret; + } + + if (nr_vmemmap_pages) + mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages); + + return ret; +} + /* * MEMORY_HOTPLUG depends on SPARSEMEM in mm/Kconfig, so it is * OK to have direct references to sparsemem variables in here. */ static int -memory_block_action(unsigned long start_section_nr, unsigned long action, - int online_type, int nid) +memory_block_action(struct memory_block *mem, unsigned long action) { - unsigned long start_pfn; - unsigned long nr_pages = PAGES_PER_SECTION * sections_per_block; int ret; - start_pfn = section_nr_to_pfn(start_section_nr); - switch (action) { case MEM_ONLINE: - ret = online_pages(start_pfn, nr_pages, online_type, nid); + ret = memory_block_online(mem); break; case MEM_OFFLINE: - ret = offline_pages(start_pfn, nr_pages); + ret = memory_block_offline(mem); break; default: WARN(1, KERN_WARNING "%s(%ld, %ld) unknown action: " - "%ld\n", __func__, start_section_nr, action, action); + "%ld\n", __func__, mem->start_section_nr, action, action); ret = -EINVAL; } @@ -210,9 +278,7 @@ static int memory_block_change_state(struct memory_block *mem, if (to_state == MEM_OFFLINE) mem->state = MEM_GOING_OFFLINE; - ret = memory_block_action(mem->start_section_nr, to_state, - mem->online_type, mem->nid); - + ret = memory_block_action(mem, to_state); mem->state = ret ? from_state_req : to_state; return ret; @@ -567,7 +633,8 @@ int register_memory(struct memory_block *memory) return ret; } -static int init_memory_block(unsigned long block_id, unsigned long state) +static int init_memory_block(unsigned long block_id, unsigned long state, + unsigned long nr_vmemmap_pages) { struct memory_block *mem; int ret = 0; @@ -584,6 +651,7 @@ static int init_memory_block(unsigned long block_id, unsigned long state) mem->start_section_nr = block_id * sections_per_block; mem->state = state; mem->nid = NUMA_NO_NODE; + mem->nr_vmemmap_pages = nr_vmemmap_pages; ret = register_memory(mem); @@ -603,7 +671,7 @@ static int add_memory_block(unsigned long base_section_nr) if (section_count == 0) return 0; return init_memory_block(memory_block_id(base_section_nr), - MEM_ONLINE); + MEM_ONLINE, 0); } static void unregister_memory(struct memory_block *memory) @@ -625,7 +693,8 @@ static void unregister_memory(struct memory_block *memory) * * Called under device_hotplug_lock. */ -int create_memory_block_devices(unsigned long start, unsigned long size) +int create_memory_block_devices(unsigned long start, unsigned long size, + unsigned long vmemmap_pages) { const unsigned long start_block_id = pfn_to_block_id(PFN_DOWN(start)); unsigned long end_block_id = pfn_to_block_id(PFN_DOWN(start + size)); @@ -638,7 +707,7 @@ int create_memory_block_devices(unsigned long start, unsigned long size) return -EINVAL; for (block_id = start_block_id; block_id != end_block_id; block_id++) { - ret = init_memory_block(block_id, MEM_OFFLINE); + ret = init_memory_block(block_id, MEM_OFFLINE, vmemmap_pages); if (ret) break; } diff --git a/drivers/base/node.c b/drivers/base/node.c index f449dbb2c746..2c36f61d30bc 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -268,21 +268,20 @@ static void node_init_cache_dev(struct node *node) if (!dev) return; + device_initialize(dev); dev->parent = &node->dev; dev->release = node_cache_release; if (dev_set_name(dev, "memory_side_cache")) - goto free_dev; + goto put_device; - if (device_register(dev)) - goto free_name; + if (device_add(dev)) + goto put_device; pm_runtime_no_callbacks(dev); node->cache_dev = dev; return; -free_name: - kfree_const(dev->kobj.name); -free_dev: - kfree(dev); +put_device: + put_device(dev); } /** @@ -319,25 +318,24 @@ void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs) return; dev = &info->dev; + device_initialize(dev); dev->parent = node->cache_dev; dev->release = node_cacheinfo_release; dev->groups = cache_groups; if (dev_set_name(dev, "index%d", cache_attrs->level)) - goto free_cache; + goto put_device; info->cache_attrs = *cache_attrs; - if (device_register(dev)) { + if (device_add(dev)) { dev_warn(&node->dev, "failed to add cache level:%d\n", cache_attrs->level); - goto free_name; + goto put_device; } pm_runtime_no_callbacks(dev); list_add_tail(&info->node, &node->cache_attrs); return; -free_name: - kfree_const(dev->kobj.name); -free_cache: - kfree(info); +put_device: + put_device(dev); } static void node_remove_caches(struct node *node) diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 2c1e2e0c1a59..0b72b134a304 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -316,10 +316,11 @@ void *platform_msi_get_host_data(struct irq_domain *domain) } /** - * platform_msi_create_device_domain - Create a platform-msi domain + * __platform_msi_create_device_domain - Create a platform-msi domain * * @dev: The device generating the MSIs * @nvec: The number of MSIs that need to be allocated + * @is_tree: flag to indicate tree hierarchy * @write_msi_msg: Callback to write an interrupt message for @dev * @ops: The hierarchy domain operations to use * @host_data: Private data associated to this domain diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 6e1f8e0b661c..9cd34def2237 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -192,7 +192,7 @@ 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 */ if (!dev || num >= dev->archdata.num_irqs) - return -ENXIO; + goto out_not_found; ret = dev->archdata.irqs[num]; goto out; #else @@ -223,10 +223,8 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num) struct irq_data *irqd; irqd = irq_get_irq_data(r->start); - if (!irqd) { - ret = -ENXIO; - goto out; - } + if (!irqd) + goto out_not_found; irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); } @@ -249,8 +247,9 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num) goto out; } - ret = -ENXIO; #endif +out_not_found: + ret = -ENXIO; out: WARN(ret == 0, "0 is an invalid IRQ number\n"); return ret; diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c index 84d5acb6301b..0251f3e6e61d 100644 --- a/drivers/base/power/clock_ops.c +++ b/drivers/base/power/clock_ops.c @@ -140,7 +140,7 @@ static void pm_clk_op_unlock(struct pm_subsys_data *psd, unsigned long *flags) } /** - * pm_clk_enable - Enable a clock, reporting any errors + * __pm_clk_enable - Enable a clock, reporting any errors * @dev: The device for the given clock * @ce: PM clock entry corresponding to the clock. */ diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 78c310d3179d..b6a782c31613 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -1088,34 +1088,6 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock, } /** - * resume_needed - Check whether to resume a device before system suspend. - * @dev: Device to check. - * @genpd: PM domain the device belongs to. - * - * There are two cases in which a device that can wake up the system from sleep - * states should be resumed by genpd_prepare(): (1) if the device is enabled - * to wake up the system and it has to remain active for this purpose while the - * system is in the sleep state and (2) if the device is not enabled to wake up - * the system from sleep states and it generally doesn't generate wakeup signals - * by itself (those signals are generated on its behalf by other parts of the - * system). In the latter case it may be necessary to reconfigure the device's - * wakeup settings during system suspend, because it may have been set up to - * signal remote wakeup from the system's working state as needed by runtime PM. - * Return 'true' in either of the above cases. - */ -static bool resume_needed(struct device *dev, - const struct generic_pm_domain *genpd) -{ - bool active_wakeup; - - if (!device_can_wakeup(dev)) - return false; - - active_wakeup = genpd_is_active_wakeup(genpd); - return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; -} - -/** * genpd_prepare - Start power transition of a device in a PM domain. * @dev: Device to start the transition of. * @@ -1135,14 +1107,6 @@ static int genpd_prepare(struct device *dev) if (IS_ERR(genpd)) return -EINVAL; - /* - * If a wakeup request is pending for the device, it should be woken up - * at this point and a system wakeup event should be reported if it's - * set up to wake up the system from sleep states. - */ - if (resume_needed(dev, genpd)) - pm_runtime_resume(dev); - genpd_lock(genpd); if (genpd->prepared_count++ == 0) diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index fe1dad68aee4..1fc1a992f90c 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -951,7 +951,7 @@ static void pm_runtime_work(struct work_struct *work) /** * pm_suspend_timer_fn - Timer function for pm_schedule_suspend(). - * @data: Device pointer passed by pm_schedule_suspend(). + * @timer: hrtimer used by pm_schedule_suspend(). * * Check if the time is right and queue a suspend request. */ diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 92073ac68473..f0b37c188514 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -400,9 +400,9 @@ void device_wakeup_detach_irq(struct device *dev) } /** - * device_wakeup_arm_wake_irqs(void) + * device_wakeup_arm_wake_irqs - * - * Itereates over the list of device wakeirqs to arm them. + * Iterates over the list of device wakeirqs to arm them. */ void device_wakeup_arm_wake_irqs(void) { @@ -416,9 +416,9 @@ void device_wakeup_arm_wake_irqs(void) } /** - * device_wakeup_disarm_wake_irqs(void) + * device_wakeup_disarm_wake_irqs - * - * Itereates over the list of device wakeirqs to disarm them. + * Iterates over the list of device wakeirqs to disarm them. */ void device_wakeup_disarm_wake_irqs(void) { @@ -532,6 +532,7 @@ EXPORT_SYMBOL_GPL(device_init_wakeup); /** * device_set_wakeup_enable - Enable or disable a device to wake up the system. * @dev: Device to handle. + * @enable: enable/disable flag */ int device_set_wakeup_enable(struct device *dev, bool enable) { @@ -581,7 +582,7 @@ static bool wakeup_source_not_registered(struct wakeup_source *ws) */ /** - * wakup_source_activate - Mark given wakeup source as active. + * wakeup_source_activate - Mark given wakeup source as active. * @ws: Wakeup source to handle. * * Update the @ws' statistics and, if @ws has just been activated, notify the PM @@ -686,7 +687,7 @@ static inline void update_prevent_sleep_time(struct wakeup_source *ws, #endif /** - * wakup_source_deactivate - Mark given wakeup source as inactive. + * wakeup_source_deactivate - Mark given wakeup source as inactive. * @ws: Wakeup source to handle. * * Update the @ws' statistics and notify the PM core that the wakeup source has @@ -785,7 +786,7 @@ EXPORT_SYMBOL_GPL(pm_relax); /** * pm_wakeup_timer_fn - Delayed finalization of a wakeup event. - * @data: Address of the wakeup source object associated with the event source. + * @t: timer list * * Call wakeup_source_deactivate() for the wakeup source whose address is stored * in @data if it is currently active and its timer has not been canceled and @@ -1021,7 +1022,7 @@ bool pm_save_wakeup_count(unsigned int count) #ifdef CONFIG_PM_AUTOSLEEP /** * pm_wakep_autosleep_enabled - Modify autosleep_enabled for all wakeup sources. - * @enabled: Whether to set or to clear the autosleep_enabled flags. + * @set: Whether to set or to clear the autosleep_enabled flags. */ void pm_wakep_autosleep_enabled(bool set) { diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c index d638259b829a..924fac493c4f 100644 --- a/drivers/base/power/wakeup_stats.c +++ b/drivers/base/power/wakeup_stats.c @@ -137,7 +137,7 @@ static struct device *wakeup_source_device_create(struct device *parent, struct wakeup_source *ws) { struct device *dev = NULL; - int retval = -ENODEV; + int retval; dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { @@ -154,7 +154,7 @@ static struct device *wakeup_source_device_create(struct device *parent, dev_set_drvdata(dev, ws); device_set_pm_not_required(dev); - retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id); + retval = dev_set_name(dev, "wakeup%d", ws->id); if (retval) goto error; diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index ff2ee87987c7..211a335a608d 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -660,6 +660,7 @@ void regmap_debugfs_exit(struct regmap *map) regmap_debugfs_free_dump_cache(map); mutex_unlock(&map->cache_lock); kfree(map->debugfs_name); + map->debugfs_name = NULL; } else { struct regmap_debugfs_node *node, *tmp; diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 19db764ffa4a..760296a4b606 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -38,6 +38,7 @@ struct regmap_irq_chip_data { unsigned int *wake_buf; unsigned int *type_buf; unsigned int *type_buf_def; + unsigned int **virt_buf; unsigned int irq_reg_stride; unsigned int type_reg_stride; @@ -45,6 +46,27 @@ struct regmap_irq_chip_data { bool clear_status:1; }; +static int sub_irq_reg(struct regmap_irq_chip_data *data, + unsigned int base_reg, int i) +{ + const struct regmap_irq_chip *chip = data->chip; + struct regmap *map = data->map; + struct regmap_irq_sub_irq_map *subreg; + unsigned int offset; + int reg = 0; + + if (!chip->sub_reg_offsets || !chip->not_fixed_stride) { + /* Assume linear mapping */ + reg = base_reg + (i * map->reg_stride * data->irq_reg_stride); + } else { + subreg = &chip->sub_reg_offsets[i]; + offset = subreg->offset[0]; + reg = base_reg + offset; + } + + return reg; +} + static inline const struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, int irq) @@ -73,7 +95,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; - int i, ret; + int i, j, ret; u32 reg; u32 unmask_offset; u32 val; @@ -87,8 +109,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (d->clear_status) { for (i = 0; i < d->chip->num_regs; i++) { - reg = d->chip->status_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->status_base, i); ret = regmap_read(map, reg, &val); if (ret) @@ -108,8 +129,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (!d->chip->mask_base) continue; - reg = d->chip->mask_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->mask_base, i); if (d->chip->mask_invert) { ret = regmap_irq_update_bits(d, reg, d->mask_buf_def[i], ~d->mask_buf[i]); @@ -136,8 +156,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) dev_err(d->map->dev, "Failed to sync masks in %x\n", reg); - reg = d->chip->wake_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->wake_base, i); if (d->wake_buf) { if (d->chip->wake_invert) ret = regmap_irq_update_bits(d, reg, @@ -161,8 +180,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data) * it'll be ignored in irq handler, then may introduce irq storm */ if (d->mask_buf[i] && (d->chip->ack_base || d->chip->use_ack)) { - reg = d->chip->ack_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->ack_base, i); + /* some chips ack by write 0 */ if (d->chip->ack_invert) ret = regmap_write(map, reg, ~d->mask_buf[i]); @@ -187,8 +206,7 @@ static void regmap_irq_sync_unlock(struct irq_data *data) for (i = 0; i < d->chip->num_type_reg; i++) { if (!d->type_buf_def[i]) continue; - reg = d->chip->type_base + - (i * map->reg_stride * d->type_reg_stride); + reg = sub_irq_reg(d, d->chip->type_base, i); if (d->chip->type_invert) ret = regmap_irq_update_bits(d, reg, d->type_buf_def[i], ~d->type_buf[i]); @@ -201,6 +219,20 @@ static void regmap_irq_sync_unlock(struct irq_data *data) } } + if (d->chip->num_virt_regs) { + for (i = 0; i < d->chip->num_virt_regs; i++) { + for (j = 0; j < d->chip->num_regs; j++) { + reg = sub_irq_reg(d, d->chip->virt_reg_base[i], + j); + ret = regmap_write(map, reg, d->virt_buf[i][j]); + if (ret != 0) + dev_err(d->map->dev, + "Failed to write virt 0x%x: %d\n", + reg, ret); + } + } + } + if (d->chip->runtime_pm) pm_runtime_put(map->dev); @@ -301,6 +333,11 @@ static int regmap_irq_set_type(struct irq_data *data, unsigned int type) default: return -EINVAL; } + + if (d->chip->set_type_virt) + return d->chip->set_type_virt(d->virt_buf, type, data->hwirq, + reg); + return 0; } @@ -352,8 +389,15 @@ static inline int read_sub_irq_data(struct regmap_irq_chip_data *data, for (i = 0; i < subreg->num_regs; i++) { unsigned int offset = subreg->offset[i]; - ret = regmap_read(map, chip->status_base + offset, - &data->status_buf[offset]); + if (chip->not_fixed_stride) + ret = regmap_read(map, + chip->status_base + offset, + &data->status_buf[b]); + else + ret = regmap_read(map, + chip->status_base + offset, + &data->status_buf[offset]); + if (ret) break; } @@ -474,10 +518,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } else { for (i = 0; i < data->chip->num_regs; i++) { - ret = regmap_read(map, chip->status_base + - (i * map->reg_stride - * data->irq_reg_stride), - &data->status_buf[i]); + unsigned int reg = sub_irq_reg(data, + data->chip->status_base, i); + ret = regmap_read(map, reg, &data->status_buf[i]); if (ret != 0) { dev_err(map->dev, @@ -499,8 +542,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && (chip->ack_base || chip->use_ack)) { - reg = chip->ack_base + - (i * map->reg_stride * data->irq_reg_stride); + reg = sub_irq_reg(data, data->chip->ack_base, i); + if (chip->ack_invert) ret = regmap_write(map, reg, ~data->status_buf[i]); @@ -605,6 +648,12 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, return -EINVAL; } + if (chip->not_fixed_stride) { + for (i = 0; i < chip->num_regs; i++) + if (chip->sub_reg_offsets[i].num_regs != 1) + return -EINVAL; + } + if (irq_base) { irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); if (irq_base < 0) { @@ -662,6 +711,24 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, goto err_alloc; } + if (chip->num_virt_regs) { + /* + * Create virt_buf[chip->num_extra_config_regs][chip->num_regs] + */ + d->virt_buf = kcalloc(chip->num_virt_regs, sizeof(*d->virt_buf), + GFP_KERNEL); + if (!d->virt_buf) + goto err_alloc; + + for (i = 0; i < chip->num_virt_regs; i++) { + d->virt_buf[i] = kcalloc(chip->num_regs, + sizeof(unsigned int), + GFP_KERNEL); + if (!d->virt_buf[i]) + goto err_alloc; + } + } + d->irq_chip = regmap_irq_chip; d->irq_chip.name = chip->name; d->irq = irq; @@ -700,8 +767,8 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (!chip->mask_base) continue; - reg = chip->mask_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->mask_base, i); + if (chip->mask_invert) ret = regmap_irq_update_bits(d, reg, d->mask_buf[i], ~d->mask_buf[i]); @@ -725,8 +792,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, continue; /* Ack masked but set interrupts */ - reg = chip->status_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->status_base, i); ret = regmap_read(map, reg, &d->status_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to read IRQ status: %d\n", @@ -735,8 +801,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, } if (d->status_buf[i] && (chip->ack_base || chip->use_ack)) { - reg = chip->ack_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->ack_base, i); if (chip->ack_invert) ret = regmap_write(map, reg, ~(d->status_buf[i] & d->mask_buf[i])); @@ -765,8 +830,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (d->wake_buf) { for (i = 0; i < chip->num_regs; i++) { d->wake_buf[i] = d->mask_buf_def[i]; - reg = chip->wake_base + - (i * map->reg_stride * d->irq_reg_stride); + reg = sub_irq_reg(d, d->chip->wake_base, i); if (chip->wake_invert) ret = regmap_irq_update_bits(d, reg, @@ -786,8 +850,7 @@ int regmap_add_irq_chip_fwnode(struct fwnode_handle *fwnode, if (chip->num_type_reg && !chip->type_in_mask) { for (i = 0; i < chip->num_type_reg; ++i) { - reg = chip->type_base + - (i * map->reg_stride * d->type_reg_stride); + reg = sub_irq_reg(d, d->chip->type_base, i); ret = regmap_read(map, reg, &d->type_buf_def[i]); @@ -838,6 +901,11 @@ err_alloc: kfree(d->mask_buf); kfree(d->status_buf); kfree(d->status_reg_buf); + if (d->virt_buf) { + for (i = 0; i < chip->num_virt_regs; i++) + kfree(d->virt_buf[i]); + kfree(d->virt_buf); + } kfree(d); return ret; } diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index fa3719ef80e4..3cc11b813f28 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -12,10 +12,10 @@ #include <linux/slab.h> struct swnode { - int id; struct kobject kobj; struct fwnode_handle fwnode; const struct software_node *node; + int id; /* hierarchy */ struct ida child_ids; @@ -720,19 +720,30 @@ software_node_find_by_name(const struct software_node *parent, const char *name) } EXPORT_SYMBOL_GPL(software_node_find_by_name); -static int -software_node_register_properties(struct software_node *node, - const struct property_entry *properties) +static struct software_node *software_node_alloc(const struct property_entry *properties) { struct property_entry *props; + struct software_node *node; props = property_entries_dup(properties); if (IS_ERR(props)) - return PTR_ERR(props); + return ERR_CAST(props); + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + property_entries_free(props); + return ERR_PTR(-ENOMEM); + } node->properties = props; - return 0; + return node; +} + +static void software_node_free(const struct software_node *node) +{ + property_entries_free(node->properties); + kfree(node); } static void software_node_release(struct kobject *kobj) @@ -746,10 +757,9 @@ static void software_node_release(struct kobject *kobj) ida_simple_remove(&swnode_root_ids, swnode->id); } - if (swnode->allocated) { - property_entries_free(swnode->node->properties); - kfree(swnode->node); - } + if (swnode->allocated) + software_node_free(swnode->node); + ida_destroy(&swnode->child_ids); kfree(swnode); } @@ -767,22 +777,19 @@ swnode_register(const struct software_node *node, struct swnode *parent, int ret; swnode = kzalloc(sizeof(*swnode), GFP_KERNEL); - if (!swnode) { - ret = -ENOMEM; - goto out_err; - } + if (!swnode) + return ERR_PTR(-ENOMEM); ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids, 0, 0, GFP_KERNEL); if (ret < 0) { kfree(swnode); - goto out_err; + return ERR_PTR(ret); } swnode->id = ret; swnode->node = node; swnode->parent = parent; - swnode->allocated = allocated; swnode->kobj.kset = swnode_kset; fwnode_init(&swnode->fwnode, &software_node_ops); @@ -803,16 +810,17 @@ swnode_register(const struct software_node *node, struct swnode *parent, return ERR_PTR(ret); } + /* + * Assign the flag only in the successful case, so + * the above kobject_put() won't mess up with properties. + */ + swnode->allocated = allocated; + if (parent) list_add_tail(&swnode->entry, &parent->children); kobject_uevent(&swnode->kobj, KOBJ_ADD); return &swnode->fwnode; - -out_err: - if (allocated) - property_entries_free(node->properties); - return ERR_PTR(ret); } /** @@ -880,7 +888,11 @@ EXPORT_SYMBOL_GPL(software_node_unregister_nodes); * software_node_register_node_group - Register a group of software nodes * @node_group: NULL terminated array of software node pointers to be registered * - * Register multiple software nodes at once. + * Register multiple software nodes at once. If any node in the array + * has its .parent pointer set (which can only be to another software_node), + * then its parent **must** have been registered before it is; either outside + * of this function or by ordering the array such that parent comes before + * child. */ int software_node_register_node_group(const struct software_node **node_group) { @@ -906,10 +918,14 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group); * software_node_unregister_node_group - Unregister a group of software nodes * @node_group: NULL terminated array of software node pointers to be unregistered * - * Unregister multiple software nodes at once. The array will be unwound in - * reverse order (i.e. last entry first) and thus if any members of the array are - * children of another member then the children must appear later in the list such - * that they are unregistered first. + * Unregister multiple software nodes at once. If parent pointers are set up + * in any of the software nodes then the array **must** be ordered such that + * parents come before their children. + * + * NOTE: If you are uncertain whether the array is ordered such that + * parents will be unregistered before their children, it is wiser to + * remove the nodes individually, in the correct order (child before + * parent). */ void software_node_unregister_node_group( const struct software_node **node_group) @@ -963,31 +979,28 @@ struct fwnode_handle * fwnode_create_software_node(const struct property_entry *properties, const struct fwnode_handle *parent) { + struct fwnode_handle *fwnode; struct software_node *node; - struct swnode *p = NULL; - int ret; + struct swnode *p; - if (parent) { - if (IS_ERR(parent)) - return ERR_CAST(parent); - if (!is_software_node(parent)) - return ERR_PTR(-EINVAL); - p = to_swnode(parent); - } + if (IS_ERR(parent)) + return ERR_CAST(parent); - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - return ERR_PTR(-ENOMEM); + p = to_swnode(parent); + if (parent && !p) + return ERR_PTR(-EINVAL); - ret = software_node_register_properties(node, properties); - if (ret) { - kfree(node); - return ERR_PTR(ret); - } + node = software_node_alloc(properties); + if (IS_ERR(node)) + return ERR_CAST(node); node->parent = p ? p->node : NULL; - return swnode_register(node, p, 1); + fwnode = swnode_register(node, p, 1); + if (IS_ERR(fwnode)) + software_node_free(node); + + return fwnode; } EXPORT_SYMBOL_GPL(fwnode_create_software_node); @@ -1032,6 +1045,7 @@ int device_add_software_node(struct device *dev, const struct software_node *nod } set_secondary_fwnode(dev, &swnode->fwnode); + software_node_notify(dev, KOBJ_ADD); return 0; } @@ -1105,8 +1119,8 @@ int software_node_notify(struct device *dev, unsigned long action) switch (action) { case KOBJ_ADD: - ret = sysfs_create_link(&dev->kobj, &swnode->kobj, - "software_node"); + ret = sysfs_create_link_nowarn(&dev->kobj, &swnode->kobj, + "software_node"); if (ret) break; diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index ba225eb1b761..2f3fa31a948e 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -8,7 +8,7 @@ config TEST_ASYNC_DRIVER_PROBE The module name will be test_async_driver_probe.ko If unsure say N. -config KUNIT_DRIVER_PE_TEST +config DRIVER_PE_KUNIT_TEST bool "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS depends on KUNIT=y default KUNIT_ALL_TESTS diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index 2f15fae8625f..64b2f3d744d5 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o -obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o +obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o CFLAGS_REMOVE_property-entry-test.o += -fplugin-arg-structleak_plugin-byref -fplugin-arg-structleak_plugin-byref-all diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c index abe03315180f..1106fedcceed 100644 --- a/drivers/base/test/property-entry-test.c +++ b/drivers/base/test/property-entry-test.c @@ -27,6 +27,9 @@ static void pe_test_uints(struct kunit *test) node = fwnode_create_software_node(entries, NULL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); + error = fwnode_property_count_u8(node, "prop-u8"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u8(node, "prop-u8", &val_u8); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u8, 8); @@ -48,6 +51,9 @@ static void pe_test_uints(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u16, 16); + error = fwnode_property_count_u16(node, "prop-u16"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16); @@ -65,6 +71,9 @@ static void pe_test_uints(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u32, 32); + error = fwnode_property_count_u32(node, "prop-u32"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32); @@ -82,6 +91,9 @@ static void pe_test_uints(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u64, 64); + error = fwnode_property_count_u64(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64); @@ -95,15 +107,19 @@ static void pe_test_uints(struct kunit *test) error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1); KUNIT_EXPECT_NE(test, error, 0); + /* Count 64-bit values as 16-bit */ + error = fwnode_property_count_u16(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 4); + fwnode_remove_software_node(node); } static void pe_test_uint_arrays(struct kunit *test) { - static const u8 a_u8[16] = { 8, 9 }; - static const u16 a_u16[16] = { 16, 17 }; - static const u32 a_u32[16] = { 32, 33 }; - static const u64 a_u64[16] = { 64, 65 }; + static const u8 a_u8[10] = { 8, 9 }; + static const u16 a_u16[10] = { 16, 17 }; + static const u32 a_u32[10] = { 32, 33 }; + static const u64 a_u64[10] = { 64, 65 }; static const struct property_entry entries[] = { PROPERTY_ENTRY_U8_ARRAY("prop-u8", a_u8), PROPERTY_ENTRY_U16_ARRAY("prop-u16", a_u16), @@ -126,6 +142,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u8, 8); + error = fwnode_property_count_u8(node, "prop-u8"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u8[0], 8); @@ -148,6 +167,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u16, 16); + error = fwnode_property_count_u16(node, "prop-u16"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16); @@ -170,6 +192,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u32, 32); + error = fwnode_property_count_u32(node, "prop-u32"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32); @@ -192,6 +217,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u64, 64); + error = fwnode_property_count_u64(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64); @@ -210,6 +238,14 @@ static void pe_test_uint_arrays(struct kunit *test) error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1); KUNIT_EXPECT_NE(test, error, 0); + /* Count 64-bit values as 16-bit */ + error = fwnode_property_count_u16(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 40); + + /* Other way around */ + error = fwnode_property_count_u64(node, "prop-u16"); + KUNIT_EXPECT_EQ(test, error, 2); + fwnode_remove_software_node(node); } @@ -239,6 +275,9 @@ static void pe_test_strings(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_STREQ(test, str, "single"); + error = fwnode_property_string_array_count(node, "str"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_string_array(node, "str", strs, 1); KUNIT_EXPECT_EQ(test, error, 1); KUNIT_EXPECT_STREQ(test, strs[0], "single"); @@ -258,6 +297,9 @@ static void pe_test_strings(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_STREQ(test, str, ""); + error = fwnode_property_string_array_count(node, "strs"); + KUNIT_EXPECT_EQ(test, error, 2); + error = fwnode_property_read_string_array(node, "strs", strs, 3); KUNIT_EXPECT_EQ(test, error, 2); KUNIT_EXPECT_STREQ(test, strs[0], "string-a"); @@ -370,15 +412,8 @@ static void pe_test_reference(struct kunit *test) }; static const struct software_node_ref_args refs[] = { - { - .node = &nodes[0], - .nargs = 0, - }, - { - .node = &nodes[1], - .nargs = 2, - .args = { 3, 4 }, - }, + SOFTWARE_NODE_REFERENCE(&nodes[0]), + SOFTWARE_NODE_REFERENCE(&nodes[1], 3, 4), }; const struct property_entry entries[] = { |