diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Kconfig | 7 | ||||
-rw-r--r-- | drivers/base/cacheinfo.c | 59 | ||||
-rw-r--r-- | drivers/base/dd.c | 5 | ||||
-rw-r--r-- | drivers/base/devres.c | 66 | ||||
-rw-r--r-- | drivers/base/power/main.c | 8 | ||||
-rw-r--r-- | drivers/base/power/trace.c | 27 | ||||
-rw-r--r-- | drivers/base/regmap/regcache-lzo.c | 8 | ||||
-rw-r--r-- | drivers/base/soc.c | 79 | ||||
-rw-r--r-- | drivers/base/topology.c | 42 |
9 files changed, 207 insertions, 94 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index fdf44cac08e6..2abea876c0a0 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -213,14 +213,16 @@ config DEBUG_DEVRES If you are unsure about this, Say N here. config DEBUG_TEST_DRIVER_REMOVE - bool "Test driver remove calls during probe" + bool "Test driver remove calls during probe (UNSTABLE)" depends on DEBUG_KERNEL help Say Y here if you want the Driver core to test driver remove functions by calling probe, remove, probe. This tests the remove path without having to unbind the driver or unload the driver module. - If you are unsure about this, say N here. + This option is expected to find errors and may render your system + unusable. You should say N here unless you are explicitly looking to + test this functionality. config SYS_HYPERVISOR bool @@ -235,6 +237,7 @@ config GENERIC_CPU_AUTOPROBE config SOC_BUS bool + select GLOB source "drivers/base/regmap/Kconfig" diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index e9fd32e91668..47983a2ba621 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -498,57 +498,30 @@ err: return rc; } -static void cache_remove_dev(unsigned int cpu) +static int cacheinfo_cpu_online(unsigned int cpu) { - if (!cpumask_test_cpu(cpu, &cache_dev_map)) - return; - cpumask_clear_cpu(cpu, &cache_dev_map); + int rc = detect_cache_attributes(cpu); - cpu_cache_sysfs_exit(cpu); + if (rc) + return rc; + rc = cache_add_dev(cpu); + if (rc) + free_cache_attributes(cpu); + return rc; } -static int cacheinfo_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) +static int cacheinfo_cpu_pre_down(unsigned int cpu) { - unsigned int cpu = (unsigned long)hcpu; - int rc = 0; - - switch (action & ~CPU_TASKS_FROZEN) { - case CPU_ONLINE: - rc = detect_cache_attributes(cpu); - if (!rc) - rc = cache_add_dev(cpu); - break; - case CPU_DEAD: - cache_remove_dev(cpu); - free_cache_attributes(cpu); - break; - } - return notifier_from_errno(rc); + if (cpumask_test_and_clear_cpu(cpu, &cache_dev_map)) + cpu_cache_sysfs_exit(cpu); + + free_cache_attributes(cpu); + return 0; } static int __init cacheinfo_sysfs_init(void) { - int cpu, rc = 0; - - cpu_notifier_register_begin(); - - for_each_online_cpu(cpu) { - rc = detect_cache_attributes(cpu); - if (rc) - goto out; - rc = cache_add_dev(cpu); - if (rc) { - free_cache_attributes(cpu); - pr_err("error populating cacheinfo..cpu%d\n", cpu); - goto out; - } - } - __hotcpu_notifier(cacheinfo_cpu_callback, 0); - -out: - cpu_notifier_register_done(); - return rc; + return cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "base/cacheinfo:online", + cacheinfo_cpu_online, cacheinfo_cpu_pre_down); } - device_initcall(cacheinfo_sysfs_init); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index d22a7260f42b..d76cd97a98b6 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -324,7 +324,8 @@ static int really_probe(struct device *dev, struct device_driver *drv) { int ret = -EPROBE_DEFER; int local_trigger_count = atomic_read(&deferred_trigger_count); - bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE); + bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) && + !drv->suppress_bind_attrs; if (defer_all_probes) { /* @@ -383,7 +384,7 @@ re_probe: if (test_remove) { test_remove = false; - if (dev->bus && dev->bus->remove) + if (dev->bus->remove) dev->bus->remove(dev); else if (drv->remove) drv->remove(dev); diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 8fc654f0807b..71d577025285 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -10,6 +10,7 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/percpu.h> #include "base.h" @@ -985,3 +986,68 @@ void devm_free_pages(struct device *dev, unsigned long addr) &devres)); } EXPORT_SYMBOL_GPL(devm_free_pages); + +static void devm_percpu_release(struct device *dev, void *pdata) +{ + void __percpu *p; + + p = *(void __percpu **)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 + * @size: Size of per-cpu memory to allocate + * @align: Alignment of per-cpu memory to allocate + * + * Managed alloc_percpu. Per-cpu memory allocated with this function is + * automatically freed on driver detach. + * + * RETURNS: + * Pointer to allocated memory on success, NULL on failure. + */ +void __percpu *__devm_alloc_percpu(struct device *dev, size_t size, + size_t align) +{ + void *p; + void __percpu *pcpu; + + pcpu = __alloc_percpu(size, align); + if (!pcpu) + return NULL; + + p = devres_alloc(devm_percpu_release, sizeof(void *), GFP_KERNEL); + if (!p) { + free_percpu(pcpu); + return NULL; + } + + *(void __percpu **)p = pcpu; + + devres_add(dev, p); + + 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) +{ + WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match, + (void *)pdata)); +} +EXPORT_SYMBOL_GPL(devm_free_percpu); diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index e44944f4be77..2932a5bd892f 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -1027,6 +1027,8 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a TRACE_DEVICE(dev); TRACE_SUSPEND(0); + dpm_wait_for_children(dev, async); + if (async_error) goto Complete; @@ -1038,8 +1040,6 @@ static int __device_suspend_noirq(struct device *dev, pm_message_t state, bool a if (dev->power.syscore || dev->power.direct_complete) goto Complete; - dpm_wait_for_children(dev, async); - if (dev->pm_domain) { info = "noirq power domain "; callback = pm_noirq_op(&dev->pm_domain->ops, state); @@ -1174,6 +1174,8 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as __pm_runtime_disable(dev, false); + dpm_wait_for_children(dev, async); + if (async_error) goto Complete; @@ -1185,8 +1187,6 @@ static int __device_suspend_late(struct device *dev, pm_message_t state, bool as if (dev->power.syscore || dev->power.direct_complete) goto Complete; - dpm_wait_for_children(dev, async); - if (dev->pm_domain) { info = "late power domain "; callback = pm_late_early_op(&dev->pm_domain->ops, state); diff --git a/drivers/base/power/trace.c b/drivers/base/power/trace.c index efec10b49d59..1cda505d6a85 100644 --- a/drivers/base/power/trace.c +++ b/drivers/base/power/trace.c @@ -10,6 +10,7 @@ #include <linux/pm-trace.h> #include <linux/export.h> #include <linux/rtc.h> +#include <linux/suspend.h> #include <linux/mc146818rtc.h> @@ -74,6 +75,9 @@ #define DEVSEED (7919) +bool pm_trace_rtc_abused __read_mostly; +EXPORT_SYMBOL_GPL(pm_trace_rtc_abused); + static unsigned int dev_hash_value; static int set_magic_time(unsigned int user, unsigned int file, unsigned int device) @@ -104,6 +108,7 @@ static int set_magic_time(unsigned int user, unsigned int file, unsigned int dev time.tm_min = (n % 20) * 3; n /= 20; mc146818_set_time(&time); + pm_trace_rtc_abused = true; return n ? -1 : 0; } @@ -239,9 +244,31 @@ int show_trace_dev_match(char *buf, size_t size) return ret; } +static int +pm_trace_notify(struct notifier_block *nb, unsigned long mode, void *_unused) +{ + switch (mode) { + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + if (pm_trace_rtc_abused) { + pm_trace_rtc_abused = false; + pr_warn("Possible incorrect RTC due to pm_trace, please use 'ntpdate' or 'rdate' to reset it.\n"); + } + break; + default: + break; + } + return 0; +} + +static struct notifier_block pm_trace_nb = { + .notifier_call = pm_trace_notify, +}; + static int early_resume_init(void) { hash_value_early_read = read_magic_time(); + register_pm_notifier(&pm_trace_nb); return 0; } diff --git a/drivers/base/regmap/regcache-lzo.c b/drivers/base/regmap/regcache-lzo.c index 6f77d7319fc6..4ff311374c4a 100644 --- a/drivers/base/regmap/regcache-lzo.c +++ b/drivers/base/regmap/regcache-lzo.c @@ -236,15 +236,13 @@ static int regcache_lzo_read(struct regmap *map, { struct regcache_lzo_ctx *lzo_block, **lzo_blocks; int ret, blkindex, blkpos; - size_t blksize, tmp_dst_len; + size_t tmp_dst_len; void *tmp_dst; /* index of the compressed lzo block */ blkindex = regcache_lzo_get_blkindex(map, reg); /* register index within the decompressed block */ blkpos = regcache_lzo_get_blkpos(map, reg); - /* size of the compressed block */ - blksize = regcache_lzo_get_blksize(map); lzo_blocks = map->cache; lzo_block = lzo_blocks[blkindex]; @@ -275,15 +273,13 @@ static int regcache_lzo_write(struct regmap *map, { struct regcache_lzo_ctx *lzo_block, **lzo_blocks; int ret, blkindex, blkpos; - size_t blksize, tmp_dst_len; + size_t tmp_dst_len; void *tmp_dst; /* index of the compressed lzo block */ blkindex = regcache_lzo_get_blkindex(map, reg); /* register index within the decompressed block */ blkpos = regcache_lzo_get_blkpos(map, reg); - /* size of the compressed block */ - blksize = regcache_lzo_get_blksize(map); lzo_blocks = map->cache; lzo_block = lzo_blocks[blkindex]; diff --git a/drivers/base/soc.c b/drivers/base/soc.c index b63f23e6ad61..dc26e5949a32 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -13,6 +13,7 @@ #include <linux/spinlock.h> #include <linux/sys_soc.h> #include <linux/err.h> +#include <linux/glob.h> static DEFINE_IDA(soc_ida); @@ -113,6 +114,12 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr struct soc_device *soc_dev; int ret; + if (!soc_bus_type.p) { + ret = bus_register(&soc_bus_type); + if (ret) + goto out1; + } + soc_dev = kzalloc(sizeof(*soc_dev), GFP_KERNEL); if (!soc_dev) { ret = -ENOMEM; @@ -156,6 +163,78 @@ void soc_device_unregister(struct soc_device *soc_dev) static int __init soc_bus_register(void) { + if (soc_bus_type.p) + return 0; + return bus_register(&soc_bus_type); } core_initcall(soc_bus_register); + +static int soc_device_match_one(struct device *dev, void *arg) +{ + struct soc_device *soc_dev = container_of(dev, struct soc_device, dev); + const struct soc_device_attribute *match = arg; + + if (match->machine && + (!soc_dev->attr->machine || + !glob_match(match->machine, soc_dev->attr->machine))) + return 0; + + if (match->family && + (!soc_dev->attr->family || + !glob_match(match->family, soc_dev->attr->family))) + return 0; + + if (match->revision && + (!soc_dev->attr->revision || + !glob_match(match->revision, soc_dev->attr->revision))) + return 0; + + if (match->soc_id && + (!soc_dev->attr->soc_id || + !glob_match(match->soc_id, soc_dev->attr->soc_id))) + return 0; + + return 1; +} + +/* + * soc_device_match - identify the SoC in the machine + * @matches: zero-terminated array of possible matches + * + * returns the first matching entry of the argument array, or NULL + * if none of them match. + * + * This function is meant as a helper in place of of_match_node() + * in cases where either no device tree is available or the information + * in a device node is insufficient to identify a particular variant + * by its compatible strings or other properties. For new devices, + * the DT binding should always provide unique compatible strings + * that allow the use of of_match_node() instead. + * + * The calling function can use the .data entry of the + * soc_device_attribute to pass a structure or function pointer for + * each entry. + */ +const struct soc_device_attribute *soc_device_match( + const struct soc_device_attribute *matches) +{ + int ret = 0; + + if (!matches) + return NULL; + + while (!ret) { + if (!(matches->machine || matches->family || + matches->revision || matches->soc_id)) + break; + ret = bus_for_each_dev(&soc_bus_type, NULL, (void *)matches, + soc_device_match_one); + if (!ret) + matches++; + else + return matches; + } + return NULL; +} +EXPORT_SYMBOL_GPL(soc_device_match); diff --git a/drivers/base/topology.c b/drivers/base/topology.c index df3c97cb4c99..d6ec1c546f5b 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -118,51 +118,19 @@ static int topology_add_dev(unsigned int cpu) return sysfs_create_group(&dev->kobj, &topology_attr_group); } -static void topology_remove_dev(unsigned int cpu) +static int topology_remove_dev(unsigned int cpu) { struct device *dev = get_cpu_device(cpu); sysfs_remove_group(&dev->kobj, &topology_attr_group); -} - -static int topology_cpu_callback(struct notifier_block *nfb, - unsigned long action, void *hcpu) -{ - unsigned int cpu = (unsigned long)hcpu; - int rc = 0; - - switch (action) { - case CPU_UP_PREPARE: - case CPU_UP_PREPARE_FROZEN: - rc = topology_add_dev(cpu); - break; - case CPU_UP_CANCELED: - case CPU_UP_CANCELED_FROZEN: - case CPU_DEAD: - case CPU_DEAD_FROZEN: - topology_remove_dev(cpu); - break; - } - return notifier_from_errno(rc); + return 0; } static int topology_sysfs_init(void) { - int cpu; - int rc = 0; - - cpu_notifier_register_begin(); - - for_each_online_cpu(cpu) { - rc = topology_add_dev(cpu); - if (rc) - goto out; - } - __hotcpu_notifier(topology_cpu_callback, 0); - -out: - cpu_notifier_register_done(); - return rc; + return cpuhp_setup_state(CPUHP_TOPOLOGY_PREPARE, + "base/topology:prepare", topology_add_dev, + topology_remove_dev); } device_initcall(topology_sysfs_init); |