From 55ac85e942c6783e728964861df36fc80e8ced93 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 19 Dec 2012 19:42:28 +0530 Subject: regmap: irq: enable wake support by default regmap-irq framework is used vastly by mfd drivers and some of devices like TPS65910, TPS80036 do not support the wake base register to enable wake. Currently wake in regmap-irq only supported if client driver passes the wake base register. As the regmap-irq is mostly used by mfd devices and it is require to have wake support from these devices in most of use cases, enabling wake support by default in regmap-irq. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 5972ad9..9129493 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -129,16 +129,15 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on) struct regmap *map = d->map; const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq); - if (!d->chip->wake_base) - return -EINVAL; - if (on) { - d->wake_buf[irq_data->reg_offset / map->reg_stride] - &= ~irq_data->mask; + if (d->wake_buf) + d->wake_buf[irq_data->reg_offset / map->reg_stride] + &= ~irq_data->mask; d->wake_count++; } else { - d->wake_buf[irq_data->reg_offset / map->reg_stride] - |= irq_data->mask; + if (d->wake_buf) + d->wake_buf[irq_data->reg_offset / map->reg_stride] + |= irq_data->mask; d->wake_count--; } @@ -316,11 +315,6 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->irq_chip = regmap_irq_chip; d->irq_chip.name = chip->name; - if (!chip->wake_base) { - d->irq_chip.irq_set_wake = NULL; - d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND | - IRQCHIP_SKIP_SET_WAKE; - } d->irq = irq; d->map = map; d->chip = chip; -- cgit v0.10.2 From 2ac902ce17f9dfa0d4d1f0818be147b5d2515fb7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 19 Dec 2012 14:51:55 +0000 Subject: regmap: flat: Add flat cache type While for I2C and SPI devices the overhead of using rbtree for devices with only one block of registers is negligible the same isn't always going to be true for MMIO devices where the I/O costs are very much lower. Cater for these devices by adding a simple flat array type for them where the lookups are simple array accesses, taking us right back to the original ASoC cache implementation. Thanks to Magnus Damm for the discussion which prompted this. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index 5e75d1b..cf12998 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_REGMAP) += regmap.o regcache.o -obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o +obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 401d191..e22bb80 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -177,5 +177,6 @@ int regcache_lookup_reg(struct regmap *map, unsigned int reg); extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; +extern struct regcache_ops regcache_flat_ops; #endif diff --git a/drivers/base/regmap/regcache-flat.c b/drivers/base/regmap/regcache-flat.c new file mode 100644 index 0000000..d9762e4 --- /dev/null +++ b/drivers/base/regmap/regcache-flat.c @@ -0,0 +1,72 @@ +/* + * Register cache access API - flat caching support + * + * Copyright 2012 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include + +#include "internal.h" + +static int regcache_flat_init(struct regmap *map) +{ + int i; + unsigned int *cache; + + map->cache = kzalloc(sizeof(unsigned int) * (map->max_register + 1), + GFP_KERNEL); + if (!map->cache) + return -ENOMEM; + + cache = map->cache; + + for (i = 0; i < map->num_reg_defaults; i++) + cache[map->reg_defaults[i].reg] = map->reg_defaults[i].def; + + return 0; +} + +static int regcache_flat_exit(struct regmap *map) +{ + kfree(map->cache); + map->cache = NULL; + + return 0; +} + +static int regcache_flat_read(struct regmap *map, + unsigned int reg, unsigned int *value) +{ + unsigned int *cache = map->cache; + + *value = cache[reg]; + + return 0; +} + +static int regcache_flat_write(struct regmap *map, unsigned int reg, + unsigned int value) +{ + unsigned int *cache = map->cache; + + cache[reg] = value; + + return 0; +} + +struct regcache_ops regcache_flat_ops = { + .type = REGCACHE_FLAT, + .name = "flat", + .init = regcache_flat_init, + .exit = regcache_flat_exit, + .read = regcache_flat_read, + .write = regcache_flat_write, +}; diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c index 835883b..e69ff3e 100644 --- a/drivers/base/regmap/regcache.c +++ b/drivers/base/regmap/regcache.c @@ -22,6 +22,7 @@ static const struct regcache_ops *cache_types[] = { ®cache_rbtree_ops, ®cache_lzo_ops, + ®cache_flat_ops, }; static int regcache_hw_init(struct regmap *map) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b7e95bf..390d879 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -28,7 +28,8 @@ struct regmap_range_cfg; enum regcache_type { REGCACHE_NONE, REGCACHE_RBTREE, - REGCACHE_COMPRESSED + REGCACHE_COMPRESSED, + REGCACHE_FLAT, }; /** -- cgit v0.10.2 From bbae92ca49f77898277576e3377b09e1391a3271 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jan 2013 13:58:33 +0000 Subject: regmap: irq: Factor register read out of the IRQ parsing loop In preparation for adding back support for block reads. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 9129493..329be98 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -170,13 +170,6 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } - /* - * Ignore masked IRQs and ack if we need to; we ack early so - * there is no race between handling and acknowleding the - * interrupt. We assume that typically few of the interrupts - * will fire simultaneously so don't worry about overhead from - * doing a write per register. - */ for (i = 0; i < data->chip->num_regs; i++) { ret = regmap_read(map, chip->status_base + (i * map->reg_stride * data->irq_reg_stride), @@ -189,7 +182,16 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) pm_runtime_put(map->dev); 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 + * interrupt. We assume that typically few of the interrupts + * will fire simultaneously so don't worry about overhead from + * doing a write per register. + */ + for (i = 0; i < data->chip->num_regs; i++) { data->status_buf[i] &= ~data->mask_buf[i]; if (data->status_buf[i] && chip->ack_base) { -- cgit v0.10.2 From a7440eaa90cf2659920b9b28973cc1a13a2b331f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 3 Jan 2013 14:27:15 +0000 Subject: regmap: irq: Use a bulk read for interrupt status where possible If the interrupt status registers are a single block of registers and the chip supports bulk reads then do a single bulk read rather than pay the extra I/O cost. This restores the original behaviour which was lost when support for register striding was added. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index 329be98..aaf599a 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -34,6 +34,7 @@ struct regmap_irq_chip_data { int irq; int wake_count; + void *status_reg_buf; unsigned int *status_buf; unsigned int *mask_buf; unsigned int *mask_buf_def; @@ -170,18 +171,60 @@ static irqreturn_t regmap_irq_thread(int irq, void *d) } } - for (i = 0; i < data->chip->num_regs; i++) { - ret = regmap_read(map, chip->status_base + (i * map->reg_stride - * data->irq_reg_stride), - &data->status_buf[i]); + /* + * Read in the statuses, using a single bulk read if possible + * in order to reduce the I/O overheads. + */ + if (!map->use_single_rw && map->reg_stride == 1 && + data->irq_reg_stride == 1) { + u8 *buf8 = data->status_reg_buf; + u16 *buf16 = data->status_reg_buf; + u32 *buf32 = data->status_reg_buf; + BUG_ON(!data->status_reg_buf); + + 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); - if (chip->runtime_pm) - pm_runtime_put(map->dev); + ret); return IRQ_NONE; } + + 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(); + return IRQ_NONE; + } + } + + } else { + for (i = 0; i < data->chip->num_regs; i++) { + ret = regmap_read(map, chip->status_base + + (i * map->reg_stride + * data->irq_reg_stride), + &data->status_buf[i]); + + if (ret != 0) { + dev_err(map->dev, + "Failed to read IRQ status: %d\n", + ret); + if (chip->runtime_pm) + pm_runtime_put(map->dev); + return IRQ_NONE; + } + } } /* @@ -327,6 +370,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, else d->irq_reg_stride = 1; + if (!map->use_single_rw && map->reg_stride == 1 && + d->irq_reg_stride == 1) { + d->status_reg_buf = kmalloc(map->format.val_bytes * + chip->num_regs, GFP_KERNEL); + if (!d->status_reg_buf) + goto err_alloc; + } + mutex_init(&d->lock); for (i = 0; i < chip->num_irqs; i++) @@ -397,6 +448,7 @@ err_alloc: kfree(d->mask_buf_def); kfree(d->mask_buf); kfree(d->status_buf); + kfree(d->status_reg_buf); kfree(d); return ret; } @@ -418,6 +470,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d) kfree(d->wake_buf); kfree(d->mask_buf_def); kfree(d->mask_buf); + kfree(d->status_reg_buf); kfree(d->status_buf); kfree(d); } -- cgit v0.10.2 From 33be49324f7f3e9dff10d3d07a5c78ee82f8d54e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 16:32:54 +0000 Subject: regmap: irq: Fix sync of wake statuses to hardware This wasn't implemented but happened to work on test systems due to lack of wake mask inversion support. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index aaf599a..be55799 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -88,6 +88,17 @@ static void regmap_irq_sync_unlock(struct irq_data *data) if (ret != 0) dev_err(d->map->dev, "Failed to sync masks in %x\n", reg); + + reg = d->chip->wake_base + + (i * map->reg_stride * d->irq_reg_stride); + if (d->wake_buf) { + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], d->wake_buf[i]); + if (ret != 0) + dev_err(d->map->dev, + "Failed to sync wakes in %x: %d\n", + reg, ret); + } } if (d->chip->runtime_pm) -- cgit v0.10.2 From 9442490a02867088bcd5ed6231fa3b35a733c2b8 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 16:35:07 +0000 Subject: regmap: irq: Support wake IRQ mask inversion Support devices which have an enable rather than mask register for wake sources. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c index be55799..4706c63 100644 --- a/drivers/base/regmap/regmap-irq.c +++ b/drivers/base/regmap/regmap-irq.c @@ -92,8 +92,14 @@ static void regmap_irq_sync_unlock(struct irq_data *data) reg = d->chip->wake_base + (i * map->reg_stride * d->irq_reg_stride); if (d->wake_buf) { - ret = regmap_update_bits(d->map, reg, - d->mask_buf_def[i], d->wake_buf[i]); + if (d->chip->wake_invert) + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], + ~d->wake_buf[i]); + else + ret = regmap_update_bits(d->map, reg, + d->mask_buf_def[i], + d->wake_buf[i]); if (ret != 0) dev_err(d->map->dev, "Failed to sync wakes in %x: %d\n", @@ -419,8 +425,15 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, d->wake_buf[i] = d->mask_buf_def[i]; reg = chip->wake_base + (i * map->reg_stride * d->irq_reg_stride); - ret = regmap_update_bits(map, reg, d->wake_buf[i], - d->wake_buf[i]); + + if (chip->wake_invert) + ret = regmap_update_bits(map, reg, + d->mask_buf_def[i], + 0); + else + ret = regmap_update_bits(map, reg, + d->mask_buf_def[i], + d->wake_buf[i]); if (ret != 0) { dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", reg, ret); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b7e95bf..8c0b50d 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -381,6 +381,7 @@ struct regmap_irq_chip { unsigned int wake_base; unsigned int irq_reg_stride; unsigned int mask_invert; + unsigned int wake_invert; bool runtime_pm; int num_regs; -- cgit v0.10.2 From cc2fb86d5c48dd901ad1f5c4dd95c77211b2c227 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 17:12:25 +0000 Subject: mfd: wm5102: Mark wakes as inverted Signed-off-by: Mark Brown diff --git a/drivers/mfd/wm5102-tables.c b/drivers/mfd/wm5102-tables.c index 088872a..1e90d8e 100644 --- a/drivers/mfd/wm5102-tables.c +++ b/drivers/mfd/wm5102-tables.c @@ -96,6 +96,7 @@ const struct regmap_irq_chip wm5102_aod = { .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, .ack_base = ARIZONA_AOD_IRQ1, .wake_base = ARIZONA_WAKE_CONTROL, + .wake_invert = 1, .num_regs = 1, .irqs = wm5102_aod_irqs, .num_irqs = ARRAY_SIZE(wm5102_aod_irqs), -- cgit v0.10.2 From 378e8ee2f842ccf5a14516741119468fc55b17f0 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 4 Jan 2013 17:12:14 +0000 Subject: mfd: wm5110: Mark wakes as inverted Signed-off-by: Mark Brown diff --git a/drivers/mfd/wm5110-tables.c b/drivers/mfd/wm5110-tables.c index adda6b1..c415998 100644 --- a/drivers/mfd/wm5110-tables.c +++ b/drivers/mfd/wm5110-tables.c @@ -255,6 +255,7 @@ const struct regmap_irq_chip wm5110_aod = { .mask_base = ARIZONA_AOD_IRQ_MASK_IRQ1, .ack_base = ARIZONA_AOD_IRQ1, .wake_base = ARIZONA_WAKE_CONTROL, + .wake_invert = 1, .num_regs = 1, .irqs = wm5110_aod_irqs, .num_irqs = ARRAY_SIZE(wm5110_aod_irqs), -- cgit v0.10.2 From 237019e7e356abb1bad591fa5edab68224793143 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 10 Jan 2013 17:06:10 +0100 Subject: regmap: Add support for 24 bit wide register addresses Since regmap already has support for formatting 24 bit wide values, so adding support for 24 bit wide registers is pretty much straight forward. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 42d5cb0..26af93a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -500,6 +500,12 @@ struct regmap *regmap_init(struct device *dev, } break; + case 24: + if (reg_endian != REGMAP_ENDIAN_BIG) + goto err_map; + map->format.format_reg = regmap_format_24; + break; + case 32: switch (reg_endian) { case REGMAP_ENDIAN_BIG: -- cgit v0.10.2 From ad278406b3b8b8e454af23b63df3c3d63f6aee94 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sat, 12 Jan 2013 12:54:12 -0800 Subject: regmap: Add provisions to have user-defined read operation This commit is a preparatory commit to provide "no-bus" configuration option for regmap API. It adds necessary plumbing needed to have the ability to provide user define register read function. Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 401d191..471eb90 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -74,6 +74,8 @@ struct regmap { const struct regmap_access_table *volatile_table; const struct regmap_access_table *precious_table; + int (*reg_read)(void *context, unsigned int reg, unsigned int *val); + u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 42d5cb0..ceaefcf 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -34,6 +34,9 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); +static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val); + bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) @@ -430,6 +433,8 @@ struct regmap *regmap_init(struct device *dev, map->read_flag_mask = bus->read_flag_mask; } + map->reg_read = _regmap_bus_read; + reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) reg_endian = bus->reg_format_endian_default; @@ -1202,10 +1207,27 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, return ret; } +static int _regmap_bus_read(void *context, unsigned int reg, + unsigned int *val) +{ + int ret; + struct regmap *map = context; + + if (!map->format.parse_val) + return -EINVAL; + + ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); + if (ret == 0) + *val = map->format.parse_val(map->work_buf); + + return ret; +} + static int _regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + BUG_ON(!map->reg_read); if (!map->cache_bypass) { ret = regcache_read(map, reg, val); @@ -1213,26 +1235,21 @@ static int _regmap_read(struct regmap *map, unsigned int reg, return 0; } - if (!map->format.parse_val) - return -EINVAL; - if (map->cache_only) return -EBUSY; - ret = _regmap_raw_read(map, reg, map->work_buf, map->format.val_bytes); + ret = map->reg_read(map, reg, val); if (ret == 0) { - *val = map->format.parse_val(map->work_buf); - #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) dev_info(map->dev, "%x => %x\n", reg, *val); #endif trace_regmap_reg_read(map->dev, reg, *val); - } - if (ret == 0 && !map->cache_bypass) - regcache_write(map, reg, *val); + if (!map->cache_bypass) + regcache_write(map, reg, *val); + } return ret; } -- cgit v0.10.2 From 07c320dc31d757b8cb59c64dab320215c929bf02 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sat, 12 Jan 2013 12:54:13 -0800 Subject: regmap: Add provisions to have user-defined write operation This commit is a preparatory commit to provide "no-bus" configuration option for regmap API. It adds necessary plumbing needed to have the ability to provide user define register write function. Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 471eb90..51f0574 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -75,6 +75,7 @@ struct regmap { const struct regmap_access_table *precious_table; int (*reg_read)(void *context, unsigned int reg, unsigned int *val); + int (*reg_write)(void *context, unsigned int reg, unsigned int val); u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ceaefcf..6845a07 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -36,6 +36,10 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg, static int _regmap_bus_read(void *context, unsigned int reg, unsigned int *val); +static int _regmap_bus_formatted_write(void *context, unsigned int reg, + unsigned int val); +static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val); bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, @@ -580,6 +584,11 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } + if (map->format.format_write) + map->reg_write = _regmap_bus_formatted_write; + else if (map->format.format_val) + map->reg_write = _regmap_bus_raw_write; + map->range_tree = RB_ROOT; for (i = 0; i < config->num_ranges; i++) { const struct regmap_range_cfg *range_cfg = &config->ranges[i]; @@ -986,12 +995,54 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, return ret; } +static int _regmap_bus_formatted_write(void *context, unsigned int reg, + unsigned int val) +{ + int ret; + struct regmap_range_node *range; + struct regmap *map = context; + + BUG_ON(!map->format.format_write); + + range = _regmap_range_lookup(map, reg); + if (range) { + ret = _regmap_select_page(map, ®, range, 1); + if (ret != 0) + return ret; + } + + map->format.format_write(map, reg, val); + + trace_regmap_hw_write_start(map->dev, reg, 1); + + ret = map->bus->write(map->bus_context, map->work_buf, + map->format.buf_size); + + trace_regmap_hw_write_done(map->dev, reg, 1); + + return ret; +} + +static int _regmap_bus_raw_write(void *context, unsigned int reg, + unsigned int val) +{ + struct regmap *map = context; + + BUG_ON(!map->format.format_val); + + map->format.format_val(map->work_buf + map->format.reg_bytes + + map->format.pad_bytes, val, 0); + return _regmap_raw_write(map, reg, + map->work_buf + + map->format.reg_bytes + + map->format.pad_bytes, + map->format.val_bytes); +} + int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { - struct regmap_range_node *range; int ret; - BUG_ON(!map->format.format_write && !map->format.format_val); if (!map->cache_bypass && map->format.format_write) { ret = regcache_write(map, reg, val); @@ -1010,33 +1061,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); - if (map->format.format_write) { - range = _regmap_range_lookup(map, reg); - if (range) { - ret = _regmap_select_page(map, ®, range, 1); - if (ret != 0) - return ret; - } - - map->format.format_write(map, reg, val); - - trace_regmap_hw_write_start(map->dev, reg, 1); - - ret = map->bus->write(map->bus_context, map->work_buf, - map->format.buf_size); - - trace_regmap_hw_write_done(map->dev, reg, 1); - - return ret; - } else { - map->format.format_val(map->work_buf + map->format.reg_bytes - + map->format.pad_bytes, val, 0); - return _regmap_raw_write(map, reg, - map->work_buf + - map->format.reg_bytes + - map->format.pad_bytes, - map->format.val_bytes); - } + return map->reg_write(map, reg, val); } /** -- cgit v0.10.2 From a3471469bcba61f8f18ca4c267b0cdd90a61e035 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 26 Jan 2013 11:45:35 +0000 Subject: regmap: regmap: avoid spurious warning in regmap_read_debugfs Gcc warns about the case where regmap_read_debugfs tries to walk an empty map->debugfs_off_cache list, which would results in uninitialized variable getting returned, if we hadn't checked the same condition just before that. After an originally suggested inferior patch from Arnd Bergmann, this is the solution that Russell King came up with, sidestepping the problem by merging the error case for an empty list with the normal path. Without this patch, building mxs_defconfig results in: drivers/base/regmap/regmap-debugfs.c: In function 'regmap_read_debugfs': drivers/base/regmap/regmap-debugfs.c:147:9: : warning: 'ret' may be used uninitialized in this function [-Wmaybe-uninitialized] Reported-by: Vincent Stehle Cc: Mark Brown Cc: Greg Kroah-Hartman Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index d9a6c94..faa93cc 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -128,10 +128,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, * allocate and we should never be in this code if there are * no registers at all. */ - if (list_empty(&map->debugfs_off_cache)) { - WARN_ON(list_empty(&map->debugfs_off_cache)); - return base; - } + WARN_ON(list_empty(&map->debugfs_off_cache)); + ret = base; /* Find the relevant block */ list_for_each_entry(c, &map->debugfs_off_cache, list) { -- cgit v0.10.2 From d2a5884a64161b524cc6749ee11b95d252e497f3 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Sun, 27 Jan 2013 10:49:05 -0800 Subject: regmap: Add "no-bus" option for regmap API This commit adds provision for "no-bus" usage of the regmap API. In this configuration user can provide API with two callbacks 'reg_read' and 'reg_write' which are to be called when reads and writes to one of device's registers is performed. This is useful for devices that expose registers but whose register access sequence does not fit the 'bus' abstraction. Signed-off-by: Andrey Smirnov Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 51f0574..b55fde5 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -77,6 +77,8 @@ struct regmap { int (*reg_read)(void *context, unsigned int reg, unsigned int *val); int (*reg_write)(void *context, unsigned int reg, unsigned int val); + bool defer_caching; + u8 read_flag_mask; u8 write_flag_mask; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6845a07..9592030 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -379,7 +379,7 @@ struct regmap *regmap_init(struct device *dev, enum regmap_endian reg_endian, val_endian; int i, j; - if (!bus || !config) + if (!config) goto err; map = kzalloc(sizeof(*map), GFP_KERNEL); @@ -393,7 +393,8 @@ struct regmap *regmap_init(struct device *dev, map->unlock = config->unlock; map->lock_arg = config->lock_arg; } else { - if (bus->fast_io) { + if ((bus && bus->fast_io) || + config->fast_io) { spin_lock_init(&map->spinlock); map->lock = regmap_lock_spinlock; map->unlock = regmap_unlock_spinlock; @@ -433,11 +434,19 @@ struct regmap *regmap_init(struct device *dev, if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; - } else { + } else if (bus) { map->read_flag_mask = bus->read_flag_mask; } - map->reg_read = _regmap_bus_read; + if (!bus) { + map->reg_read = config->reg_read; + map->reg_write = config->reg_write; + + map->defer_caching = false; + goto skip_format_initialization; + } else { + map->reg_read = _regmap_bus_read; + } reg_endian = config->reg_format_endian; if (reg_endian == REGMAP_ENDIAN_DEFAULT) @@ -584,10 +593,15 @@ struct regmap *regmap_init(struct device *dev, goto err_map; } - if (map->format.format_write) + if (map->format.format_write) { + map->defer_caching = false; map->reg_write = _regmap_bus_formatted_write; - else if (map->format.format_val) + } else if (map->format.format_val) { + map->defer_caching = true; map->reg_write = _regmap_bus_raw_write; + } + +skip_format_initialization: map->range_tree = RB_ROOT; for (i = 0; i < config->num_ranges; i++) { @@ -790,7 +804,7 @@ void regmap_exit(struct regmap *map) regcache_exit(map); regmap_debugfs_exit(map); regmap_range_exit(map); - if (map->bus->free_context) + if (map->bus && map->bus->free_context) map->bus->free_context(map->bus_context); kfree(map->work_buf); kfree(map); @@ -893,6 +907,8 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, size_t len; int i; + BUG_ON(!map->bus); + /* Check for unwritable registers before we start */ if (map->writeable_reg) for (i = 0; i < val_len / map->format.val_bytes; i++) @@ -1002,7 +1018,7 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, struct regmap_range_node *range; struct regmap *map = context; - BUG_ON(!map->format.format_write); + BUG_ON(!map->bus || !map->format.format_write); range = _regmap_range_lookup(map, reg); if (range) { @@ -1028,7 +1044,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, { struct regmap *map = context; - BUG_ON(!map->format.format_val); + BUG_ON(!map->bus || !map->format.format_val); map->format.format_val(map->work_buf + map->format.reg_bytes + map->format.pad_bytes, val, 0); @@ -1039,12 +1055,18 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, map->format.val_bytes); } +static inline void *_regmap_map_get_context(struct regmap *map) +{ + return (map->bus) ? map : map->bus_context; +} + int _regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { int ret; + void *context = _regmap_map_get_context(map); - if (!map->cache_bypass && map->format.format_write) { + if (!map->cache_bypass && !map->defer_caching) { ret = regcache_write(map, reg, val); if (ret != 0) return ret; @@ -1061,7 +1083,7 @@ int _regmap_write(struct regmap *map, unsigned int reg, trace_regmap_reg_write(map->dev, reg, val); - return map->reg_write(map, reg, val); + return map->reg_write(context, reg, val); } /** @@ -1112,6 +1134,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, { int ret; + if (!map->bus) + return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; if (reg % map->reg_stride) @@ -1148,6 +1172,8 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_bytes = map->format.val_bytes; void *wval; + if (!map->bus) + return -EINVAL; if (!map->format.parse_val) return -EINVAL; if (reg % map->reg_stride) @@ -1201,6 +1227,8 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, u8 *u8 = map->work_buf; int ret; + BUG_ON(!map->bus); + range = _regmap_range_lookup(map, reg); if (range) { ret = _regmap_select_page(map, ®, range, @@ -1252,6 +1280,8 @@ static int _regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { int ret; + void *context = _regmap_map_get_context(map); + BUG_ON(!map->reg_read); if (!map->cache_bypass) { @@ -1263,7 +1293,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg, if (map->cache_only) return -EBUSY; - ret = map->reg_read(map, reg, val); + ret = map->reg_read(context, reg, val); if (ret == 0) { #ifdef LOG_DEVICE if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0) @@ -1325,6 +1355,8 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int v; int ret, i; + if (!map->bus) + return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; if (reg % map->reg_stride) @@ -1376,6 +1408,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, size_t val_bytes = map->format.val_bytes; bool vol = regmap_volatile_range(map, reg, val_count); + if (!map->bus) + return -EINVAL; if (!map->format.parse_val) return -EINVAL; if (reg % map->reg_stride) diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b7e95bf..28dde94 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -127,7 +127,18 @@ typedef void (*regmap_unlock)(void *); * @lock_arg: this field is passed as the only argument of lock/unlock * functions (ignored in case regular lock/unlock functions * are not overridden). - * + * @reg_read: Optional callback that if filled will be used to perform + * all the reads from the registers. Should only be provided for + * devices whos read operation cannot be represented as a simple read + * operation on a bus such as SPI, I2C, etc. Most of the devices do + * not need this. + * @reg_write: Same as above for writing. + * @fast_io: Register IO is fast. Use a spinlock instead of a mutex + * to perform locking. This field is ignored if custom lock/unlock + * functions are used (see fields lock/unlock of struct regmap_config). + * This field is a duplicate of a similar file in + * 'struct regmap_bus' and serves exact same purpose. + * Use it only for "no-bus" cases. * @max_register: Optional, specifies the maximum valid register index. * @wr_table: Optional, points to a struct regmap_access_table specifying * valid ranges for write access. @@ -177,6 +188,11 @@ struct regmap_config { regmap_unlock unlock; void *lock_arg; + int (*reg_read)(void *context, unsigned int reg, unsigned int *val); + int (*reg_write)(void *context, unsigned int reg, unsigned int val); + + bool fast_io; + unsigned int max_register; const struct regmap_access_table *wr_table; const struct regmap_access_table *rd_table; -- cgit v0.10.2 From 0d509f2b112b21411712f0bf789b372987967e49 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Sun, 27 Jan 2013 22:07:38 +0800 Subject: regmap: Add asynchronous I/O support Some use cases like firmware download can transfer a lot of data in quick succession. With high speed buses these use cases can benefit from having multiple transfers scheduled at once since this allows the bus to minimise the delay between transfers. Support this by adding regmap_raw_write_async(), allowing raw transfers to be scheduled, and regmap_async_complete() to wait for them to finish. Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 51f0574..2025186 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -16,6 +16,7 @@ #include #include #include +#include struct regmap; struct regcache_ops; @@ -39,6 +40,13 @@ struct regmap_format { unsigned int (*parse_val)(void *buf); }; +struct regmap_async { + struct list_head list; + struct work_struct cleanup; + struct regmap *map; + void *work_buf; +}; + struct regmap { struct mutex mutex; spinlock_t spinlock; @@ -53,6 +61,11 @@ struct regmap { void *bus_context; const char *name; + spinlock_t async_lock; + wait_queue_head_t async_waitq; + struct list_head async_list; + int async_ret; + #ifdef CONFIG_DEBUG_FS struct dentry *debugfs; const char *debugfs_name; @@ -178,6 +191,8 @@ bool regcache_set_val(void *base, unsigned int idx, unsigned int val, unsigned int word_size); int regcache_lookup_reg(struct regmap *map, unsigned int reg); +void regmap_async_complete_cb(struct regmap_async *async, int ret); + extern struct regcache_ops regcache_rbtree_ops; extern struct regcache_ops regcache_lzo_ops; diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6845a07..e57b7bc 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -41,6 +41,15 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg, static int _regmap_bus_raw_write(void *context, unsigned int reg, unsigned int val); +static void async_cleanup(struct work_struct *work) +{ + struct regmap_async *async = container_of(work, struct regmap_async, + cleanup); + + kfree(async->work_buf); + kfree(async); +} + bool regmap_reg_in_ranges(unsigned int reg, const struct regmap_range *ranges, unsigned int nranges) @@ -430,6 +439,10 @@ struct regmap *regmap_init(struct device *dev, map->cache_type = config->cache_type; map->name = config->name; + spin_lock_init(&map->async_lock); + INIT_LIST_HEAD(&map->async_list); + init_waitqueue_head(&map->async_waitq); + if (config->read_flag_mask || config->write_flag_mask) { map->read_flag_mask = config->read_flag_mask; map->write_flag_mask = config->write_flag_mask; @@ -884,10 +897,13 @@ static int _regmap_select_page(struct regmap *map, unsigned int *reg, } static int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len) + const void *val, size_t val_len, bool async) { 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; int ret = -ENOTSUPP; size_t len; @@ -932,7 +948,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, dev_dbg(map->dev, "Writing window %d/%zu\n", win_residue, val_len / map->format.val_bytes); ret = _regmap_raw_write(map, reg, val, win_residue * - map->format.val_bytes); + map->format.val_bytes, async); if (ret != 0) return ret; @@ -955,6 +971,50 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, u8[0] |= map->write_flag_mask; + if (async && map->bus->async_write) { + struct regmap_async *async = map->bus->async_alloc(); + if (!async) + return -ENOMEM; + + async->work_buf = kzalloc(map->format.buf_size, + GFP_KERNEL | GFP_DMA); + if (!async->work_buf) { + kfree(async); + return -ENOMEM; + } + + INIT_WORK(&async->cleanup, async_cleanup); + async->map = map; + + /* If the caller supplied the value we can use it safely. */ + memcpy(async->work_buf, map->work_buf, map->format.pad_bytes + + map->format.reg_bytes + map->format.val_bytes); + if (val == work_val) + val = async->work_buf + map->format.pad_bytes + + map->format.reg_bytes; + + spin_lock_irqsave(&map->async_lock, flags); + list_add_tail(&async->list, &map->async_list); + spin_unlock_irqrestore(&map->async_lock, flags); + + ret = map->bus->async_write(map->bus_context, async->work_buf, + map->format.reg_bytes + + map->format.pad_bytes, + val, val_len, async); + + if (ret != 0) { + dev_err(map->dev, "Failed to schedule write: %d\n", + ret); + + spin_lock_irqsave(&map->async_lock, flags); + list_del(&async->list); + spin_unlock_irqrestore(&map->async_lock, flags); + + kfree(async->work_buf); + kfree(async); + } + } + trace_regmap_hw_write_start(map->dev, reg, val_len / map->format.val_bytes); @@ -962,8 +1022,7 @@ static int _regmap_raw_write(struct regmap *map, unsigned int reg, * send the work_buf directly, otherwise try to do a gather * write. */ - if (val == (map->work_buf + map->format.pad_bytes + - map->format.reg_bytes)) + if (val == work_val) ret = map->bus->write(map->bus_context, map->work_buf, map->format.reg_bytes + map->format.pad_bytes + @@ -1036,7 +1095,7 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, map->work_buf + map->format.reg_bytes + map->format.pad_bytes, - map->format.val_bytes); + map->format.val_bytes, false); } int _regmap_write(struct regmap *map, unsigned int reg, @@ -1119,7 +1178,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, map->lock(map->lock_arg); - ret = _regmap_raw_write(map, reg, val, val_len); + ret = _regmap_raw_write(map, reg, val, val_len, false); map->unlock(map->lock_arg); @@ -1175,14 +1234,15 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, 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); + 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); + ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count, + false); } if (val_bytes != 1) @@ -1194,6 +1254,48 @@ out: } EXPORT_SYMBOL_GPL(regmap_bulk_write); +/** + * regmap_raw_write_async(): Write raw values to one or more registers + * asynchronously + * + * @map: Register map to write to + * @reg: Initial register to write to + * @val: Block of data to be written, laid out for direct transmission to the + * device. Must be valid until regmap_async_complete() is called. + * @val_len: Length of data pointed to by val. + * + * This function is intended to be used for things like firmware + * download where a large block of data needs to be transferred to the + * device. No formatting will be done on the data provided. + * + * If supported by the underlying bus the write will be scheduled + * asynchronously, helping maximise I/O speed on higher speed buses + * like SPI. regmap_async_complete() can be called to ensure that all + * asynchrnous writes have been completed. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_raw_write_async(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + int ret; + + if (val_len % map->format.val_bytes) + return -EINVAL; + if (reg % map->reg_stride) + return -EINVAL; + + map->lock(map->lock_arg); + + ret = _regmap_raw_write(map, reg, val, val_len, true); + + map->unlock(map->lock_arg); + + return ret; +} +EXPORT_SYMBOL_GPL(regmap_raw_write_async); + static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val, unsigned int val_len) { @@ -1492,6 +1594,66 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, } EXPORT_SYMBOL_GPL(regmap_update_bits_check); +void regmap_async_complete_cb(struct regmap_async *async, int ret) +{ + struct regmap *map = async->map; + bool wake; + + spin_lock(&map->async_lock); + + list_del(&async->list); + wake = list_empty(&map->async_list); + + if (ret != 0) + map->async_ret = ret; + + spin_unlock(&map->async_lock); + + schedule_work(&async->cleanup); + + if (wake) + wake_up(&map->async_waitq); +} + +static int regmap_async_is_done(struct regmap *map) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&map->async_lock, flags); + ret = list_empty(&map->async_list); + spin_unlock_irqrestore(&map->async_lock, flags); + + return ret; +} + +/** + * regmap_async_complete: Ensure all asynchronous I/O has completed. + * + * @map: Map to operate on. + * + * Blocks until any pending asynchronous I/O has completed. Returns + * an error code for any failed I/O operations. + */ +int regmap_async_complete(struct regmap *map) +{ + unsigned long flags; + int ret; + + /* Nothing to do with no async support */ + if (!map->bus->async_write) + return 0; + + wait_event(map->async_waitq, regmap_async_is_done(map)); + + spin_lock_irqsave(&map->async_lock, flags); + ret = map->async_ret; + map->async_ret = 0; + spin_unlock_irqrestore(&map->async_lock, flags); + + return ret; +} + /** * regmap_register_patch: Register and apply register updates to be applied * on device initialistion diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b7e95bf..f9b7fbe 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -235,14 +235,21 @@ struct regmap_range_cfg { unsigned int window_len; }; +struct regmap_async; + typedef int (*regmap_hw_write)(void *context, const void *data, size_t count); typedef int (*regmap_hw_gather_write)(void *context, const void *reg, size_t reg_len, const void *val, size_t val_len); +typedef int (*regmap_hw_async_write)(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len, + struct regmap_async *async); typedef int (*regmap_hw_read)(void *context, const void *reg_buf, size_t reg_size, void *val_buf, size_t val_size); +typedef struct regmap_async *(*regmap_hw_async_alloc)(void); typedef void (*regmap_hw_free_context)(void *context); /** @@ -255,8 +262,11 @@ typedef void (*regmap_hw_free_context)(void *context); * @write: Write operation. * @gather_write: Write operation with split register/value, return -ENOTSUPP * if not implemented on a given device. + * @async_write: Write operation which completes asynchronously, optional and + * must serialise with respect to non-async I/O. * @read: Read operation. Data is returned in the buffer used to transmit * data. + * @async_alloc: Allocate a regmap_async() structure. * @read_flag_mask: Mask to be set in the top byte of the register when doing * a read. * @reg_format_endian_default: Default endianness for formatted register @@ -265,13 +275,16 @@ typedef void (*regmap_hw_free_context)(void *context); * @val_format_endian_default: Default endianness for formatted register * values. Used when the regmap_config specifies DEFAULT. If this is * DEFAULT, BIG is assumed. + * @async_size: Size of struct used for async work. */ struct regmap_bus { bool fast_io; regmap_hw_write write; regmap_hw_gather_write gather_write; + regmap_hw_async_write async_write; regmap_hw_read read; regmap_hw_free_context free_context; + regmap_hw_async_alloc async_alloc; u8 read_flag_mask; enum regmap_endian reg_format_endian_default; enum regmap_endian val_format_endian_default; @@ -310,6 +323,8 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, const void *val, size_t val_len); int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count); +int regmap_raw_write_async(struct regmap *map, unsigned int reg, + const void *val, size_t val_len); int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, size_t val_len); @@ -321,6 +336,7 @@ int regmap_update_bits_check(struct regmap *map, unsigned int reg, unsigned int mask, unsigned int val, bool *change); int regmap_get_val_bytes(struct regmap *map); +int regmap_async_complete(struct regmap *map); int regcache_sync(struct regmap *map); int regcache_sync_region(struct regmap *map, unsigned int min, @@ -422,6 +438,13 @@ static inline int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; } +static inline int regmap_raw_write_async(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + WARN_ONCE(1, "regmap API is disabled"); + return -EINVAL; +} + static inline int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, size_t val_count) { @@ -500,6 +523,11 @@ static inline void regcache_mark_dirty(struct regmap *map) WARN_ONCE(1, "regmap API is disabled"); } +static inline void regmap_async_complete(struct regmap *map) +{ + WARN_ONCE(1, "regmap API is disabled"); +} + static inline int regmap_register_patch(struct regmap *map, const struct reg_default *regs, int num_regs) -- cgit v0.10.2 From e0356dfe98f227cd18314bdbcf93a9b464026ce7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 29 Jan 2013 12:14:12 +0800 Subject: regmap: spi: Support asynchronous I/O for SPI Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index ffa46a9..913274b 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -15,6 +15,21 @@ #include #include +#include "internal.h" + +struct regmap_async_spi { + struct regmap_async core; + struct spi_message m; + struct spi_transfer t[2]; +}; + +static void regmap_spi_complete(void *data) +{ + struct regmap_async_spi *async = data; + + regmap_async_complete_cb(&async->core, async->m.status); +} + static int regmap_spi_write(void *context, const void *data, size_t count) { struct device *dev = context; @@ -40,6 +55,41 @@ static int regmap_spi_gather_write(void *context, return spi_sync(spi, &m); } +static int regmap_spi_async_write(void *context, + const void *reg, size_t reg_len, + const void *val, size_t val_len, + struct regmap_async *a) +{ + struct regmap_async_spi *async = container_of(a, + struct regmap_async_spi, + core); + struct device *dev = context; + struct spi_device *spi = to_spi_device(dev); + + async->t[0].tx_buf = reg; + async->t[0].len = reg_len; + async->t[1].tx_buf = val; + async->t[1].len = val_len; + + spi_message_init(&async->m); + spi_message_add_tail(&async->t[0], &async->m); + spi_message_add_tail(&async->t[1], &async->m); + + async->m.complete = regmap_spi_complete; + async->m.context = async; + + return spi_async(spi, &async->m); +} + +static struct regmap_async *regmap_spi_async_alloc(void) +{ + struct regmap_async_spi *async_spi; + + async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL); + + return &async_spi->core; +} + static int regmap_spi_read(void *context, const void *reg, size_t reg_size, void *val, size_t val_size) @@ -53,6 +103,8 @@ static int regmap_spi_read(void *context, static struct regmap_bus regmap_spi = { .write = regmap_spi_write, .gather_write = regmap_spi_gather_write, + .async_write = regmap_spi_async_write, + .async_alloc = regmap_spi_async_alloc, .read = regmap_spi_read, .read_flag_mask = 0x80, }; -- cgit v0.10.2 From 30b2a553742747d951861a6f582fa90dd9220124 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Sat, 2 Feb 2013 22:50:14 -0700 Subject: regmap: include linux/sched.h to fix build This fixes: drivers/base/regmap/regmap.c: In function 'regmap_async_complete_cb': drivers/base/regmap/regmap.c:1656:3: error: 'TASK_NORMAL' undeclared (first use in this function) drivers/base/regmap/regmap.c:1656:3: note: each undeclared identifier is reported only once for each function it appears in drivers/base/regmap/regmap.c: In function 'regmap_async_complete': drivers/base/regmap/regmap.c:1688:2: error: 'TASK_UNINTERRUPTIBLE' undeclared (first use in this function) drivers/base/regmap/regmap.c:1688:2: error: implicit declaration of function 'schedule' An alternative might be to adjust linux/wait.h to include linux/sched.h, but since that hasn't been done before, I assume we're consciously avoiding doing that. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e57b7bc..0bd5d0a 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -16,6 +16,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include -- cgit v0.10.2 From f804fb562b0d9f4a8546fa2808d14e80aea8ff26 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sun, 3 Feb 2013 00:14:13 +0800 Subject: regmap: Export regmap_async_complete_cb This fixes below build error when CONFIG_REGMAP=y && CONFIG_REGMAP_SPI=m ERROR: "regmap_async_complete_cb" [drivers/base/regmap/regmap-spi.ko] undefined! make[1]: *** [__modpost] Error 1 make: *** [modules] Error 2 Signed-off-by: Axel Lin Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0bd5d0a..6d1e756 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1615,6 +1615,7 @@ void regmap_async_complete_cb(struct regmap_async *async, int ret) if (wake) wake_up(&map->async_waitq); } +EXPORT_SYMBOL_GPL(regmap_async_complete_cb); static int regmap_async_is_done(struct regmap *map) { -- cgit v0.10.2 From f88948eff9a6160ed74e3ee4b12f41f5beeff115 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Feb 2013 13:53:26 +0000 Subject: regmap: Export regmap_async_complete() Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 6d1e756..296df36 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1655,6 +1655,7 @@ int regmap_async_complete(struct regmap *map) return ret; } +EXPORT_SYMBOL_GPL(regmap_async_complete); /** * regmap_register_patch: Register and apply register updates to be applied -- cgit v0.10.2 From 95601d65a1aa0902f838a2919e11ee6311efe371 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 5 Feb 2013 14:14:32 +0000 Subject: regmap: spi: Handle allocation failures gracefully Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c index 913274b..4c506bd 100644 --- a/drivers/base/regmap/regmap-spi.c +++ b/drivers/base/regmap/regmap-spi.c @@ -86,6 +86,8 @@ static struct regmap_async *regmap_spi_async_alloc(void) struct regmap_async_spi *async_spi; async_spi = kzalloc(sizeof(*async_spi), GFP_KERNEL); + if (!async_spi) + return NULL; return &async_spi->core; } -- cgit v0.10.2 From f3eb83994cb4c172ce5028b5ae7ea08462cc3f1d Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Thu, 7 Feb 2013 10:51:56 +0000 Subject: regmap: debugfs: Fix reading in register field units At the moment, if the length of the register field format is N bytes, we can only get anything meaningful back to userspace by providing a buffer that is N + 2 bytes large. Fix this so we that we only need to provide a buffer of N bytes. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index faa93cc..0e94fd3 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -185,7 +185,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, /* If we're in the region the user is trying to read */ if (p >= *ppos) { /* ...but not beyond it */ - if (buf_pos + 1 + map->debugfs_tot_len >= count) + if (buf_pos + map->debugfs_tot_len > count) break; /* Format the register */ -- cgit v0.10.2 From c2c1ee66016a45477f58f0fd30907b1e959ca76b Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 8 Feb 2013 12:47:14 +0000 Subject: regmap: debugfs: Add a `max_reg' member in struct regmap_debugfs_off_cache We are keeping track of the maximum register as well, this will make things easier for us in sharing this code with the code implementing the register ranges functionality. It also simplifies a bit the calculations when looking for the relevant block:offset from within the cache. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h index 401d191..26b8ffd 100644 --- a/drivers/base/regmap/internal.h +++ b/drivers/base/regmap/internal.h @@ -25,6 +25,7 @@ struct regmap_debugfs_off_cache { off_t min; off_t max; unsigned int base_reg; + unsigned int max_reg; }; struct regmap_format { diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 0e94fd3..3fade1c 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -81,6 +81,8 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, struct regmap_debugfs_off_cache *c = NULL; loff_t p = 0; unsigned int i, ret; + unsigned int fpos_offset; + unsigned int reg_offset; /* * If we don't have a cache build one so we don't have to do a @@ -93,6 +95,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, regmap_precious(map, i)) { if (c) { c->max = p - 1; + fpos_offset = c->max - c->min; + reg_offset = fpos_offset / map->debugfs_tot_len; + c->max_reg = c->base_reg + reg_offset; list_add_tail(&c->list, &map->debugfs_off_cache); c = NULL; @@ -119,6 +124,9 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, /* Close the last entry off if we didn't scan beyond it */ if (c) { c->max = p - 1; + fpos_offset = c->max - c->min; + reg_offset = fpos_offset / map->debugfs_tot_len; + c->max_reg = c->base_reg + reg_offset; list_add_tail(&c->list, &map->debugfs_off_cache); } -- cgit v0.10.2 From cf57d6071f0610c99856c006ac06eb98a151f485 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Fri, 8 Feb 2013 12:47:20 +0000 Subject: regmap: debugfs: Optimize seeking within blocks of registers Optimize this so that we can better guess where to start scanning from. We know the length of the register field format, therefore given the file pointer position align to the nearest register field and scan from there onwards. We round down in this calculation and we let the rest of the code figure out where to start scanning from. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 3fade1c..246f459 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -139,15 +139,17 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, WARN_ON(list_empty(&map->debugfs_off_cache)); ret = base; - /* Find the relevant block */ + /* Find the relevant block:offset */ list_for_each_entry(c, &map->debugfs_off_cache, list) { if (from >= c->min && from <= c->max) { - *pos = c->min; - return c->base_reg; + fpos_offset = from - c->min; + reg_offset = fpos_offset / map->debugfs_tot_len; + *pos = c->min + (reg_offset * map->debugfs_tot_len); + return c->base_reg + reg_offset; } - *pos = c->min; - ret = c->base_reg; + *pos = c->max; + ret = c->max_reg; } return ret; -- cgit v0.10.2 From 4dd7c5531d3b046574da39096b1a66c738aca102 Mon Sep 17 00:00:00 2001 From: Dimitris Papastamos Date: Mon, 11 Feb 2013 10:50:59 +0000 Subject: regmap: debugfs: Factor out debugfs_tot_len calc into a function In preparation to support the regmap debugfs ranges functionality factor this code out to a separate function. We'll need to ensure that the value has been correctly calculated from two separate places in the code. Signed-off-by: Dimitris Papastamos Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 246f459..78d5f20 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -155,6 +155,19 @@ static unsigned int regmap_debugfs_get_dump_start(struct regmap *map, return ret; } +static inline void regmap_calc_tot_len(struct regmap *map, + void *buf, size_t count) +{ + /* Calculate the length of a fixed format */ + if (!map->debugfs_tot_len) { + map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, + buf, count); + map->debugfs_val_len = 2 * map->format.val_bytes; + map->debugfs_tot_len = map->debugfs_reg_len + + map->debugfs_val_len + 3; /* : \n */ + } +} + static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, unsigned int to, char __user *user_buf, size_t count, loff_t *ppos) @@ -173,14 +186,7 @@ static ssize_t regmap_read_debugfs(struct regmap *map, unsigned int from, if (!buf) return -ENOMEM; - /* Calculate the length of a fixed format */ - if (!map->debugfs_tot_len) { - map->debugfs_reg_len = regmap_calc_reg_len(map->max_register, - buf, count); - map->debugfs_val_len = 2 * map->format.val_bytes; - map->debugfs_tot_len = map->debugfs_reg_len + - map->debugfs_val_len + 3; /* : \n */ - } + regmap_calc_tot_len(map, buf, count); /* Work out which register we're starting at */ start_reg = regmap_debugfs_get_dump_start(map, from, *ppos, &p); -- cgit v0.10.2 From 878ec67b3ac528a2b6d44055f049cdbb9cfda30c Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Thu, 14 Feb 2013 17:39:08 +0100 Subject: regmap: mmio: add register clock support Some mmio devices have a dedicated interface clock that needs to be enabled to access their registers. This patch optionally enables a clock before accessing registers in the regmap_bus callbacks. I added (devm_)regmap_init_mmio_clk variants of the init functions that have an added clk_id string parameter. This is passed to clk_get to request the clock from the clk framework. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index f05fc74..98745dd 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -26,6 +27,7 @@ struct regmap_mmio_context { void __iomem *regs; unsigned val_bytes; + struct clk *clk; }; static int regmap_mmio_gather_write(void *context, @@ -34,9 +36,16 @@ static int regmap_mmio_gather_write(void *context, { struct regmap_mmio_context *ctx = context; u32 offset; + int ret; BUG_ON(reg_size != 4); + if (ctx->clk) { + ret = clk_enable(ctx->clk); + if (ret < 0) + return ret; + } + offset = *(u32 *)reg; while (val_size) { @@ -64,6 +73,9 @@ static int regmap_mmio_gather_write(void *context, offset += ctx->val_bytes; } + if (ctx->clk) + clk_disable(ctx->clk); + return 0; } @@ -80,9 +92,16 @@ static int regmap_mmio_read(void *context, { struct regmap_mmio_context *ctx = context; u32 offset; + int ret; BUG_ON(reg_size != 4); + if (ctx->clk) { + ret = clk_enable(ctx->clk); + if (ret < 0) + return ret; + } + offset = *(u32 *)reg; while (val_size) { @@ -110,11 +129,20 @@ static int regmap_mmio_read(void *context, offset += ctx->val_bytes; } + if (ctx->clk) + clk_disable(ctx->clk); + return 0; } static void regmap_mmio_free_context(void *context) { + struct regmap_mmio_context *ctx = context; + + if (ctx->clk) { + clk_unprepare(ctx->clk); + clk_put(ctx->clk); + } kfree(context); } @@ -128,11 +156,14 @@ static struct regmap_bus regmap_mmio = { .val_format_endian_default = REGMAP_ENDIAN_NATIVE, }; -static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, +static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev, + const char *clk_id, + void __iomem *regs, const struct regmap_config *config) { struct regmap_mmio_context *ctx; int min_stride; + int ret; if (config->reg_bits != 32) return ERR_PTR(-EINVAL); @@ -179,37 +210,59 @@ static struct regmap_mmio_context *regmap_mmio_gen_context(void __iomem *regs, ctx->regs = regs; ctx->val_bytes = config->val_bits / 8; + if (clk_id == NULL) + return ctx; + + ctx->clk = clk_get(dev, clk_id); + if (IS_ERR(ctx->clk)) { + ret = PTR_ERR(ctx->clk); + goto err_free; + } + + ret = clk_prepare(ctx->clk); + if (ret < 0) { + clk_put(ctx->clk); + goto err_free; + } + return ctx; + +err_free: + kfree(ctx); + + return ERR_PTR(ret); } /** - * regmap_init_mmio(): Initialise register map + * regmap_init_mmio_clk(): Initialise register map with register clock * * @dev: Device that will be interacted with + * @clk_id: register clock consumer ID * @regs: Pointer to memory-mapped IO region * @config: Configuration for register map * * The return value will be an ERR_PTR() on error or a valid pointer to * a struct regmap. */ -struct regmap *regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config) +struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config) { struct regmap_mmio_context *ctx; - ctx = regmap_mmio_gen_context(regs, config); + ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); if (IS_ERR(ctx)) return ERR_CAST(ctx); return regmap_init(dev, ®map_mmio, ctx, config); } -EXPORT_SYMBOL_GPL(regmap_init_mmio); +EXPORT_SYMBOL_GPL(regmap_init_mmio_clk); /** - * devm_regmap_init_mmio(): Initialise managed register map + * devm_regmap_init_mmio_clk(): Initialise managed register map with clock * * @dev: Device that will be interacted with + * @clk_id: register clock consumer ID * @regs: Pointer to memory-mapped IO region * @config: Configuration for register map * @@ -217,18 +270,18 @@ EXPORT_SYMBOL_GPL(regmap_init_mmio); * to a struct regmap. The regmap will be automatically freed by the * device management code. */ -struct regmap *devm_regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config) +struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config) { struct regmap_mmio_context *ctx; - ctx = regmap_mmio_gen_context(regs, config); + ctx = regmap_mmio_gen_context(dev, clk_id, regs, config); if (IS_ERR(ctx)) return ERR_CAST(ctx); return devm_regmap_init(dev, ®map_mmio, ctx, config); } -EXPORT_SYMBOL_GPL(devm_regmap_init_mmio); +EXPORT_SYMBOL_GPL(devm_regmap_init_mmio_clk); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/regmap.h b/include/linux/regmap.h index b7e95bf..f0480a3 100644 --- a/include/linux/regmap.h +++ b/include/linux/regmap.h @@ -285,9 +285,9 @@ struct regmap *regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); -struct regmap *regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config); +struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config); struct regmap *devm_regmap_init(struct device *dev, const struct regmap_bus *bus, @@ -297,9 +297,44 @@ struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, const struct regmap_config *config); struct regmap *devm_regmap_init_spi(struct spi_device *dev, const struct regmap_config *config); -struct regmap *devm_regmap_init_mmio(struct device *dev, - void __iomem *regs, - const struct regmap_config *config); +struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id, + void __iomem *regs, + const struct regmap_config *config); + +/** + * regmap_init_mmio(): Initialise register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer to + * a struct regmap. + */ +static inline struct regmap *regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + return regmap_init_mmio_clk(dev, NULL, regs, config); +} + +/** + * devm_regmap_init_mmio(): Initialise managed register map + * + * @dev: Device that will be interacted with + * @regs: Pointer to memory-mapped IO region + * @config: Configuration for register map + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap. The regmap will be automatically freed by the + * device management code. + */ +static inline struct regmap *devm_regmap_init_mmio(struct device *dev, + void __iomem *regs, + const struct regmap_config *config) +{ + return devm_regmap_init_mmio_clk(dev, NULL, regs, config); +} void regmap_exit(struct regmap *map); int regmap_reinit_cache(struct regmap *map, -- cgit v0.10.2