diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/Kconfig | 10 | ||||
-rw-r--r-- | drivers/base/attribute_container.c | 2 | ||||
-rw-r--r-- | drivers/base/core.c | 39 | ||||
-rw-r--r-- | drivers/base/cpu.c | 11 | ||||
-rw-r--r-- | drivers/base/dd.c | 33 | ||||
-rw-r--r-- | drivers/base/dma-coherent.c | 10 | ||||
-rw-r--r-- | drivers/base/dma-mapping.c | 11 | ||||
-rw-r--r-- | drivers/base/memory.c | 5 | ||||
-rw-r--r-- | drivers/base/pinctrl.c | 12 | ||||
-rw-r--r-- | drivers/base/platform.c | 18 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 5 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-debugfs.c | 16 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 64 | ||||
-rw-r--r-- | drivers/base/soc.c | 9 |
14 files changed, 173 insertions, 72 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 98504ec99c7d..fdf44cac08e6 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -212,6 +212,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" + 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. + config SYS_HYPERVISOR bool default n diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 2ba4cac080c5..95e3ef82f3b7 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -243,7 +243,7 @@ attribute_container_remove_device(struct device *dev, * @dev: The generic device to run the trigger for * @fn the function to execute for each classdev. * - * This funcion is for executing a trigger when you need to know both + * This function is for executing a trigger when you need to know both * the container and the classdev. If you only care about the * container, then use attribute_container_trigger() instead. */ diff --git a/drivers/base/core.c b/drivers/base/core.c index 70c5be5b03a7..ce057a568673 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -836,11 +836,29 @@ static struct kobject *get_device_parent(struct device *dev, return NULL; } +static inline bool live_in_glue_dir(struct kobject *kobj, + struct device *dev) +{ + if (!kobj || !dev->class || + kobj->kset != &dev->class->p->glue_dirs) + return false; + return true; +} + +static inline struct kobject *get_glue_dir(struct device *dev) +{ + return dev->kobj.parent; +} + +/* + * make sure cleaning up dir as the last step, we need to make + * sure .release handler of kobject is run with holding the + * global lock + */ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir) { /* see if we live in a "glue" directory */ - if (!glue_dir || !dev->class || - glue_dir->kset != &dev->class->p->glue_dirs) + if (!live_in_glue_dir(glue_dir, dev)) return; mutex_lock(&gdp_mutex); @@ -848,11 +866,6 @@ static void cleanup_glue_dir(struct device *dev, struct kobject *glue_dir) mutex_unlock(&gdp_mutex); } -static void cleanup_device_parent(struct device *dev) -{ - cleanup_glue_dir(dev, dev->kobj.parent); -} - static int device_add_class_symlinks(struct device *dev) { struct device_node *of_node = dev_of_node(dev); @@ -1028,6 +1041,7 @@ int device_add(struct device *dev) struct kobject *kobj; struct class_interface *class_intf; int error = -EINVAL; + struct kobject *glue_dir = NULL; dev = get_device(dev); if (!dev) @@ -1072,8 +1086,10 @@ int device_add(struct device *dev) /* first, register with generic layer. */ /* we require the name to be set before, and pass NULL */ error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); - if (error) + if (error) { + glue_dir = get_glue_dir(dev); goto Error; + } /* notify platform of device entry */ if (platform_notify) @@ -1154,9 +1170,10 @@ done: device_remove_file(dev, &dev_attr_uevent); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); + glue_dir = get_glue_dir(dev); kobject_del(&dev->kobj); Error: - cleanup_device_parent(dev); + cleanup_glue_dir(dev, glue_dir); put_device(parent); name_error: kfree(dev->p); @@ -1232,6 +1249,7 @@ EXPORT_SYMBOL_GPL(put_device); void device_del(struct device *dev) { struct device *parent = dev->parent; + struct kobject *glue_dir = NULL; struct class_interface *class_intf; /* Notify clients of device removal. This call must come @@ -1277,8 +1295,9 @@ void device_del(struct device *dev) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_REMOVED_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_REMOVE); - cleanup_device_parent(dev); + glue_dir = get_glue_dir(dev); kobject_del(&dev->kobj); + cleanup_glue_dir(dev, glue_dir); put_device(parent); } EXPORT_SYMBOL_GPL(device_del); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 691eeea2f19a..4c28e1a09786 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -371,12 +371,13 @@ int register_cpu(struct cpu *cpu, int num) if (cpu->hotpluggable) cpu->dev.groups = hotplugable_cpu_attr_groups; error = device_register(&cpu->dev); - if (!error) - per_cpu(cpu_sys_devices, num) = &cpu->dev; - if (!error) - register_cpu_under_node(num, cpu_to_node(num)); + if (error) + return error; - return error; + per_cpu(cpu_sys_devices, num) = &cpu->dev; + register_cpu_under_node(num, cpu_to_node(num)); + + return 0; } struct device *get_cpu_device(unsigned cpu) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 16688f50729c..d22a7260f42b 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -51,7 +51,6 @@ static DEFINE_MUTEX(deferred_probe_mutex); static LIST_HEAD(deferred_probe_pending_list); static LIST_HEAD(deferred_probe_active_list); -static struct workqueue_struct *deferred_wq; static atomic_t deferred_trigger_count = ATOMIC_INIT(0); /* @@ -175,7 +174,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. */ - queue_work(deferred_wq, &deferred_probe_work); + schedule_work(&deferred_probe_work); } /** @@ -211,14 +210,10 @@ void device_unblock_probing(void) */ static int deferred_probe_initcall(void) { - deferred_wq = create_singlethread_workqueue("deferwq"); - if (WARN_ON(!deferred_wq)) - return -ENOMEM; - driver_deferred_probe_enable = true; driver_deferred_probe_trigger(); /* Sort as many dependencies as possible before exiting initcalls */ - flush_workqueue(deferred_wq); + flush_work(&deferred_probe_work); return 0; } late_initcall(deferred_probe_initcall); @@ -329,6 +324,7 @@ 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); if (defer_all_probes) { /* @@ -346,6 +342,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) drv->bus->name, __func__, drv->name, dev_name(dev)); WARN_ON(!list_empty(&dev->devres_head)); +re_probe: dev->driver = drv; /* If using pinctrl, bind pins now before probing */ @@ -383,6 +380,25 @@ static int really_probe(struct device *dev, struct device_driver *drv) goto probe_failed; } + if (test_remove) { + test_remove = false; + + if (dev->bus && dev->bus->remove) + dev->bus->remove(dev); + else if (drv->remove) + drv->remove(dev); + + devres_release_all(dev); + driver_sysfs_remove(dev); + dev->driver = NULL; + dev_set_drvdata(dev, NULL); + if (dev->pm_domain && dev->pm_domain->dismiss) + dev->pm_domain->dismiss(dev); + pm_runtime_reinit(dev); + + goto re_probe; + } + pinctrl_init_done(dev); if (dev->pm_domain && dev->pm_domain->sync) @@ -460,8 +476,7 @@ int driver_probe_done(void) void wait_for_device_probe(void) { /* wait for the deferred probe workqueue to finish */ - if (driver_deferred_probe_enable) - flush_workqueue(deferred_wq); + flush_work(&deferred_probe_work); /* wait for the known devices to complete their probing */ wait_event(probe_waitqueue, atomic_read(&probe_count) == 0); diff --git a/drivers/base/dma-coherent.c b/drivers/base/dma-coherent.c index bdf28f7dd5e8..640a7e63c453 100644 --- a/drivers/base/dma-coherent.c +++ b/drivers/base/dma-coherent.c @@ -165,6 +165,7 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, int order = get_order(size); unsigned long flags; int pageno; + int dma_memory_map; if (!dev) return 0; @@ -187,11 +188,12 @@ int dma_alloc_from_coherent(struct device *dev, ssize_t size, */ *dma_handle = mem->device_base + (pageno << PAGE_SHIFT); *ret = mem->virt_base + (pageno << PAGE_SHIFT); - if (mem->flags & DMA_MEMORY_MAP) + dma_memory_map = (mem->flags & DMA_MEMORY_MAP); + spin_unlock_irqrestore(&mem->spinlock, flags); + if (dma_memory_map) memset(*ret, 0, size); else memset_io(*ret, 0, size); - spin_unlock_irqrestore(&mem->spinlock, flags); return 1; @@ -261,8 +263,8 @@ int dma_mmap_from_coherent(struct device *dev, struct vm_area_struct *vma, (mem->virt_base + (mem->size << PAGE_SHIFT))) { unsigned long off = vma->vm_pgoff; int start = (vaddr - mem->virt_base) >> PAGE_SHIFT; - int user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; - int count = size >> PAGE_SHIFT; + int user_count = vma_pages(vma); + int count = PAGE_ALIGN(size) >> PAGE_SHIFT; *ret = -ENXIO; if (off < count && user_count <= count - off) { diff --git a/drivers/base/dma-mapping.c b/drivers/base/dma-mapping.c index d799662f19eb..8f8b68c80986 100644 --- a/drivers/base/dma-mapping.c +++ b/drivers/base/dma-mapping.c @@ -198,10 +198,13 @@ int dmam_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr, rc = dma_declare_coherent_memory(dev, phys_addr, device_addr, size, flags); - if (rc == 0) + if (rc) { devres_add(dev, res); - else + rc = 0; + } else { devres_free(res); + rc = -ENOMEM; + } return rc; } @@ -247,7 +250,7 @@ int dma_common_mmap(struct device *dev, struct vm_area_struct *vma, { int ret = -ENXIO; #if defined(CONFIG_MMU) && !defined(CONFIG_ARCH_NO_COHERENT_DMA_MMAP) - unsigned long user_count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + unsigned long user_count = vma_pages(vma); unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT; unsigned long pfn = page_to_pfn(virt_to_page(cpu_addr)); unsigned long off = vma->vm_pgoff; @@ -334,7 +337,7 @@ void dma_common_free_remap(void *cpu_addr, size_t size, unsigned long vm_flags) return; } - unmap_kernel_range((unsigned long)cpu_addr, size); + unmap_kernel_range((unsigned long)cpu_addr, PAGE_ALIGN(size)); vunmap(cpu_addr); } #endif diff --git a/drivers/base/memory.c b/drivers/base/memory.c index dc75de9059cd..62c63c0c5c22 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -361,8 +361,11 @@ store_mem_state(struct device *dev, err: unlock_device_hotplug(); - if (ret) + if (ret < 0) return ret; + if (ret) + return -EINVAL; + return count; } diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c index 076297592754..5917b4b5fb99 100644 --- a/drivers/base/pinctrl.c +++ b/drivers/base/pinctrl.c @@ -91,9 +91,13 @@ cleanup_alloc: devm_kfree(dev, dev->pins); dev->pins = NULL; - /* Only return deferrals */ - if (ret != -EPROBE_DEFER) - ret = 0; + /* Return deferrals */ + if (ret == -EPROBE_DEFER) + return ret; + /* Return serious errors */ + if (ret == -EINVAL) + return ret; + /* We ignore errors like -ENOENT meaning no pinctrl state */ - return ret; + return 0; } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 6482d47deb50..c4af00385502 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -97,7 +97,7 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) int ret; ret = of_irq_get(dev->dev.of_node, num); - if (ret >= 0 || ret == -EPROBE_DEFER) + if (ret > 0 || ret == -EPROBE_DEFER) return ret; } @@ -108,9 +108,14 @@ int platform_get_irq(struct platform_device *dev, unsigned int num) * IORESOURCE_BITS correspond 1-to-1 to the IRQF_TRIGGER* * settings. */ - if (r && r->flags & IORESOURCE_BITS) - irqd_set_trigger_type(irq_get_irq_data(r->start), - r->flags & IORESOURCE_BITS); + if (r && r->flags & IORESOURCE_BITS) { + struct irq_data *irqd; + + irqd = irq_get_irq_data(r->start); + if (!irqd) + return -ENXIO; + irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); + } return r ? r->start : -ENXIO; #endif @@ -175,7 +180,7 @@ int platform_get_irq_byname(struct platform_device *dev, const char *name) int ret; ret = of_irq_get_byname(dev->dev.of_node, name); - if (ret >= 0 || ret == -EPROBE_DEFER) + if (ret > 0 || ret == -EPROBE_DEFER) return ret; } @@ -434,6 +439,7 @@ void platform_device_del(struct platform_device *pdev) int i; if (pdev) { + device_remove_properties(&pdev->dev); device_del(&pdev->dev); if (pdev->id_auto) { @@ -446,8 +452,6 @@ void platform_device_del(struct platform_device *pdev) if (r->parent) release_resource(r); } - - device_remove_properties(&pdev->dev); } } EXPORT_SYMBOL_GPL(platform_device_del); diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index a0380338946a..2a4435d76028 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -105,8 +105,8 @@ struct regmap { bool defer_caching; - u8 read_flag_mask; - u8 write_flag_mask; + unsigned long read_flag_mask; + unsigned long write_flag_mask; /* number of bits to (left) shift the reg value when formatting*/ int reg_shift; @@ -173,6 +173,7 @@ struct regcache_ops { int (*drop)(struct regmap *map, unsigned int min, unsigned int max); }; +bool regmap_cached(struct regmap *map, unsigned int reg); bool regmap_writeable(struct regmap *map, unsigned int reg); bool regmap_readable(struct regmap *map, unsigned int reg); bool regmap_volatile(struct regmap *map, unsigned int reg); diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 1ee3d40861c7..36ce3511c733 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -77,6 +77,17 @@ static void regmap_debugfs_free_dump_cache(struct regmap *map) } } +static bool regmap_printable(struct regmap *map, unsigned int reg) +{ + if (regmap_precious(map, reg)) + return false; + + if (!regmap_readable(map, reg) && !regmap_cached(map, reg)) + return false; + + return true; +} + /* * Work out where the start offset maps into register numbers, bearing * in mind that we suppress hidden registers. @@ -105,8 +116,7 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, if (list_empty(&map->debugfs_off_cache)) { for (; i <= map->max_register; i += map->reg_stride) { /* Skip unprinted registers, closing off cache entry */ - if (!regmap_readable(map, i) || - regmap_precious(map, i)) { + if (!regmap_printable(map, i)) { if (c) { c->max = p - 1; c->max_reg = i - map->reg_stride; @@ -204,7 +214,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); for (i = start_reg; i <= to; i += map->reg_stride) { - if (!regmap_readable(map, i)) + if (!regmap_readable(map, i) && !regmap_cached(map, i)) continue; if (regmap_precious(map, i)) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e964d068874d..ae63bb0875ea 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -93,6 +93,29 @@ bool regmap_writeable(struct regmap *map, unsigned int reg) return true; } +bool regmap_cached(struct regmap *map, unsigned int reg) +{ + int ret; + unsigned int val; + + if (map->cache == REGCACHE_NONE) + return false; + + if (!map->cache_ops) + return false; + + if (map->max_register && reg > map->max_register) + return false; + + map->lock(map->lock_arg); + ret = regcache_read(map, reg, &val); + map->unlock(map->lock_arg); + if (ret) + return false; + + return true; +} + bool regmap_readable(struct regmap *map, unsigned int reg) { if (!map->reg_read) @@ -749,6 +772,9 @@ struct regmap *__regmap_init(struct device *dev, case REGMAP_ENDIAN_BIG: map->format.format_reg = regmap_format_16_be; break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_reg = regmap_format_16_le; + break; case REGMAP_ENDIAN_NATIVE: map->format.format_reg = regmap_format_16_native; break; @@ -768,6 +794,9 @@ struct regmap *__regmap_init(struct device *dev, case REGMAP_ENDIAN_BIG: map->format.format_reg = regmap_format_32_be; break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_reg = regmap_format_32_le; + break; case REGMAP_ENDIAN_NATIVE: map->format.format_reg = regmap_format_32_native; break; @@ -782,6 +811,9 @@ struct regmap *__regmap_init(struct device *dev, case REGMAP_ENDIAN_BIG: map->format.format_reg = regmap_format_64_be; break; + case REGMAP_ENDIAN_LITTLE: + map->format.format_reg = regmap_format_64_le; + break; case REGMAP_ENDIAN_NATIVE: map->format.format_reg = regmap_format_64_native; break; @@ -1296,12 +1328,26 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, return 0; } +static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes, + unsigned long mask) +{ + u8 *buf; + int i; + + if (!mask || !map->work_buf) + return; + + buf = map->work_buf; + + for (i = 0; i < max_bytes; i++) + buf[i] |= (mask >> (8 * i)) & 0xff; +} + int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { struct regmap_range_node *range; unsigned long flags; - u8 *u8 = map->work_buf; void *work_val = map->work_buf + map->format.reg_bytes + map->format.pad_bytes; void *buf; @@ -1370,8 +1416,8 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, } map->format.format_reg(map->work_buf, reg, map->reg_shift); - - u8[0] |= map->write_flag_mask; + regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, + map->write_flag_mask); /* * Essentially all I/O mechanisms will be faster with a single @@ -2251,7 +2297,6 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { struct regmap_range_node *range; - u8 *u8 = map->work_buf; int ret; WARN_ON(!map->bus); @@ -2268,15 +2313,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, } map->format.format_reg(map->work_buf, reg, map->reg_shift); - - /* - * Some buses or devices flag reads by setting the high bits in the - * register address; since it's always the high bits for all - * current formats we can do this here rather than in - * formatting. This may break if we get interesting formats. - */ - u8[0] |= map->read_flag_mask; - + regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, + map->read_flag_mask); trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes); ret = map->bus->read(map->bus_context, map->work_buf, diff --git a/drivers/base/soc.c b/drivers/base/soc.c index 75b98aad6faf..b63f23e6ad61 100644 --- a/drivers/base/soc.c +++ b/drivers/base/soc.c @@ -6,7 +6,6 @@ */ #include <linux/sysfs.h> -#include <linux/module.h> #include <linux/init.h> #include <linux/stat.h> #include <linux/slab.h> @@ -160,11 +159,3 @@ static int __init soc_bus_register(void) return bus_register(&soc_bus_type); } core_initcall(soc_bus_register); - -static void __exit soc_bus_unregister(void) -{ - ida_destroy(&soc_ida); - - bus_unregister(&soc_bus_type); -} -module_exit(soc_bus_unregister); |