diff options
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/bus.c | 3 | ||||
-rw-r--r-- | drivers/base/core.c | 58 | ||||
-rw-r--r-- | drivers/base/cpu.c | 4 | ||||
-rw-r--r-- | drivers/base/devres.c | 35 | ||||
-rw-r--r-- | drivers/base/devtmpfs.c | 6 | ||||
-rw-r--r-- | drivers/base/dma-buf.c | 12 | ||||
-rw-r--r-- | drivers/base/driver.c | 2 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 4 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 153 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 107 |
10 files changed, 297 insertions, 87 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 26a06b8..2bcef65 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -21,8 +21,7 @@ #include "power/power.h" /* /sys/devices/system */ -/* FIXME: make static after drivers/base/sys.c is deleted */ -struct kset *system_kset; +static struct kset *system_kset; #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) diff --git a/drivers/base/core.c b/drivers/base/core.c index e28ce98..346be8b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -25,6 +25,7 @@ #include <linux/mutex.h> #include <linux/async.h> #include <linux/pm_runtime.h> +#include <linux/netdevice.h> #include "base.h" #include "power/power.h" @@ -65,7 +66,7 @@ static inline int device_is_not_partition(struct device *dev) * @dev: struct device to get the name of * * Will return the device's driver's name if it is bound to a device. If - * the device is not bound to a device, it will return the name of the bus + * the device is not bound to a driver, it will return the name of the bus * it is attached to. If it is not attached to a bus either, an empty * string will be returned. */ @@ -878,8 +879,8 @@ EXPORT_SYMBOL_GPL(dev_set_name); * to NULL prevents an entry from being created. class->dev_kobj must * be set (or cleared) before any devices are registered to the class * otherwise device_create_sys_dev_entry() and - * device_remove_sys_dev_entry() will disagree about the the presence - * of the link. + * device_remove_sys_dev_entry() will disagree about the presence of + * the link. */ static struct kobject *device_to_dev_kobj(struct device *dev) { @@ -1843,15 +1844,60 @@ void device_shutdown(void) */ #ifdef CONFIG_PRINTK - int __dev_printk(const char *level, const struct device *dev, struct va_format *vaf) { + char dict[128]; + size_t dictlen = 0; + const char *subsys; + if (!dev) return printk("%s(NULL device *): %pV", level, vaf); - return printk("%s%s %s: %pV", - level, dev_driver_string(dev), dev_name(dev), vaf); + if (dev->class) + subsys = dev->class->name; + else if (dev->bus) + subsys = dev->bus->name; + else + goto skip; + + dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, + "SUBSYSTEM=%s", subsys); + + /* + * Add device identifier DEVICE=: + * b12:8 block dev_t + * c127:3 char dev_t + * n8 netdev ifindex + * +sound:card0 subsystem:devname + */ + if (MAJOR(dev->devt)) { + char c; + + if (strcmp(subsys, "block") == 0) + c = 'b'; + else + c = 'c'; + dictlen++; + dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, + "DEVICE=%c%u:%u", + c, MAJOR(dev->devt), MINOR(dev->devt)); + } else if (strcmp(subsys, "net") == 0) { + struct net_device *net = to_net_dev(dev); + + dictlen++; + dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, + "DEVICE=n%u", net->ifindex); + } else { + dictlen++; + dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen, + "DEVICE=+%s:%s", subsys, dev_name(dev)); + } +skip: + return printk_emit(0, level[1] - '0', + dictlen ? dict : NULL, dictlen, + "%s %s: %pV", + dev_driver_string(dev), dev_name(dev), vaf); } EXPORT_SYMBOL(__dev_printk); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index adf937b..6345294 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -330,8 +330,4 @@ void __init cpu_dev_init(void) panic("Failed to register CPU subsystem"); cpu_dev_register_generic(); - -#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT) - sched_create_sysfs_power_savings_entries(cpu_subsys.dev_root); -#endif } diff --git a/drivers/base/devres.c b/drivers/base/devres.c index 524bf96..2360adb 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -309,6 +309,10 @@ EXPORT_SYMBOL_GPL(devres_remove); * which @match returns 1. If @match is NULL, it's considered to * match all. If found, the resource is removed atomically and freed. * + * Note that the release function for the resource will not be called, + * only the devres-allocated data will be freed. The caller becomes + * responsible for freeing any other data. + * * RETURNS: * 0 if devres is found and freed, -ENOENT if not found. */ @@ -326,6 +330,37 @@ int devres_destroy(struct device *dev, dr_release_t release, } EXPORT_SYMBOL_GPL(devres_destroy); + +/** + * devres_release - Find a device resource and destroy it, calling release + * @dev: Device to find resource from + * @release: Look for resources associated with this release function + * @match: Match function (optional) + * @match_data: Data for the match function + * + * Find the latest devres of @dev associated with @release and for + * which @match returns 1. If @match is NULL, it's considered to + * match all. If found, the resource is removed atomically, the + * release function called and the resource freed. + * + * RETURNS: + * 0 if devres is found and freed, -ENOENT if not found. + */ +int devres_release(struct device *dev, dr_release_t release, + dr_match_t match, void *match_data) +{ + void *res; + + res = devres_remove(dev, release, match, match_data); + if (unlikely(!res)) + return -ENOENT; + + (*release)(dev, res); + devres_free(res); + return 0; +} +EXPORT_SYMBOL_GPL(devres_release); + static int remove_nodes(struct device *dev, struct list_head *first, struct list_head *end, struct list_head *todo) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 8493536..765c3a2 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -7,9 +7,9 @@ * devtmpfs, a tmpfs-based filesystem is created. Every driver-core * device which requests a device node, will add a node in this * filesystem. - * By default, all devices are named after the the name of the - * device, owned by root and have a default mode of 0600. Subsystems - * can overwrite the default setting if needed. + * By default, all devices are named after the name of the device, + * owned by root and have a default mode of 0600. Subsystems can + * overwrite the default setting if needed. */ #include <linux/kernel.h> diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c index 07cbbc6..05c64c1 100644 --- a/drivers/base/dma-buf.c +++ b/drivers/base/dma-buf.c @@ -293,7 +293,7 @@ EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); * cpu in the kernel context. Calls begin_cpu_access to allow exporter-specific * preparations. Coherency is only guaranteed in the specified range for the * specified access direction. - * @dma_buf: [in] buffer to prepare cpu access for. + * @dmabuf: [in] buffer to prepare cpu access for. * @start: [in] start of range for cpu access. * @len: [in] length of range for cpu access. * @direction: [in] length of range for cpu access. @@ -320,7 +320,7 @@ EXPORT_SYMBOL_GPL(dma_buf_begin_cpu_access); * cpu in the kernel context. Calls end_cpu_access to allow exporter-specific * actions. Coherency is only guaranteed in the specified range for the * specified access direction. - * @dma_buf: [in] buffer to complete cpu access for. + * @dmabuf: [in] buffer to complete cpu access for. * @start: [in] start of range for cpu access. * @len: [in] length of range for cpu access. * @direction: [in] length of range for cpu access. @@ -340,7 +340,7 @@ EXPORT_SYMBOL_GPL(dma_buf_end_cpu_access); /** * dma_buf_kmap_atomic - Map a page of the buffer object into kernel address * space. The same restrictions as for kmap_atomic and friends apply. - * @dma_buf: [in] buffer to map page from. + * @dmabuf: [in] buffer to map page from. * @page_num: [in] page in PAGE_SIZE units to map. * * This call must always succeed, any necessary preparations that might fail @@ -356,7 +356,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kmap_atomic); /** * dma_buf_kunmap_atomic - Unmap a page obtained by dma_buf_kmap_atomic. - * @dma_buf: [in] buffer to unmap page from. + * @dmabuf: [in] buffer to unmap page from. * @page_num: [in] page in PAGE_SIZE units to unmap. * @vaddr: [in] kernel space pointer obtained from dma_buf_kmap_atomic. * @@ -375,7 +375,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kunmap_atomic); /** * dma_buf_kmap - Map a page of the buffer object into kernel address space. The * same restrictions as for kmap and friends apply. - * @dma_buf: [in] buffer to map page from. + * @dmabuf: [in] buffer to map page from. * @page_num: [in] page in PAGE_SIZE units to map. * * This call must always succeed, any necessary preparations that might fail @@ -391,7 +391,7 @@ EXPORT_SYMBOL_GPL(dma_buf_kmap); /** * dma_buf_kunmap - Unmap a page obtained by dma_buf_kmap. - * @dma_buf: [in] buffer to unmap page from. + * @dmabuf: [in] buffer to unmap page from. * @page_num: [in] page in PAGE_SIZE units to unmap. * @vaddr: [in] kernel space pointer obtained from dma_buf_kmap. * diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 3ec3896..207c27d 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -80,7 +80,7 @@ struct device *driver_find_device(struct device_driver *drv, struct klist_iter i; struct device *dev; - if (!drv) + if (!drv || !drv->p) return NULL; klist_iter_init_node(&drv->p->klist_devices, &i, diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index d92e9b1..b986b86 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -45,6 +45,7 @@ struct regmap { struct regmap_format format; /* Buffer format */ const struct regmap_bus *bus; void *bus_context; + const char *name; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; @@ -91,6 +92,9 @@ struct regmap { struct reg_default *patch; int patch_regs; + + /* if set, converts bulk rw to single rw */ + bool use_single_rw; }; struct regcache_ops { diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index fc69d29..4fac4b9 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -15,6 +15,7 @@ #include <linux/regmap.h> #include <linux/irq.h> #include <linux/interrupt.h> +#include <linux/irqdomain.h> #include <linux/slab.h> #include "internal.h" @@ -26,18 +27,20 @@ struct regmap_irq_chip_data { struct regmap_irq_chip *chip; int irq_base; + struct irq_domain *domain; - void *status_reg_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; + + unsigned int irq_reg_stride; }; static inline const struct regmap_irq *irq_to_regmap_irq(struct regmap_irq_chip_data *data, int irq) { - return &data->chip->irqs[irq - data->irq_base]; + return &data->chip->irqs[irq]; } static void regmap_irq_lock(struct irq_data *data) @@ -60,7 +63,8 @@ static void regmap_irq_sync_unlock(struct irq_data *data) */ for (i = 0; i < d->chip->num_regs; i++) { ret = regmap_update_bits(d->map, d->chip->mask_base + - (i * map->reg_stride), + (i * map->reg_stride * + d->irq_reg_stride), d->mask_buf_def[i], d->mask_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", @@ -74,7 +78,7 @@ static void regmap_irq_enable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; - const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); d->mask_buf[irq_data->reg_offset / map->reg_stride] &= ~irq_data->mask; } @@ -83,7 +87,7 @@ static void regmap_irq_disable(struct irq_data *data) { struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap *map = d->map; - const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->irq); + const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; } @@ -102,18 +106,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) struct regmap_irq_chip *chip = data->chip; struct regmap *map = data->map; int ret, i; - u8 *buf8 = data->status_reg_buf; - u16 *buf16 = data->status_reg_buf; - u32 *buf32 = data->status_reg_buf; bool handled = false; - ret = regmap_bulk_read(map, chip->status_base, data->status_reg_buf, - chip->num_regs); - if (ret != 0) { - dev_err(map->dev, "Failed to read IRQ status: %d\n", ret); - return IRQ_NONE; - } - /* * Ignore masked IRQs and ack if we need to; we ack early so * there is no race between handling and acknowleding the @@ -122,18 +116,13 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) * doing a write per register. */ for (i = 0; i < data->chip->num_regs; i++) { - switch (map->format.val_bytes) { - case 1: - data->status_buf[i] = buf8[i]; - break; - case 2: - data->status_buf[i] = buf16[i]; - break; - case 4: - data->status_buf[i] = buf32[i]; - break; - default: - BUG(); + ret = regmap_read(map, chip->status_base + (i * map->reg_stride + * data->irq_reg_stride), + &data->status_buf[i]); + + if (ret != 0) { + dev_err(map->dev, "Failed to read IRQ status: %d\n", + ret); return IRQ_NONE; } @@ -141,7 +130,8 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) if (data->status_buf[i] && chip->ack_base) { ret = regmap_write(map, chip->ack_base + - (i * map->reg_stride), + (i * map->reg_stride * + data->irq_reg_stride), data->status_buf[i]); if (ret != 0) dev_err(map->dev, "Failed to ack 0x%x: %d\n", @@ -153,7 +143,7 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) for (i = 0; i < chip->num_irqs; i++) { if (data->status_buf[chip->irqs[i].reg_offset / map->reg_stride] & chip->irqs[i].mask) { - handle_nested_irq(data->irq_base + i); + handle_nested_irq(irq_find_mapping(data->domain, i)); handled = true; } } @@ -164,6 +154,31 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) return IRQ_NONE; } +static int regmap_irq_map(struct irq_domain *h, unsigned int virq, + irq_hw_number_t hw) +{ + struct regmap_irq_chip_data *data = h->host_data; + + irq_set_chip_data(virq, data); + irq_set_chip_and_handler(virq, ®map_irq_chip, handle_edge_irq); + irq_set_nested_thread(virq, 1); + + /* ARM needs us to explicitly flag the IRQ as valid + * and will set them noprobe when we do so. */ +#ifdef CONFIG_ARM + set_irq_flags(virq, IRQF_VALID); +#else + irq_set_noprobe(virq); +#endif + + return 0; +} + +static struct irq_domain_ops regmap_domain_ops = { + .map = regmap_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + /** * regmap_add_irq_chip(): Use standard regmap IRQ controller handling * @@ -184,7 +199,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, struct regmap_irq_chip_data **data) { struct regmap_irq_chip_data *d; - int cur_irq, i; + int i; int ret = -ENOMEM; for (i = 0; i < chip->num_irqs; i++) { @@ -195,27 +210,26 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, return -EINVAL; } - irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); - if (irq_base < 0) { - dev_warn(map->dev, "Failed to allocate IRQs: %d\n", - irq_base); - return irq_base; + if (irq_base) { + irq_base = irq_alloc_descs(irq_base, 0, chip->num_irqs, 0); + if (irq_base < 0) { + dev_warn(map->dev, "Failed to allocate IRQs: %d\n", + irq_base); + return irq_base; + } } d = kzalloc(sizeof(*d), GFP_KERNEL); if (!d) return -ENOMEM; + *data = d; + d->status_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); if (!d->status_buf) goto err_alloc; - d->status_reg_buf = kzalloc(map->format.val_bytes * chip->num_regs, - GFP_KERNEL); - if (!d->status_reg_buf) - goto err_alloc; - d->mask_buf = kzalloc(sizeof(unsigned int) * chip->num_regs, GFP_KERNEL); if (!d->mask_buf) @@ -229,6 +243,12 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->map = map; d->chip = chip; d->irq_base = irq_base; + + if (chip->irq_reg_stride) + d->irq_reg_stride = chip->irq_reg_stride; + else + d->irq_reg_stride = 1; + mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) @@ -238,7 +258,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, /* Mask all the interrupts by default */ for (i = 0; i < chip->num_regs; i++) { d->mask_buf[i] = d->mask_buf_def[i]; - ret = regmap_write(map, chip->mask_base + (i * map->reg_stride), + ret = regmap_write(map, chip->mask_base + (i * map->reg_stride + * d->irq_reg_stride), d->mask_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", @@ -247,37 +268,34 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, } } - /* Register them with genirq */ - for (cur_irq = irq_base; - cur_irq < chip->num_irqs + irq_base; - cur_irq++) { - irq_set_chip_data(cur_irq, d); - irq_set_chip_and_handler(cur_irq, ®map_irq_chip, - handle_edge_irq); - irq_set_nested_thread(cur_irq, 1); - - /* ARM needs us to explicitly flag the IRQ as valid - * and will set them noprobe when we do so. */ -#ifdef CONFIG_ARM - set_irq_flags(cur_irq, IRQF_VALID); -#else - irq_set_noprobe(cur_irq); -#endif + if (irq_base) + d->domain = irq_domain_add_legacy(map->dev->of_node, + chip->num_irqs, irq_base, 0, + ®map_domain_ops, d); + else + d->domain = irq_domain_add_linear(map->dev->of_node, + chip->num_irqs, + ®map_domain_ops, d); + if (!d->domain) { + dev_err(map->dev, "Failed to create IRQ domain\n"); + ret = -ENOMEM; + goto err_alloc; } ret = request_threaded_irq(irq, NULL, regmap_irq_thread, irq_flags, chip->name, d); if (ret != 0) { dev_err(map->dev, "Failed to request IRQ %d: %d\n", irq, ret); - goto err_alloc; + goto err_domain; } return 0; +err_domain: + /* Should really dispose of the domain but... */ err_alloc: kfree(d->mask_buf_def); kfree(d->mask_buf); - kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); return ret; @@ -296,9 +314,9 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) return; free_irq(irq, d); + /* We should unmap the domain but... */ kfree(d->mask_buf_def); kfree(d->mask_buf); - kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); } @@ -313,6 +331,21 @@ EXPORT_SYMBOL_GPL(regmap_del_irq_chip); */ int regmap_irq_chip_get_base(struct regmap_irq_chip_data *data) { + WARN_ON(!data->irq_base); return data->irq_base; } EXPORT_SYMBOL_GPL(regmap_irq_chip_get_base); + +/** + * regmap_irq_get_virq(): Map an interrupt on a chip to a virtual IRQ + * + * Useful for drivers to request their own IRQs. + * + * @data: regmap_irq controller to operate on. + * @irq: index of the interrupt requested in the chip IRQs + */ +int regmap_irq_get_virq(struct regmap_irq_chip_data *data, int irq) +{ + return irq_create_mapping(data->domain, irq); +} +EXPORT_SYMBOL_GPL(regmap_irq_get_virq); diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index de9f796..0bcda48 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -199,6 +199,15 @@ static void regmap_unlock_spinlock(struct regmap *map) spin_unlock(&map->spinlock); } +static void dev_get_regmap_release(struct device *dev, void *res) +{ + /* + * We don't actually have anything to do here; the goal here + * is not to manage the regmap but to provide a simple way to + * get the regmap back given a struct device. + */ +} + /** * regmap_init(): Initialise register map * @@ -216,7 +225,7 @@ struct regmap *regmap_init(struct device *dev, void *bus_context, const struct regmap_config *config) { - struct regmap *map; + struct regmap *map, **m; int ret = -EINVAL; if (!bus || !config) @@ -247,6 +256,7 @@ struct regmap *regmap_init(struct device *dev, map->reg_stride = config->reg_stride; else map->reg_stride = 1; + map->use_single_rw = config->use_single_rw; map->dev = dev; map->bus = bus; map->bus_context = bus_context; @@ -256,6 +266,7 @@ struct regmap *regmap_init(struct device *dev, map->volatile_reg = config->volatile_reg; map->precious_reg = config->precious_reg; map->cache_type = config->cache_type; + map->name = config->name; if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; @@ -340,6 +351,9 @@ struct regmap *regmap_init(struct device *dev, break; } + if (map->format.format_write) + map->use_single_rw = true; + if (!map->format.format_write && !(map->format.format_reg && map->format.format_val)) goto err_map; @@ -356,8 +370,19 @@ struct regmap *regmap_init(struct device *dev, if (ret < 0) goto err_free_workbuf; + /* Add a devres resource for dev_get_regmap() */ + m = devres_alloc(dev_get_regmap_release, sizeof(*m), GFP_KERNEL); + if (!m) { + ret = -ENOMEM; + goto err_cache; + } + *m = map; + devres_add(dev, m); + return map; +err_cache: + regcache_exit(map); err_free_workbuf: kfree(map->work_buf); err_map: @@ -461,6 +486,44 @@ void regmap_exit(struct regmap *map) } EXPORT_SYMBOL_GPL(regmap_exit); +static int dev_get_regmap_match(struct device *dev, void *res, void *data) +{ + struct regmap **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + + /* If the user didn't specify a name match any */ + if (data) + return (*r)->name == data; + else + return 1; +} + +/** + * dev_get_regmap(): Obtain the regmap (if any) for a device + * + * @dev: Device to retrieve the map for + * @name: Optional name for the register map, usually NULL. + * + * Returns the regmap for the device if one is present, or NULL. If + * name is specified then it must match the name specified when + * registering the device, if it is NULL then the first regmap found + * will be used. Devices with multiple register maps are very rare, + * generic code should normally not need to specify a name. + */ +struct regmap *dev_get_regmap(struct device *dev, const char *name) +{ + struct regmap **r = devres_find(dev, dev_get_regmap_release, + dev_get_regmap_match, (void *)name); + + if (!r) + return NULL; + return *r; +} +EXPORT_SYMBOL_GPL(dev_get_regmap); + static int _regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len) { @@ -686,7 +749,22 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_val(wval + i); } - ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + /* + * Some devices does not support bulk write, for + * them we have a series of single write operations. + */ + if (map->use_single_rw) { + for (i = 0; i < val_count; i++) { + ret = regmap_raw_write(map, + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes); + if (ret != 0) + return ret; + } + } else { + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); + } if (val_bytes != 1) kfree(wval); @@ -749,6 +827,9 @@ static int _regmap_read(struct regmap *map, unsigned int reg, trace_regmap_reg_read(map->dev, reg, *val); } + if (ret == 0 && !map->cache_bypass) + regcache_write(map, reg, *val); + return ret; } @@ -855,9 +936,25 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, return -EINVAL; if (vol || map->cache_type == REGCACHE_NONE) { - ret = regmap_raw_read(map, reg, val, val_bytes * val_count); - if (ret != 0) - return ret; + /* + * Some devices does not support bulk read, for + * them we have a series of single read operations. + */ + if (map->use_single_rw) { + for (i = 0; i < val_count; i++) { + ret = regmap_raw_read(map, + reg + (i * map->reg_stride), + val + (i * val_bytes), + val_bytes); + if (ret != 0) + return ret; + } + } else { + ret = regmap_raw_read(map, reg, val, + val_bytes * val_count); + if (ret != 0) + return ret; + } for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_val(val + i); |