diff options
Diffstat (limited to 'drivers/base')
30 files changed, 632 insertions, 385 deletions
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index e1eff05bea4a..84ec92bff642 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -823,12 +823,106 @@ void remove_cpu_topology(unsigned int cpu) clear_cpu_topology(cpu); } +#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) +struct cpu_smt_info { + unsigned int thread_num; + int core_id; +}; + +static bool __init acpi_cpu_is_threaded(int cpu) +{ + int is_threaded = acpi_pptt_cpu_is_thread(cpu); + + /* + * if the PPTT doesn't have thread information, check for architecture + * specific fallback if available + */ + if (is_threaded < 0) + is_threaded = arch_cpu_is_threaded(); + + return !!is_threaded; +} + +/* + * Propagate the topology information of the processor_topology_node tree to the + * cpu_topology array. + */ __weak int __init parse_acpi_topology(void) { + unsigned int max_smt_thread_num = 1; + struct cpu_smt_info *entry; + struct xarray hetero_cpu; + unsigned long hetero_id; + int cpu, topology_id; + + if (acpi_disabled) + return 0; + + xa_init(&hetero_cpu); + + for_each_possible_cpu(cpu) { + topology_id = find_acpi_cpu_topology(cpu, 0); + if (topology_id < 0) + return topology_id; + + if (acpi_cpu_is_threaded(cpu)) { + cpu_topology[cpu].thread_id = topology_id; + topology_id = find_acpi_cpu_topology(cpu, 1); + cpu_topology[cpu].core_id = topology_id; + + /* + * In the PPTT, CPUs below a node with the 'identical + * implementation' flag have the same number of threads. + * Count the number of threads for only one CPU (i.e. + * one core_id) among those with the same hetero_id. + * See the comment of find_acpi_cpu_topology_hetero_id() + * for more details. + * + * One entry is created for each node having: + * - the 'identical implementation' flag + * - its parent not having the flag + */ + hetero_id = find_acpi_cpu_topology_hetero_id(cpu); + entry = xa_load(&hetero_cpu, hetero_id); + if (!entry) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + WARN_ON_ONCE(!entry); + + if (entry) { + entry->core_id = topology_id; + entry->thread_num = 1; + xa_store(&hetero_cpu, hetero_id, + entry, GFP_KERNEL); + } + } else if (entry->core_id == topology_id) { + entry->thread_num++; + } + } else { + cpu_topology[cpu].thread_id = -1; + cpu_topology[cpu].core_id = topology_id; + } + topology_id = find_acpi_cpu_topology_cluster(cpu); + cpu_topology[cpu].cluster_id = topology_id; + topology_id = find_acpi_cpu_topology_package(cpu); + cpu_topology[cpu].package_id = topology_id; + } + + /* + * This is a short loop since the number of XArray elements is the + * number of heterogeneous CPU clusters. On a homogeneous system + * there's only one entry in the XArray. + */ + xa_for_each(&hetero_cpu, hetero_id, entry) { + max_smt_thread_num = max(max_smt_thread_num, entry->thread_num); + xa_erase(&hetero_cpu, hetero_id); + kfree(entry); + } + + cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num); + xa_destroy(&hetero_cpu); return 0; } -#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) void __init init_cpu_topology(void) { int cpu, ret; diff --git a/drivers/base/base.h b/drivers/base/base.h index 86fa7fbb3548..430cbefbc97f 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -85,6 +85,18 @@ struct driver_private { }; #define to_driver(obj) container_of(obj, struct driver_private, kobj) +#ifdef CONFIG_RUST +/** + * struct driver_type - Representation of a Rust driver type. + */ +struct driver_type { + /** + * @id: Representation of core::any::TypeId. + */ + u8 id[16]; +} __packed; +#endif + /** * struct device_private - structure to hold the private to the driver core portions of the device structure. * @@ -100,6 +112,7 @@ struct driver_private { * @async_driver - pointer to device driver awaiting probe via async_probe * @device - pointer back to the struct device that this structure is * associated with. + * @driver_type - The type of the bound Rust driver. * @dead - This device is currently either in the process of or has been * removed from the system. Any asynchronous events scheduled for this * device should exit without taking any action. @@ -116,6 +129,9 @@ struct device_private { const struct device_driver *async_driver; char *deferred_probe_reason; struct device *device; +#ifdef CONFIG_RUST + struct driver_type driver_type; +#endif u8 dead:1; }; #define to_device_private_parent(obj) \ diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 5e75e1bce551..320e155c6be7 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -533,8 +533,7 @@ void bus_probe_device(struct device *dev) if (!sp) return; - if (sp->drivers_autoprobe) - device_initial_probe(dev); + device_initial_probe(dev); mutex_lock(&sp->mutex); list_for_each_entry(sif, &sp->interfaces, node) diff --git a/drivers/base/core.c b/drivers/base/core.c index f69dc9c85954..40de2f51a1b1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4138,7 +4138,7 @@ int __init devices_init(void) sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); if (!sysfs_dev_char_kobj) goto char_kobj_err; - device_link_wq = alloc_workqueue("device_link_wq", 0, 0); + device_link_wq = alloc_workqueue("device_link_wq", WQ_PERCPU, 0); if (!device_link_wq) goto wq_err; diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index fa0a2eef93ac..c6c57b6f61c6 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -300,13 +300,30 @@ static ssize_t print_cpus_isolated(struct device *dev, } static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL); +static ssize_t housekeeping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct cpumask *hk_mask; + + hk_mask = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE); + + if (housekeeping_enabled(HK_TYPE_KERNEL_NOISE)) + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(hk_mask)); + return sysfs_emit(buf, "\n"); +} +static DEVICE_ATTR_RO(housekeeping); + #ifdef CONFIG_NO_HZ_FULL -static ssize_t print_cpus_nohz_full(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t nohz_full_show(struct device *dev, + struct device_attribute *attr, + char *buf) { - return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask)); + if (cpumask_available(tick_nohz_full_mask)) + return sysfs_emit(buf, "%*pbl\n", + cpumask_pr_args(tick_nohz_full_mask)); + return sysfs_emit(buf, "\n"); } -static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL); +static DEVICE_ATTR_RO(nohz_full); #endif #ifdef CONFIG_CRASH_HOTPLUG @@ -505,6 +522,7 @@ static struct attribute *cpu_root_attrs[] = { &dev_attr_offline.attr, &dev_attr_enabled.attr, &dev_attr_isolated.attr, + &dev_attr_housekeeping.attr, #ifdef CONFIG_NO_HZ_FULL &dev_attr_nohz_full.attr, #endif diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 13ab98e033ea..349f31bedfa1 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -193,7 +193,7 @@ void driver_deferred_probe_trigger(void) * Kick the re-probe thread. It may already be scheduled, but it is * safe to kick it again. */ - queue_work(system_unbound_wq, &deferred_probe_work); + queue_work(system_dfl_wq, &deferred_probe_work); } /** @@ -1077,7 +1077,15 @@ EXPORT_SYMBOL_GPL(device_attach); void device_initial_probe(struct device *dev) { - __device_attach(dev, true); + struct subsys_private *sp = bus_to_subsys(dev->bus); + + if (!sp) + return; + + if (sp->drivers_autoprobe) + __device_attach(dev, true); + + subsys_put(sp); } /* diff --git a/drivers/base/devres.c b/drivers/base/devres.c index c948c88d3956..f54db6d138ab 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -1222,13 +1222,6 @@ static void devm_percpu_release(struct device *dev, void *pdata) free_percpu(p); } -static int devm_percpu_match(struct device *dev, void *data, void *p) -{ - struct devres *devr = container_of(data, struct devres, data); - - return *(void **)devr->data == p; -} - /** * __devm_alloc_percpu - Resource-managed alloc_percpu * @dev: Device to allocate per-cpu memory for @@ -1264,21 +1257,3 @@ void __percpu *__devm_alloc_percpu(struct device *dev, size_t size, return pcpu; } EXPORT_SYMBOL_GPL(__devm_alloc_percpu); - -/** - * devm_free_percpu - Resource-managed free_percpu - * @dev: Device this memory belongs to - * @pdata: Per-cpu memory to free - * - * Free memory allocated with devm_alloc_percpu(). - */ -void devm_free_percpu(struct device *dev, void __percpu *pdata) -{ - /* - * Use devres_release() to prevent memory leakage as - * devm_free_pages() does. - */ - WARN_ON(devres_release(dev, devm_percpu_release, devm_percpu_match, - (void *)(__force unsigned long)pdata)); -} -EXPORT_SYMBOL_GPL(devm_free_percpu); diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 9d4e46ad8352..194b44075ac7 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -70,7 +70,7 @@ static struct file_system_type internal_fs_type = { #else .init_fs_context = ramfs_init_fs_context, #endif - .kill_sb = kill_litter_super, + .kill_sb = kill_anon_super, }; /* Simply take a ref on the existing mount */ @@ -180,7 +180,7 @@ static int dev_mkdir(const char *name, umode_t mode) if (IS_ERR(dentry)) return PTR_ERR(dentry); - dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode); + dentry = vfs_mkdir(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, NULL); if (!IS_ERR(dentry)) /* mark as kernel-created inode */ d_inode(dentry)->i_private = &thread; @@ -231,7 +231,7 @@ static int handle_create(const char *nodename, umode_t mode, kuid_t uid, return PTR_ERR(dentry); err = vfs_mknod(&nop_mnt_idmap, d_inode(path.dentry), dentry, mode, - dev->devt); + dev->devt, NULL); if (!err) { struct iattr newattrs; @@ -261,7 +261,7 @@ static int dev_rmdir(const char *name) return PTR_ERR(dentry); if (d_inode(dentry)->i_private == &thread) err = vfs_rmdir(&nop_mnt_idmap, d_inode(parent.dentry), - dentry); + dentry, NULL); else err = -EPERM; diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index 6942c62fa59d..4ebdca9e4da4 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -829,8 +829,6 @@ _request_firmware(const struct firmware **firmware_p, const char *name, size_t offset, u32 opt_flags) { struct firmware *fw = NULL; - struct cred *kern_cred = NULL; - const struct cred *old_cred; bool nondirect = false; int ret; @@ -871,45 +869,38 @@ _request_firmware(const struct firmware **firmware_p, const char *name, * called by a driver when serving an unrelated request from userland, we use * the kernel credentials to read the file. */ - kern_cred = prepare_kernel_cred(&init_task); - if (!kern_cred) { - ret = -ENOMEM; - goto out; - } - old_cred = override_creds(kern_cred); - - ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL); + scoped_with_kernel_creds() { + ret = fw_get_filesystem_firmware(device, fw->priv, "", NULL); - /* Only full reads can support decompression, platform, and sysfs. */ - if (!(opt_flags & FW_OPT_PARTIAL)) - nondirect = true; + /* Only full reads can support decompression, platform, and sysfs. */ + if (!(opt_flags & FW_OPT_PARTIAL)) + nondirect = true; #ifdef CONFIG_FW_LOADER_COMPRESS_ZSTD - if (ret == -ENOENT && nondirect) - ret = fw_get_filesystem_firmware(device, fw->priv, ".zst", - fw_decompress_zstd); + if (ret == -ENOENT && nondirect) + ret = fw_get_filesystem_firmware(device, fw->priv, ".zst", + fw_decompress_zstd); #endif #ifdef CONFIG_FW_LOADER_COMPRESS_XZ - if (ret == -ENOENT && nondirect) - ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", - fw_decompress_xz); + if (ret == -ENOENT && nondirect) + ret = fw_get_filesystem_firmware(device, fw->priv, ".xz", + fw_decompress_xz); #endif - if (ret == -ENOENT && nondirect) - ret = firmware_fallback_platform(fw->priv); + if (ret == -ENOENT && nondirect) + ret = firmware_fallback_platform(fw->priv); - if (ret) { - if (!(opt_flags & FW_OPT_NO_WARN)) - dev_warn(device, - "Direct firmware load for %s failed with error %d\n", - name, ret); - if (nondirect) - ret = firmware_fallback_sysfs(fw, name, device, - opt_flags, ret); - } else - ret = assign_fw(fw, device); - - revert_creds(old_cred); - put_cred(kern_cred); + if (ret) { + if (!(opt_flags & FW_OPT_NO_WARN)) + dev_warn(device, + "Direct firmware load for %s failed with error %d\n", + name, ret); + if (nondirect) + ret = firmware_fallback_sysfs(fw, name, device, + opt_flags, ret); + } else { + ret = assign_fw(fw, device); + } + } out: if (ret < 0) { @@ -1585,16 +1576,20 @@ static int fw_pm_notify(struct notifier_block *notify_block, } /* stop caching firmware once syscore_suspend is reached */ -static int fw_suspend(void) +static int fw_suspend(void *data) { fw_cache.state = FW_LOADER_NO_CACHE; return 0; } -static struct syscore_ops fw_syscore_ops = { +static const struct syscore_ops fw_syscore_ops = { .suspend = fw_suspend, }; +static struct syscore fw_syscore = { + .ops = &fw_syscore_ops, +}; + static int __init register_fw_pm_ops(void) { int ret; @@ -1610,14 +1605,14 @@ static int __init register_fw_pm_ops(void) if (ret) return ret; - register_syscore_ops(&fw_syscore_ops); + register_syscore(&fw_syscore); return ret; } static inline void unregister_fw_pm_ops(void) { - unregister_syscore_ops(&fw_syscore_ops); + unregister_syscore(&fw_syscore); unregister_pm_notifier(&fw_cache.pm_notify); } #else diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c index add0b9b75edd..92e91050f96a 100644 --- a/drivers/base/firmware_loader/sysfs.c +++ b/drivers/base/firmware_loader/sysfs.c @@ -47,7 +47,10 @@ static ssize_t timeout_show(const struct class *class, const struct class_attrib static ssize_t timeout_store(const struct class *class, const struct class_attribute *attr, const char *buf, size_t count) { - int tmp_loading_timeout = simple_strtol(buf, NULL, 10); + int tmp_loading_timeout; + + if (kstrtoint(buf, 10, &tmp_loading_timeout)) + return -EINVAL; if (tmp_loading_timeout < 0) tmp_loading_timeout = 0; @@ -157,7 +160,10 @@ static ssize_t firmware_loading_store(struct device *dev, struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); struct fw_priv *fw_priv; ssize_t written = count; - int loading = simple_strtol(buf, NULL, 10); + int loading; + + if (kstrtoint(buf, 10, &loading)) + return -EINVAL; mutex_lock(&fw_lock); fw_priv = fw_sysfs->fw_priv; diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c index 829270067d16..c3797b93c5f5 100644 --- a/drivers/base/firmware_loader/sysfs_upload.c +++ b/drivers/base/firmware_loader/sysfs_upload.c @@ -100,8 +100,10 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&fwlp->lock); - if (fwlp->progress == FW_UPLOAD_PROG_IDLE) - ret = -ENODEV; + if (fwlp->progress == FW_UPLOAD_PROG_IDLE) { + mutex_unlock(&fwlp->lock); + return -ENODEV; + } fwlp->ops->cancel(fwlp->fw_upload); mutex_unlock(&fwlp->lock); diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 6d84a02cfa5d..751f248ca4a8 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -198,15 +198,15 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, break; default: WARN_ON(1); - return sysfs_emit(buf, "ERROR-UNKNOWN-%ld\n", mem->state); + return sysfs_emit(buf, "ERROR-UNKNOWN-%d\n", mem->state); } return sysfs_emit(buf, "%s\n", output); } -int memory_notify(unsigned long val, void *v) +int memory_notify(enum memory_block_state state, void *v) { - return blocking_notifier_call_chain(&memory_chain, val, v); + return blocking_notifier_call_chain(&memory_chain, state, v); } #if defined(CONFIG_MEMORY_FAILURE) && defined(CONFIG_MEMORY_HOTPLUG) @@ -226,7 +226,6 @@ 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 = 0; - struct memory_notify arg; struct zone *zone; int ret; @@ -246,19 +245,9 @@ static int memory_block_online(struct memory_block *mem) if (mem->altmap) nr_vmemmap_pages = mem->altmap->free; - arg.altmap_start_pfn = start_pfn; - arg.altmap_nr_pages = nr_vmemmap_pages; - arg.start_pfn = start_pfn + nr_vmemmap_pages; - arg.nr_pages = nr_pages - nr_vmemmap_pages; mem_hotplug_begin(); - ret = memory_notify(MEM_PREPARE_ONLINE, &arg); - ret = notifier_to_errno(ret); - if (ret) - goto out_notifier; - if (nr_vmemmap_pages) { - ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, - zone, mem->altmap->inaccessible); + ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone); if (ret) goto out; } @@ -280,11 +269,7 @@ static int memory_block_online(struct memory_block *mem) nr_vmemmap_pages); mem->zone = zone; - mem_hotplug_done(); - return ret; out: - memory_notify(MEM_FINISH_OFFLINE, &arg); -out_notifier: mem_hotplug_done(); return ret; } @@ -297,7 +282,6 @@ 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 = 0; - struct memory_notify arg; int ret; if (!mem->zone) @@ -329,11 +313,6 @@ static int memory_block_offline(struct memory_block *mem) mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages); mem->zone = NULL; - arg.altmap_start_pfn = start_pfn; - arg.altmap_nr_pages = nr_vmemmap_pages; - arg.start_pfn = start_pfn + nr_vmemmap_pages; - arg.nr_pages = nr_pages - nr_vmemmap_pages; - memory_notify(MEM_FINISH_OFFLINE, &arg); out: mem_hotplug_done(); return ret; diff --git a/drivers/base/node.c b/drivers/base/node.c index 83aeb0518e1d..00cf4532f121 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -676,50 +676,6 @@ static void node_device_release(struct device *dev) kfree(to_node(dev)); } -/* - * register_node - Setup a sysfs device for a node. - * @num - Node number to use when creating the device. - * - * Initialize and register the node device. - */ -static int register_node(struct node *node, int num) -{ - int error; - - node->dev.id = num; - node->dev.bus = &node_subsys; - node->dev.release = node_device_release; - node->dev.groups = node_dev_groups; - error = device_register(&node->dev); - - if (error) { - put_device(&node->dev); - } else { - hugetlb_register_node(node); - compaction_register_node(node); - reclaim_register_node(node); - } - - return error; -} - -/** - * unregister_node - unregister a node device - * @node: node going away - * - * Unregisters a node device @node. All the devices on the node must be - * unregistered before calling this function. - */ -void unregister_node(struct node *node) -{ - hugetlb_unregister_node(node); - compaction_unregister_node(node); - reclaim_unregister_node(node); - node_remove_accesses(node); - node_remove_caches(node); - device_unregister(&node->dev); -} - struct node *node_devices[MAX_NUMNODES]; /* @@ -907,7 +863,13 @@ void register_memory_blocks_under_node_hotplug(int nid, unsigned long start_pfn, } #endif /* CONFIG_MEMORY_HOTPLUG */ -int register_one_node(int nid) +/** + * register_node - Initialize and register the node device. + * @nid: Node number to use when creating the device. + * + * Return: 0 on success, -errno otherwise + */ +int register_node(int nid) { int error; int cpu; @@ -918,14 +880,23 @@ int register_one_node(int nid) return -ENOMEM; INIT_LIST_HEAD(&node->access_list); - node_devices[nid] = node; - error = register_node(node_devices[nid], nid); + node->dev.id = nid; + node->dev.bus = &node_subsys; + node->dev.release = node_device_release; + node->dev.groups = node_dev_groups; + + error = device_register(&node->dev); if (error) { - node_devices[nid] = NULL; + put_device(&node->dev); return error; } + node_devices[nid] = node; + hugetlb_register_node(node); + compaction_register_node(node); + reclaim_register_node(node); + /* link cpu under this node */ for_each_present_cpu(cpu) { if (cpu_to_node(cpu) == nid) @@ -936,13 +907,26 @@ int register_one_node(int nid) return error; } - -void unregister_one_node(int nid) +/** + * unregister_node - unregister a node device + * @nid: nid of the node going away + * + * Unregisters the node device at node id @nid. All the devices on the + * node must be unregistered before calling this function. + */ +void unregister_node(int nid) { - if (!node_devices[nid]) + struct node *node = node_devices[nid]; + + if (!node) return; - unregister_node(node_devices[nid]); + hugetlb_unregister_node(node); + compaction_unregister_node(node); + reclaim_unregister_node(node); + node_remove_accesses(node); + node_remove_caches(node); + device_unregister(&node->dev); node_devices[nid] = NULL; } @@ -1018,7 +1002,7 @@ void __init node_dev_init(void) * to already created cpu devices. */ for_each_online_node(i) { - ret = register_one_node(i); + ret = register_node(i); if (ret) panic("%s() failed to add node: %d\n", __func__, ret); } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 09450349cf32..b45d41b018ca 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -150,25 +150,37 @@ devm_platform_ioremap_resource_byname(struct platform_device *pdev, EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname); #endif /* CONFIG_HAS_IOMEM */ +static const struct cpumask *get_irq_affinity(struct platform_device *dev, + unsigned int num) +{ + const struct cpumask *mask = NULL; +#ifndef CONFIG_SPARC + struct fwnode_handle *fwnode = dev_fwnode(&dev->dev); + + if (is_of_node(fwnode)) + mask = of_irq_get_affinity(to_of_node(fwnode), num); + else if (is_acpi_device_node(fwnode)) + mask = acpi_irq_get_affinity(ACPI_HANDLE_FWNODE(fwnode), num); +#endif + + return mask ?: cpu_possible_mask; +} + /** - * platform_get_irq_optional - get an optional IRQ for a device - * @dev: platform device - * @num: IRQ number index + * platform_get_irq_affinity - get an optional IRQ and its affinity for a device + * @dev: platform device + * @num: interrupt number index + * @affinity: optional cpumask pointer to get the affinity of a per-cpu interrupt * - * 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. - * - * For example:: - * - * int irq = platform_get_irq_optional(pdev, 0); - * if (irq < 0) - * return irq; + * Gets an interupt 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. Optional affinity information is provided in the + * affinity pointer if available, and NULL otherwise. * - * Return: non-zero IRQ number on success, negative error number on failure. + * Return: non-zero interrupt number on success, negative error number on failure. */ -int platform_get_irq_optional(struct platform_device *dev, unsigned int num) +int platform_get_irq_affinity(struct platform_device *dev, unsigned int num, + const struct cpumask **affinity) { int ret; #ifdef CONFIG_SPARC @@ -236,8 +248,37 @@ out_not_found: out: if (WARN(!ret, "0 is an invalid IRQ number\n")) return -EINVAL; + + if (ret > 0 && affinity) + *affinity = get_irq_affinity(dev, num); + return ret; } +EXPORT_SYMBOL_GPL(platform_get_irq_affinity); + +/** + * platform_get_irq_optional - get an optional interrupt for a device + * @dev: platform device + * @num: interrupt number index + * + * Gets an interrupt 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 interrupt can not be + * obtained. + * + * For example:: + * + * int irq = platform_get_irq_optional(pdev, 0); + * if (irq < 0) + * return irq; + * + * Return: non-zero interrupt number on success, negative error number on failure. + */ +int platform_get_irq_optional(struct platform_device *dev, unsigned int num) +{ + return platform_get_irq_affinity(dev, num, NULL); +} EXPORT_SYMBOL_GPL(platform_get_irq_optional); /** diff --git a/drivers/base/power/generic_ops.c b/drivers/base/power/generic_ops.c index 6502720bb564..af99bbcf281c 100644 --- a/drivers/base/power/generic_ops.c +++ b/drivers/base/power/generic_ops.c @@ -8,6 +8,13 @@ #include <linux/pm_runtime.h> #include <linux/export.h> +#define CALL_PM_OP(dev, op) \ +({ \ + struct device *_dev = (dev); \ + const struct dev_pm_ops *pm = _dev->driver ? _dev->driver->pm : NULL; \ + pm && pm->op ? pm->op(_dev) : 0; \ +}) + #ifdef CONFIG_PM /** * pm_generic_runtime_suspend - Generic runtime suspend callback for subsystems. @@ -19,12 +26,7 @@ */ int pm_generic_runtime_suspend(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - int ret; - - ret = pm && pm->runtime_suspend ? pm->runtime_suspend(dev) : 0; - - return ret; + return CALL_PM_OP(dev, runtime_suspend); } EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); @@ -38,12 +40,7 @@ EXPORT_SYMBOL_GPL(pm_generic_runtime_suspend); */ int pm_generic_runtime_resume(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - int ret; - - ret = pm && pm->runtime_resume ? pm->runtime_resume(dev) : 0; - - return ret; + return CALL_PM_OP(dev, runtime_resume); } EXPORT_SYMBOL_GPL(pm_generic_runtime_resume); #endif /* CONFIG_PM */ @@ -72,9 +69,7 @@ int pm_generic_prepare(struct device *dev) */ int pm_generic_suspend_noirq(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->suspend_noirq ? pm->suspend_noirq(dev) : 0; + return CALL_PM_OP(dev, suspend_noirq); } EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq); @@ -84,9 +79,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq); */ int pm_generic_suspend_late(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->suspend_late ? pm->suspend_late(dev) : 0; + return CALL_PM_OP(dev, suspend_late); } EXPORT_SYMBOL_GPL(pm_generic_suspend_late); @@ -96,9 +89,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend_late); */ int pm_generic_suspend(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->suspend ? pm->suspend(dev) : 0; + return CALL_PM_OP(dev, suspend); } EXPORT_SYMBOL_GPL(pm_generic_suspend); @@ -108,9 +99,7 @@ EXPORT_SYMBOL_GPL(pm_generic_suspend); */ int pm_generic_freeze_noirq(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->freeze_noirq ? pm->freeze_noirq(dev) : 0; + return CALL_PM_OP(dev, freeze_noirq); } EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq); @@ -120,9 +109,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq); */ int pm_generic_freeze(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->freeze ? pm->freeze(dev) : 0; + return CALL_PM_OP(dev, freeze); } EXPORT_SYMBOL_GPL(pm_generic_freeze); @@ -132,9 +119,7 @@ EXPORT_SYMBOL_GPL(pm_generic_freeze); */ int pm_generic_poweroff_noirq(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->poweroff_noirq ? pm->poweroff_noirq(dev) : 0; + return CALL_PM_OP(dev, poweroff_noirq); } EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq); @@ -144,9 +129,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq); */ int pm_generic_poweroff_late(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->poweroff_late ? pm->poweroff_late(dev) : 0; + return CALL_PM_OP(dev, poweroff_late); } EXPORT_SYMBOL_GPL(pm_generic_poweroff_late); @@ -156,9 +139,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff_late); */ int pm_generic_poweroff(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->poweroff ? pm->poweroff(dev) : 0; + return CALL_PM_OP(dev, poweroff); } EXPORT_SYMBOL_GPL(pm_generic_poweroff); @@ -168,9 +149,7 @@ EXPORT_SYMBOL_GPL(pm_generic_poweroff); */ int pm_generic_thaw_noirq(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->thaw_noirq ? pm->thaw_noirq(dev) : 0; + return CALL_PM_OP(dev, thaw_noirq); } EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq); @@ -180,9 +159,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq); */ int pm_generic_thaw(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->thaw ? pm->thaw(dev) : 0; + return CALL_PM_OP(dev, thaw); } EXPORT_SYMBOL_GPL(pm_generic_thaw); @@ -192,9 +169,7 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw); */ int pm_generic_resume_noirq(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->resume_noirq ? pm->resume_noirq(dev) : 0; + return CALL_PM_OP(dev, resume_noirq); } EXPORT_SYMBOL_GPL(pm_generic_resume_noirq); @@ -204,9 +179,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_noirq); */ int pm_generic_resume_early(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->resume_early ? pm->resume_early(dev) : 0; + return CALL_PM_OP(dev, resume_early); } EXPORT_SYMBOL_GPL(pm_generic_resume_early); @@ -216,9 +189,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume_early); */ int pm_generic_resume(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->resume ? pm->resume(dev) : 0; + return CALL_PM_OP(dev, resume); } EXPORT_SYMBOL_GPL(pm_generic_resume); @@ -228,9 +199,7 @@ EXPORT_SYMBOL_GPL(pm_generic_resume); */ int pm_generic_restore_noirq(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->restore_noirq ? pm->restore_noirq(dev) : 0; + return CALL_PM_OP(dev, restore_noirq); } EXPORT_SYMBOL_GPL(pm_generic_restore_noirq); @@ -240,9 +209,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_noirq); */ int pm_generic_restore_early(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->restore_early ? pm->restore_early(dev) : 0; + return CALL_PM_OP(dev, restore_early); } EXPORT_SYMBOL_GPL(pm_generic_restore_early); @@ -252,9 +219,7 @@ EXPORT_SYMBOL_GPL(pm_generic_restore_early); */ int pm_generic_restore(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - - return pm && pm->restore ? pm->restore(dev) : 0; + return CALL_PM_OP(dev, restore); } EXPORT_SYMBOL_GPL(pm_generic_restore); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e83503bdc1fd..97a8b4fcf471 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -34,6 +34,7 @@ #include <linux/cpufreq.h> #include <linux/devfreq.h> #include <linux/timer.h> +#include <linux/nmi.h> #include "../base.h" #include "power.h" @@ -95,6 +96,8 @@ static const char *pm_verb(int event) return "restore"; case PM_EVENT_RECOVER: return "recover"; + case PM_EVENT_POWEROFF: + return "poweroff"; default: return "(unknown PM event)"; } @@ -367,6 +370,7 @@ static pm_callback_t pm_op(const struct dev_pm_ops *ops, pm_message_t state) case PM_EVENT_FREEZE: case PM_EVENT_QUIESCE: return ops->freeze; + case PM_EVENT_POWEROFF: case PM_EVENT_HIBERNATE: return ops->poweroff; case PM_EVENT_THAW: @@ -401,6 +405,7 @@ static pm_callback_t pm_late_early_op(const struct dev_pm_ops *ops, case PM_EVENT_FREEZE: case PM_EVENT_QUIESCE: return ops->freeze_late; + case PM_EVENT_POWEROFF: case PM_EVENT_HIBERNATE: return ops->poweroff_late; case PM_EVENT_THAW: @@ -435,6 +440,7 @@ static pm_callback_t pm_noirq_op(const struct dev_pm_ops *ops, pm_message_t stat case PM_EVENT_FREEZE: case PM_EVENT_QUIESCE: return ops->freeze_noirq; + case PM_EVENT_POWEROFF: case PM_EVENT_HIBERNATE: return ops->poweroff_noirq; case PM_EVENT_THAW: @@ -515,6 +521,11 @@ struct dpm_watchdog { #define DECLARE_DPM_WATCHDOG_ON_STACK(wd) \ struct dpm_watchdog wd +static bool __read_mostly dpm_watchdog_all_cpu_backtrace; +module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644); +MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace, + "Backtrace all CPUs on DPM watchdog timeout"); + /** * dpm_watchdog_handler - Driver suspend / resume watchdog handler. * @t: The timer that PM watchdog depends on. @@ -530,8 +541,12 @@ static void dpm_watchdog_handler(struct timer_list *t) unsigned int time_left; if (wd->fatal) { + unsigned int this_cpu = smp_processor_id(); + dev_emerg(wd->dev, "**** DPM device timeout ****\n"); show_stack(wd->tsk, NULL, KERN_EMERG); + if (dpm_watchdog_all_cpu_backtrace) + trigger_allbutcpu_cpu_backtrace(this_cpu); panic("%s %s: unrecoverable failure\n", dev_driver_string(wd->dev), dev_name(wd->dev)); } @@ -888,12 +903,15 @@ static void device_resume_early(struct device *dev, pm_message_t state, bool asy TRACE_DEVICE(dev); TRACE_RESUME(0); - if (dev->power.syscore || dev->power.direct_complete) + if (dev->power.direct_complete) goto Out; if (!dev->power.is_late_suspended) goto Out; + if (dev->power.syscore) + goto Skip; + if (!dpm_wait_for_superior(dev, async)) goto Out; @@ -926,11 +944,11 @@ Run: Skip: dev->power.is_late_suspended = false; + pm_runtime_enable(dev); Out: TRACE_RESUME(error); - pm_runtime_enable(dev); complete_all(&dev->power.completion); if (error) { @@ -1615,12 +1633,6 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy TRACE_DEVICE(dev); TRACE_SUSPEND(0); - /* - * Disable runtime PM for the device without checking if there is a - * pending resume request for it. - */ - __pm_runtime_disable(dev, false); - dpm_wait_for_subordinate(dev, async); if (READ_ONCE(async_error)) @@ -1631,9 +1643,18 @@ static void device_suspend_late(struct device *dev, pm_message_t state, bool asy goto Complete; } - if (dev->power.syscore || dev->power.direct_complete) + if (dev->power.direct_complete) goto Complete; + /* + * Disable runtime PM for the device without checking if there is a + * pending resume request for it. + */ + __pm_runtime_disable(dev, false); + + if (dev->power.syscore) + goto Skip; + if (dev->pm_domain) { info = "late power domain "; callback = pm_late_early_op(&dev->pm_domain->ops, state); @@ -1664,6 +1685,7 @@ Run: WRITE_ONCE(async_error, error); dpm_save_failed_dev(dev_name(dev)); pm_dev_err(dev, state, async ? " async late" : " late", error); + pm_runtime_enable(dev); goto Complete; } dpm_propagate_wakeup_to_parent(dev); @@ -2126,6 +2148,7 @@ static int device_prepare(struct device *dev, pm_message_t state) device_lock(dev); dev->power.wakeup_path = false; + dev->power.out_band_wakeup = false; if (dev->power.no_pm_callbacks) goto unlock; diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 1b11a3cd4acc..62707738caa4 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -90,7 +90,7 @@ static void update_pm_runtime_accounting(struct device *dev) /* * Because ktime_get_mono_fast_ns() is not monotonic during * timekeeping updates, ensure that 'now' is after the last saved - * timesptamp. + * timestamp. */ if (now < last) return; @@ -217,7 +217,7 @@ static int dev_memalloc_noio(struct device *dev, void *data) * resume/suspend callback of any one of its ancestors(or the * block device itself), the deadlock may be triggered inside the * memory allocation since it might not complete until the block - * device becomes active and the involed page I/O finishes. The + * device becomes active and the involved page I/O finishes. The * situation is pointed out first by Alan Stern. Network device * are involved in iSCSI kind of situation. * @@ -1210,7 +1210,7 @@ EXPORT_SYMBOL_GPL(__pm_runtime_resume); * * Otherwise, if its runtime PM status is %RPM_ACTIVE and (1) @ign_usage_count * is set, or (2) @dev is not ignoring children and its active child count is - * nonero, or (3) the runtime PM usage counter of @dev is not zero, increment + * nonzero, or (3) the runtime PM usage counter of @dev is not zero, increment * the usage counter of @dev and return 1. * * Otherwise, return 0 without changing the usage counter. @@ -1664,9 +1664,12 @@ EXPORT_SYMBOL_GPL(devm_pm_runtime_get_noresume); * pm_runtime_forbid - Block runtime PM of a device. * @dev: Device to handle. * - * Increase the device's usage count and clear its power.runtime_auto flag, - * so that it cannot be suspended at run time until pm_runtime_allow() is called - * for it. + * Resume @dev if already suspended and block runtime suspend of @dev in such + * a way that it can be unblocked via the /sys/devices/.../power/control + * interface, or otherwise by calling pm_runtime_allow(). + * + * Calling this function many times in a row has the same effect as calling it + * once. */ void pm_runtime_forbid(struct device *dev) { @@ -1687,7 +1690,13 @@ EXPORT_SYMBOL_GPL(pm_runtime_forbid); * pm_runtime_allow - Unblock runtime PM of a device. * @dev: Device to handle. * - * Decrease the device's usage count and set its power.runtime_auto flag. + * Unblock runtime suspend of @dev after it has been blocked by + * pm_runtime_forbid() (for instance, if it has been blocked via the + * /sys/devices/.../power/control interface), check if @dev can be + * suspended and suspend it in that case. + * + * Calling this function many times in a row has the same effect as calling it + * once. */ void pm_runtime_allow(struct device *dev) { diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index cd6e559648b2..d8da7195bb00 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -238,10 +238,8 @@ int show_trace_dev_match(char *buf, size_t size) unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH); if (hash == value) { - int len = snprintf(buf, size, "%s\n", + int len = scnprintf(buf, size, "%s\n", dev_driver_string(dev)); - if (len > size) - len = size; buf += len; ret += len; size -= len; diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index d1283ff1080b..1e1a0e7eeac5 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -189,17 +189,16 @@ static void wakeup_source_remove(struct wakeup_source *ws) if (WARN_ON(!ws)) return; + /* + * After shutting down the timer, wakeup_source_activate() will warn if + * the given wakeup source is passed to it. + */ + timer_shutdown_sync(&ws->timer); + raw_spin_lock_irqsave(&events_lock, flags); list_del_rcu(&ws->entry); raw_spin_unlock_irqrestore(&events_lock, flags); synchronize_srcu(&wakeup_srcu); - - timer_delete_sync(&ws->timer); - /* - * Clear timer.function to make wakeup_source_not_registered() treat - * this wakeup source as not registered. - */ - ws->timer.function = NULL; } /** @@ -506,14 +505,14 @@ int device_set_wakeup_enable(struct device *dev, bool enable) EXPORT_SYMBOL_GPL(device_set_wakeup_enable); /** - * wakeup_source_not_registered - validate the given wakeup source. + * wakeup_source_not_usable - validate the given wakeup source. * @ws: Wakeup source to be validated. */ -static bool wakeup_source_not_registered(struct wakeup_source *ws) +static bool wakeup_source_not_usable(struct wakeup_source *ws) { /* - * Use timer struct to check if the given source is initialized - * by wakeup_source_add. + * Use the timer struct to check if the given wakeup source has been + * initialized by wakeup_source_add() and it is not going away. */ return ws->timer.function != pm_wakeup_timer_fn; } @@ -558,8 +557,7 @@ static void wakeup_source_activate(struct wakeup_source *ws) { unsigned int cec; - if (WARN_ONCE(wakeup_source_not_registered(ws), - "unregistered wakeup source\n")) + if (WARN_ONCE(wakeup_source_not_usable(ws), "unusable wakeup source\n")) return; ws->active = true; diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 6f31240ee4a9..1477329410ec 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -186,6 +186,7 @@ struct regcache_ops { enum regcache_type type; int (*init)(struct regmap *map); int (*exit)(struct regmap *map); + int (*populate)(struct regmap *map); #ifdef CONFIG_DEBUG_FS void (*debugfs_init)(struct regmap *map); #endif @@ -288,6 +289,7 @@ enum regmap_endian regmap_get_val_endian(struct device *dev, const struct regmap_bus *bus, const struct regmap_config *config); +extern struct regcache_ops regcache_flat_sparse_ops; extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_maple_ops; extern struct regcache_ops regcache_flat_ops; diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c index f36d3618b67c..53cc59c84e2f 100644 --- a/drivers/base/regmap/regcache-flat.c +++ b/drivers/base/regmap/regcache-flat.c @@ -6,7 +6,11 @@ // // Author: Mark Brown <broonie@opensource.wolfsonmicro.com> +#include <linux/bitmap.h> +#include <linux/bitops.h> #include <linux/device.h> +#include <linux/limits.h> +#include <linux/overflow.h> #include <linux/seq_file.h> #include <linux/slab.h> @@ -18,46 +22,92 @@ static inline unsigned int regcache_flat_get_index(const struct regmap *map, return regcache_get_index_by_order(map, reg); } +struct regcache_flat_data { + unsigned long *valid; + unsigned int data[]; +}; + static int regcache_flat_init(struct regmap *map) { - int i; - unsigned int *cache; + unsigned int cache_size; + struct regcache_flat_data *cache; if (!map || map->reg_stride_order < 0 || !map->max_register_is_set) return -EINVAL; - map->cache = kcalloc(regcache_flat_get_index(map, map->max_register) - + 1, sizeof(unsigned int), map->alloc_flags); - if (!map->cache) + cache_size = regcache_flat_get_index(map, map->max_register) + 1; + cache = kzalloc(struct_size(cache, data, cache_size), map->alloc_flags); + if (!cache) return -ENOMEM; - cache = map->cache; + cache->valid = bitmap_zalloc(cache_size, map->alloc_flags); + if (!cache->valid) + goto err_free; + + map->cache = cache; + + return 0; + +err_free: + kfree(cache); + return -ENOMEM; +} + +static int regcache_flat_exit(struct regmap *map) +{ + struct regcache_flat_data *cache = map->cache; + + if (cache) + bitmap_free(cache->valid); + + kfree(cache); + map->cache = NULL; + + return 0; +} + +static int regcache_flat_populate(struct regmap *map) +{ + struct regcache_flat_data *cache = map->cache; + unsigned int i; for (i = 0; i < map->num_reg_defaults; i++) { unsigned int reg = map->reg_defaults[i].reg; unsigned int index = regcache_flat_get_index(map, reg); - cache[index] = map->reg_defaults[i].def; + cache->data[index] = map->reg_defaults[i].def; + __set_bit(index, cache->valid); } return 0; } -static int regcache_flat_exit(struct regmap *map) +static int regcache_flat_read(struct regmap *map, + unsigned int reg, unsigned int *value) { - kfree(map->cache); - map->cache = NULL; + struct regcache_flat_data *cache = map->cache; + unsigned int index = regcache_flat_get_index(map, reg); + + /* legacy behavior: ignore validity, but warn the user */ + if (unlikely(!test_bit(index, cache->valid))) + dev_warn_once(map->dev, + "using zero-initialized flat cache, this may cause unexpected behavior"); + + *value = cache->data[index]; return 0; } -static int regcache_flat_read(struct regmap *map, - unsigned int reg, unsigned int *value) +static int regcache_flat_sparse_read(struct regmap *map, + unsigned int reg, unsigned int *value) { - unsigned int *cache = map->cache; + struct regcache_flat_data *cache = map->cache; unsigned int index = regcache_flat_get_index(map, reg); - *value = cache[index]; + if (unlikely(!test_bit(index, cache->valid))) + return -ENOENT; + + *value = cache->data[index]; return 0; } @@ -65,10 +115,23 @@ static int regcache_flat_read(struct regmap *map, static int regcache_flat_write(struct regmap *map, unsigned int reg, unsigned int value) { - unsigned int *cache = map->cache; + struct regcache_flat_data *cache = map->cache; unsigned int index = regcache_flat_get_index(map, reg); - cache[index] = value; + cache->data[index] = value; + __set_bit(index, cache->valid); + + return 0; +} + +static int regcache_flat_drop(struct regmap *map, unsigned int min, + unsigned int max) +{ + struct regcache_flat_data *cache = map->cache; + unsigned int bitmap_min = regcache_flat_get_index(map, min); + unsigned int bitmap_max = regcache_flat_get_index(map, max); + + bitmap_clear(cache->valid, bitmap_min, bitmap_max + 1 - bitmap_min); return 0; } @@ -78,6 +141,18 @@ struct regcache_ops regcache_flat_ops = { .name = "flat", .init = regcache_flat_init, .exit = regcache_flat_exit, + .populate = regcache_flat_populate, .read = regcache_flat_read, .write = regcache_flat_write, }; + +struct regcache_ops regcache_flat_sparse_ops = { + .type = REGCACHE_FLAT_S, + .name = "flat-sparse", + .init = regcache_flat_init, + .exit = regcache_flat_exit, + .populate = regcache_flat_populate, + .read = regcache_flat_sparse_read, + .write = regcache_flat_write, + .drop = regcache_flat_drop, +}; diff --git a/drivers/base/regmap/regcache-maple.c b/drivers/base/regmap/regcache-maple.c index 2319c30283a6..ca1c72b68f31 100644 --- a/drivers/base/regmap/regcache-maple.c +++ b/drivers/base/regmap/regcache-maple.c @@ -289,6 +289,23 @@ out: return ret; } +static int regcache_maple_init(struct regmap *map) +{ + struct maple_tree *mt; + + mt = kmalloc(sizeof(*mt), map->alloc_flags); + if (!mt) + return -ENOMEM; + map->cache = mt; + + mt_init(mt); + + if (!mt_external_lock(mt) && map->lock_key) + lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1); + + return 0; +} + static int regcache_maple_exit(struct regmap *map) { struct maple_tree *mt = map->cache; @@ -340,26 +357,12 @@ static int regcache_maple_insert_block(struct regmap *map, int first, return ret; } -static int regcache_maple_init(struct regmap *map) +static int regcache_maple_populate(struct regmap *map) { - struct maple_tree *mt; int i; int ret; int range_start; - mt = kmalloc(sizeof(*mt), map->alloc_flags); - if (!mt) - return -ENOMEM; - map->cache = mt; - - mt_init(mt); - - if (!mt_external_lock(mt) && map->lock_key) - lockdep_set_class_and_subclass(&mt->ma_lock, map->lock_key, 1); - - if (!map->num_reg_defaults) - return 0; - range_start = 0; /* Scan for ranges of contiguous registers */ @@ -369,23 +372,14 @@ static int regcache_maple_init(struct regmap *map) ret = regcache_maple_insert_block(map, range_start, i - 1); if (ret != 0) - goto err; + return ret; range_start = i; } } /* Add the last block */ - ret = regcache_maple_insert_block(map, range_start, - map->num_reg_defaults - 1); - if (ret != 0) - goto err; - - return 0; - -err: - regcache_maple_exit(map); - return ret; + return regcache_maple_insert_block(map, range_start, map->num_reg_defaults - 1); } struct regcache_ops regcache_maple_ops = { @@ -393,6 +387,7 @@ struct regcache_ops regcache_maple_ops = { .name = "maple", .init = regcache_maple_init, .exit = regcache_maple_exit, + .populate = regcache_maple_populate, .read = regcache_maple_read, .write = regcache_maple_write, .drop = regcache_maple_drop, diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index a9d17f316e55..3344b82c3799 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -184,8 +184,6 @@ static void rbtree_debugfs_init(struct regmap *map) static int regcache_rbtree_init(struct regmap *map) { struct regcache_rbtree_ctx *rbtree_ctx; - int i; - int ret; map->cache = kmalloc(sizeof *rbtree_ctx, map->alloc_flags); if (!map->cache) @@ -195,19 +193,7 @@ static int regcache_rbtree_init(struct regmap *map) rbtree_ctx->root = RB_ROOT; rbtree_ctx->cached_rbnode = NULL; - for (i = 0; i < map->num_reg_defaults; i++) { - ret = regcache_rbtree_write(map, - map->reg_defaults[i].reg, - map->reg_defaults[i].def); - if (ret) - goto err; - } - return 0; - -err: - regcache_rbtree_exit(map); - return ret; } static int regcache_rbtree_exit(struct regmap *map) @@ -239,6 +225,22 @@ static int regcache_rbtree_exit(struct regmap *map) return 0; } +static int regcache_rbtree_populate(struct regmap *map) +{ + unsigned int i; + int ret; + + for (i = 0; i < map->num_reg_defaults; i++) { + ret = regcache_rbtree_write(map, + map->reg_defaults[i].reg, + map->reg_defaults[i].def); + if (ret) + return ret; + } + + return 0; +} + static int regcache_rbtree_read(struct regmap *map, unsigned int reg, unsigned int *value) { @@ -546,6 +548,7 @@ struct regcache_ops regcache_rbtree_ops = { .name = "rbtree", .init = regcache_rbtree_init, .exit = regcache_rbtree_exit, + .populate = regcache_rbtree_populate, #ifdef CONFIG_DEBUG_FS .debugfs_init = rbtree_debugfs_init, #endif diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index c7650fa434ad..319c342bf5a0 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -16,6 +16,7 @@ #include "internal.h" static const struct regcache_ops *cache_types[] = { + ®cache_flat_sparse_ops, ®cache_rbtree_ops, ®cache_maple_ops, ®cache_flat_ops, @@ -221,8 +222,24 @@ int regcache_init(struct regmap *map, const struct regmap_config *config) if (ret) goto err_free; } + + if (map->num_reg_defaults && map->cache_ops->populate) { + dev_dbg(map->dev, "Populating %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); + ret = map->cache_ops->populate(map); + map->unlock(map->lock_arg); + if (ret) + goto err_exit; + } return 0; +err_exit: + if (map->cache_ops->exit) { + dev_dbg(map->dev, "Destroying %s cache\n", map->cache_ops->name); + map->lock(map->lock_arg); + ret = map->cache_ops->exit(map); + map->unlock(map->lock_arg); + } err_free: kfree(map->reg_defaults); if (map->cache_free) diff --git a/drivers/base/regmap/regmap-i3c.c b/drivers/base/regmap/regmap-i3c.c index b5300b7c477e..6a0f6c826980 100644 --- a/drivers/base/regmap/regmap-i3c.c +++ b/drivers/base/regmap/regmap-i3c.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2018 Synopsys, Inc. and/or its affiliates. +#include <linux/array_size.h> #include <linux/regmap.h> #include <linux/i3c/device.h> #include <linux/i3c/master.h> @@ -18,7 +19,7 @@ static int regmap_i3c_write(void *context, const void *data, size_t count) }, }; - return i3c_device_do_priv_xfers(i3c, xfers, 1); + return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers)); } static int regmap_i3c_read(void *context, @@ -37,7 +38,7 @@ static int regmap_i3c_read(void *context, xfers[1].len = val_size; xfers[1].data.in = val; - return i3c_device_do_priv_xfers(i3c, xfers, 2); + return i3c_device_do_priv_xfers(i3c, xfers, ARRAY_SIZE(xfers)); } static const struct regmap_bus regmap_i3c = { diff --git a/drivers/base/regmap/regmap-kunit.c b/drivers/base/regmap/regmap-kunit.c index 95c5bf2a78ee..f6fc5ed016da 100644 --- a/drivers/base/regmap/regmap-kunit.c +++ b/drivers/base/regmap/regmap-kunit.c @@ -54,6 +54,8 @@ static const char *regcache_type_name(enum regcache_type type) return "none"; case REGCACHE_FLAT: return "flat"; + case REGCACHE_FLAT_S: + return "flat-sparse"; case REGCACHE_RBTREE: return "rbtree"; case REGCACHE_MAPLE: @@ -93,6 +95,8 @@ static const struct regmap_test_param regcache_types_list[] = { { .cache = REGCACHE_NONE, .fast_io = true }, { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, + { .cache = REGCACHE_FLAT_S }, + { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, @@ -104,6 +108,8 @@ KUNIT_ARRAY_PARAM(regcache_types, regcache_types_list, param_to_desc); static const struct regmap_test_param real_cache_types_only_list[] = { { .cache = REGCACHE_FLAT }, { .cache = REGCACHE_FLAT, .fast_io = true }, + { .cache = REGCACHE_FLAT_S }, + { .cache = REGCACHE_FLAT_S, .fast_io = true }, { .cache = REGCACHE_RBTREE }, { .cache = REGCACHE_RBTREE, .fast_io = true }, { .cache = REGCACHE_MAPLE }, @@ -119,6 +125,12 @@ static const struct regmap_test_param real_cache_types_list[] = { { .cache = REGCACHE_FLAT, .from_reg = 0x2002 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2003 }, { .cache = REGCACHE_FLAT, .from_reg = 0x2004 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, @@ -136,6 +148,12 @@ static const struct regmap_test_param real_cache_types_list[] = { KUNIT_ARRAY_PARAM(real_cache_types, real_cache_types_list, param_to_desc); static const struct regmap_test_param sparse_cache_types_list[] = { + { .cache = REGCACHE_FLAT_S, .from_reg = 0 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0, .fast_io = true }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2001 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2002 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2003 }, + { .cache = REGCACHE_FLAT_S, .from_reg = 0x2004 }, { .cache = REGCACHE_RBTREE, .from_reg = 0 }, { .cache = REGCACHE_RBTREE, .from_reg = 0, .fast_io = true }, { .cache = REGCACHE_RBTREE, .from_reg = 0x2001 }, @@ -1597,6 +1615,8 @@ static const struct regmap_test_param raw_types_list[] = { { .cache = REGCACHE_NONE, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE }, @@ -1608,6 +1628,8 @@ KUNIT_ARRAY_PARAM(raw_test_types, raw_types_list, param_to_desc); static const struct regmap_test_param raw_cache_types_list[] = { { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_FLAT, .val_endian = REGMAP_ENDIAN_BIG }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_LITTLE }, + { .cache = REGCACHE_FLAT_S, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_LITTLE }, { .cache = REGCACHE_RBTREE, .val_endian = REGMAP_ENDIAN_BIG }, { .cache = REGCACHE_MAPLE, .val_endian = REGMAP_ENDIAN_LITTLE }, diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c index 86644bbd0710..6a61629f5f89 100644 --- a/drivers/base/regmap/regmap-sdw-mbq.c +++ b/drivers/base/regmap/regmap-sdw-mbq.c @@ -15,11 +15,13 @@ struct regmap_mbq_context { struct device *dev; + struct sdw_slave *sdw; + + bool (*readable_reg)(struct device *dev, unsigned int reg); struct regmap_sdw_mbq_cfg cfg; int val_size; - bool (*readable_reg)(struct device *dev, unsigned int reg); }; static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg) @@ -46,7 +48,7 @@ static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned i static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg, struct regmap_mbq_context *ctx) { - struct device *dev = &slave->dev; + struct device *dev = ctx->dev; int val, ret = 0; dev_dbg(dev, "Deferring transaction for 0x%x\n", reg); @@ -96,8 +98,7 @@ static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave, static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val) { struct regmap_mbq_context *ctx = context; - struct device *dev = ctx->dev; - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = ctx->sdw; bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); int mbq_size = regmap_sdw_mbq_size(ctx, reg); int ret; @@ -156,8 +157,7 @@ static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave, static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val) { struct regmap_mbq_context *ctx = context; - struct device *dev = ctx->dev; - struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct sdw_slave *slave = ctx->sdw; bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg); int mbq_size = regmap_sdw_mbq_size(ctx, reg); int ret; @@ -208,6 +208,7 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config) static struct regmap_mbq_context * regmap_sdw_mbq_gen_context(struct device *dev, + struct sdw_slave *sdw, const struct regmap_config *config, const struct regmap_sdw_mbq_cfg *mbq_config) { @@ -218,6 +219,7 @@ regmap_sdw_mbq_gen_context(struct device *dev, return ERR_PTR(-ENOMEM); ctx->dev = dev; + ctx->sdw = sdw; if (mbq_config) ctx->cfg = *mbq_config; @@ -228,7 +230,7 @@ regmap_sdw_mbq_gen_context(struct device *dev, return ctx; } -struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw, +struct regmap *__regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw, const struct regmap_config *config, const struct regmap_sdw_mbq_cfg *mbq_config, struct lock_class_key *lock_key, @@ -241,16 +243,16 @@ struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw, if (ret) return ERR_PTR(ret); - ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config); + ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config); if (IS_ERR(ctx)) return ERR_CAST(ctx); - return __regmap_init(&sdw->dev, ®map_sdw_mbq, ctx, + return __regmap_init(dev, ®map_sdw_mbq, ctx, config, lock_key, lock_name); } EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq); -struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw, +struct regmap *__devm_regmap_init_sdw_mbq(struct device *dev, struct sdw_slave *sdw, const struct regmap_config *config, const struct regmap_sdw_mbq_cfg *mbq_config, struct lock_class_key *lock_key, @@ -263,11 +265,11 @@ struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw, if (ret) return ERR_PTR(ret); - ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config); + ctx = regmap_sdw_mbq_gen_context(dev, sdw, config, mbq_config); if (IS_ERR(ctx)) return ERR_CAST(ctx); - return __devm_regmap_init(&sdw->dev, ®map_sdw_mbq, ctx, + return __devm_regmap_init(dev, ®map_sdw_mbq, ctx, config, lock_key, lock_name); } EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq); diff --git a/drivers/base/regmap/regmap-slimbus.c b/drivers/base/regmap/regmap-slimbus.c index 54eb7d227cf4..e523fae73004 100644 --- a/drivers/base/regmap/regmap-slimbus.c +++ b/drivers/base/regmap/regmap-slimbus.c @@ -48,8 +48,7 @@ struct regmap *__regmap_init_slimbus(struct slim_device *slimbus, if (IS_ERR(bus)) return ERR_CAST(bus); - return __regmap_init(&slimbus->dev, bus, &slimbus->dev, config, - lock_key, lock_name); + return __regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name); } EXPORT_SYMBOL_GPL(__regmap_init_slimbus); @@ -63,8 +62,7 @@ struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus, if (IS_ERR(bus)) return ERR_CAST(bus); - return __devm_regmap_init(&slimbus->dev, bus, &slimbus, config, - lock_key, lock_name); + return __devm_regmap_init(&slimbus->dev, bus, slimbus, config, lock_key, lock_name); } EXPORT_SYMBOL_GPL(__devm_regmap_init_slimbus); diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index be1e9e61a7bf..16a8301c25d6 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -535,14 +535,29 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, ref_array = prop->pointer; ref = &ref_array[index]; - refnode = software_node_fwnode(ref->node); + /* + * A software node can reference other software nodes or firmware + * nodes (which are the abstraction layer sitting on top of them). + * This is done to ensure we can create references to static software + * nodes before they're registered with the firmware node framework. + * At the time the reference is being resolved, we expect the swnodes + * in question to already have been registered and to be backed by + * a firmware node. This is why we use the fwnode API below to read the + * relevant properties and bump the reference count. + */ + + if (ref->swnode) + refnode = software_node_fwnode(ref->swnode); + else if (ref->fwnode) + refnode = ref->fwnode; + else + return -EINVAL; + if (!refnode) return -ENOENT; if (nargs_prop) { - error = property_entry_read_int_array(ref->node->properties, - nargs_prop, sizeof(u32), - &nargs_prop_val, 1); + error = fwnode_property_read_u32(refnode, nargs_prop, &nargs_prop_val); if (error) return error; @@ -555,7 +570,7 @@ software_node_get_reference_args(const struct fwnode_handle *fwnode, if (!args) return 0; - args->fwnode = software_node_get(refnode); + args->fwnode = fwnode_handle_get(refnode); args->nargs = nargs; for (i = 0; i < nargs; i++) @@ -635,7 +650,10 @@ software_node_graph_get_remote_endpoint(const struct fwnode_handle *fwnode) ref = prop->pointer; - return software_node_get(software_node_fwnode(ref[0].node)); + if (!ref->swnode) + return NULL; + + return software_node_get(software_node_fwnode(ref->swnode)); } static struct fwnode_handle * diff --git a/drivers/base/syscore.c b/drivers/base/syscore.c index 13db1f78d2ce..483adb796654 100644 --- a/drivers/base/syscore.c +++ b/drivers/base/syscore.c @@ -11,32 +11,32 @@ #include <linux/suspend.h> #include <trace/events/power.h> -static LIST_HEAD(syscore_ops_list); -static DEFINE_MUTEX(syscore_ops_lock); +static LIST_HEAD(syscore_list); +static DEFINE_MUTEX(syscore_lock); /** - * register_syscore_ops - Register a set of system core operations. - * @ops: System core operations to register. + * register_syscore - Register a set of system core operations. + * @syscore: System core operations to register. */ -void register_syscore_ops(struct syscore_ops *ops) +void register_syscore(struct syscore *syscore) { - mutex_lock(&syscore_ops_lock); - list_add_tail(&ops->node, &syscore_ops_list); - mutex_unlock(&syscore_ops_lock); + mutex_lock(&syscore_lock); + list_add_tail(&syscore->node, &syscore_list); + mutex_unlock(&syscore_lock); } -EXPORT_SYMBOL_GPL(register_syscore_ops); +EXPORT_SYMBOL_GPL(register_syscore); /** - * unregister_syscore_ops - Unregister a set of system core operations. - * @ops: System core operations to unregister. + * unregister_syscore - Unregister a set of system core operations. + * @syscore: System core operations to unregister. */ -void unregister_syscore_ops(struct syscore_ops *ops) +void unregister_syscore(struct syscore *syscore) { - mutex_lock(&syscore_ops_lock); - list_del(&ops->node); - mutex_unlock(&syscore_ops_lock); + mutex_lock(&syscore_lock); + list_del(&syscore->node); + mutex_unlock(&syscore_lock); } -EXPORT_SYMBOL_GPL(unregister_syscore_ops); +EXPORT_SYMBOL_GPL(unregister_syscore); #ifdef CONFIG_PM_SLEEP /** @@ -46,7 +46,7 @@ EXPORT_SYMBOL_GPL(unregister_syscore_ops); */ int syscore_suspend(void) { - struct syscore_ops *ops; + struct syscore *syscore; int ret = 0; trace_suspend_resume(TPS("syscore_suspend"), 0, true); @@ -59,25 +59,27 @@ int syscore_suspend(void) WARN_ONCE(!irqs_disabled(), "Interrupts enabled before system core suspend.\n"); - list_for_each_entry_reverse(ops, &syscore_ops_list, node) - if (ops->suspend) { - pm_pr_dbg("Calling %pS\n", ops->suspend); - ret = ops->suspend(); + list_for_each_entry_reverse(syscore, &syscore_list, node) + if (syscore->ops->suspend) { + pm_pr_dbg("Calling %pS\n", syscore->ops->suspend); + ret = syscore->ops->suspend(syscore->data); if (ret) goto err_out; WARN_ONCE(!irqs_disabled(), - "Interrupts enabled after %pS\n", ops->suspend); + "Interrupts enabled after %pS\n", + syscore->ops->suspend); } trace_suspend_resume(TPS("syscore_suspend"), 0, false); return 0; err_out: - pr_err("PM: System core suspend callback %pS failed.\n", ops->suspend); + pr_err("PM: System core suspend callback %pS failed.\n", + syscore->ops->suspend); - list_for_each_entry_continue(ops, &syscore_ops_list, node) - if (ops->resume) - ops->resume(); + list_for_each_entry_continue(syscore, &syscore_list, node) + if (syscore->ops->resume) + syscore->ops->resume(syscore->data); return ret; } @@ -90,18 +92,19 @@ EXPORT_SYMBOL_GPL(syscore_suspend); */ void syscore_resume(void) { - struct syscore_ops *ops; + struct syscore *syscore; trace_suspend_resume(TPS("syscore_resume"), 0, true); WARN_ONCE(!irqs_disabled(), "Interrupts enabled before system core resume.\n"); - list_for_each_entry(ops, &syscore_ops_list, node) - if (ops->resume) { - pm_pr_dbg("Calling %pS\n", ops->resume); - ops->resume(); + list_for_each_entry(syscore, &syscore_list, node) + if (syscore->ops->resume) { + pm_pr_dbg("Calling %pS\n", syscore->ops->resume); + syscore->ops->resume(syscore->data); WARN_ONCE(!irqs_disabled(), - "Interrupts enabled after %pS\n", ops->resume); + "Interrupts enabled after %pS\n", + syscore->ops->resume); } trace_suspend_resume(TPS("syscore_resume"), 0, false); } @@ -113,16 +116,17 @@ EXPORT_SYMBOL_GPL(syscore_resume); */ void syscore_shutdown(void) { - struct syscore_ops *ops; + struct syscore *syscore; - mutex_lock(&syscore_ops_lock); + mutex_lock(&syscore_lock); - list_for_each_entry_reverse(ops, &syscore_ops_list, node) - if (ops->shutdown) { + list_for_each_entry_reverse(syscore, &syscore_list, node) + if (syscore->ops->shutdown) { if (initcall_debug) - pr_info("PM: Calling %pS\n", ops->shutdown); - ops->shutdown(); + pr_info("PM: Calling %pS\n", + syscore->ops->shutdown); + syscore->ops->shutdown(syscore->data); } - mutex_unlock(&syscore_ops_lock); + mutex_unlock(&syscore_lock); } |
