From f00e71091ab92eba52122332586c6ecaa9cd1a56 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 8 Feb 2018 10:23:44 +0300 Subject: regmap: Fix reversed bounds check in regmap_raw_write() We're supposed to be checking that "val_len" is not too large but instead we check if it is smaller than the max. The only function affected would be regmap_i2c_smbus_i2c_write() in drivers/base/regmap/regmap-i2c.c. Strangely that function has its own limit check which returns an error if (count >= I2C_SMBUS_BLOCK_MAX) so it doesn't look like it has ever been able to do anything except return an error. Fixes: c335931ed9d2 ("regmap: Add raw_write/read checks for max_raw_write/read sizes") Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee302ccdfbc8..453116fd4362 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1831,7 +1831,7 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; - if (map->max_raw_write && map->max_raw_write > val_len) + if (map->max_raw_write && map->max_raw_write < val_len) return -E2BIG; map->lock(map->lock_arg); -- cgit v1.2.3 From 86effbe0d198aaf57459ec9ad3855e88e29ecb1c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 8 Feb 2018 10:25:28 +0300 Subject: regmap-i2c: Off by one in regmap_i2c_smbus_i2c_read/write() The commit message says that we are allowed to read and write up to 32 bytes but the code only allows us to write 31 bytes. In other words, the ">=" should be changed to ">". But this is already checked in regmap_raw_read()/write() so we can just remove the if statemetents. Fixes: 29332534e2b6 ("regmap-i2c: Add smbus i2c block support") Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-i2c.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 4735318f4268..056acde5e7d3 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -217,8 +217,6 @@ static int regmap_i2c_smbus_i2c_write(void *context, const void *data, if (count < 1) return -EINVAL; - if (count >= I2C_SMBUS_BLOCK_MAX) - return -E2BIG; --count; return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count, @@ -235,8 +233,6 @@ static int regmap_i2c_smbus_i2c_read(void *context, const void *reg, if (reg_size != 1 || val_size < 1) return -EINVAL; - if (val_size >= I2C_SMBUS_BLOCK_MAX) - return -E2BIG; ret = i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val); if (ret == val_size) -- cgit v1.2.3 From b8f9a03b741ddfdde4aa8b607fa7d88eb63a6338 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 12 Feb 2018 18:15:45 +0000 Subject: regmap: Correct offset handling in regmap_volatile_range The current implementation is broken for regmaps that have a reg_stride, since it doesn't take the stride into account. Correct this by using the helper function to calculate the register offset. Fixes: f01ee60fffa4 ("regmap: implement register striding") Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee302ccdfbc8..f89d01e7b257 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -174,7 +174,7 @@ static bool regmap_volatile_range(struct regmap *map, unsigned int reg, unsigned int i; for (i = 0; i < num; i++) - if (!regmap_volatile(map, reg + i)) + if (!regmap_volatile(map, reg + regmap_get_offset(map, i))) return false; return true; -- cgit v1.2.3 From 71df179363a5a733a8932e9afb869760d7559383 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 12 Feb 2018 18:15:44 +0000 Subject: regmap: Correct comparison in regmap_cached The cache pointer points to the actual memory used by the cache, as the comparison here is looking for the type of the cache it should check against cache_type. Fixes: 1ea975cf1ef5 ("regmap: Add a function to check if a regmap register is cached") Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee302ccdfbc8..7519b039c162 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -99,7 +99,7 @@ bool regmap_cached(struct regmap *map, unsigned int reg) int ret; unsigned int val; - if (map->cache == REGCACHE_NONE) + if (map->cache_type == REGCACHE_NONE) return false; if (!map->cache_ops) -- cgit v1.2.3 From 9ae27a8d1f3ebff09191fb8cb1341414547293b2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 12 Feb 2018 18:15:46 +0000 Subject: regmap: Don't use format_val in regmap_bulk_read A bulk read can be implemented either through regmap_raw_read, or by reading each register individually using regmap_read. Both regmap_read and regmap_bulk_read should return values in native endian. In the individual case the current implementation calls format_val to put the data into the output array, which can cause endian issues. The regmap_read will have already converted the data into native endian, if the hosts endian differs from the device then format_val will switch the endian back again. Rather than using format_val simply use the code that is called if there is no format_val function. This code supports all cases except 24-bit but there don't appear to be any users of regmap_bulk_read for 24-bit. Additionally, it would have to be a big endian host for the old code to actually function correctly anyway. Fixes: 15b8d2c41fe5 ("regmap: Fix regmap_bulk_read in BE mode") Reported-by: David Rhodes Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 55 ++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee302ccdfbc8..4037b3782bd3 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2709,47 +2709,38 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_inplace(val + i); } else { +#ifdef CONFIG_64BIT + u64 *u64 = val; +#endif + u32 *u32 = val; + u16 *u16 = val; + u8 *u8 = val; + for (i = 0; i < val_count; i++) { unsigned int ival; + ret = regmap_read(map, reg + regmap_get_offset(map, i), &ival); if (ret != 0) return ret; - if (map->format.format_val) { - map->format.format_val(val + (i * val_bytes), ival, 0); - } else { - /* Devices providing read and write - * operations can use the bulk I/O - * functions if they define a val_bytes, - * we assume that the values are native - * endian. - */ -#ifdef CONFIG_64BIT - u64 *u64 = val; -#endif - u32 *u32 = val; - u16 *u16 = val; - u8 *u8 = val; - - switch (map->format.val_bytes) { + switch (map->format.val_bytes) { #ifdef CONFIG_64BIT - case 8: - u64[i] = ival; - break; + case 8: + u64[i] = ival; + break; #endif - case 4: - u32[i] = ival; - break; - case 2: - u16[i] = ival; - break; - case 1: - u8[i] = ival; - break; - default: - return -EINVAL; - } + case 4: + u32[i] = ival; + break; + case 2: + u16[i] = ival; + break; + case 1: + u8[i] = ival; + break; + default: + return -EINVAL; } } } -- cgit v1.2.3 From 45abcc556721a6d18e4af82e20e164044f0a3e36 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Mon, 12 Feb 2018 18:15:47 +0000 Subject: regmap: Use helper function for register offset As a helper function exists for calculating register offsets lets use that rather than open coding with the reg_stride. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 4037b3782bd3..f075c05859b0 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1993,7 +1993,7 @@ out: return -EINVAL; } - ret = regmap_write(map, reg + (i * map->reg_stride), + ret = regmap_write(map, reg + regmap_get_offset(map, i), ival); if (ret) return ret; -- cgit v1.2.3 From 0645ba4331c2b02ba9907b1591ba722535890e9f Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 15 Feb 2018 17:52:16 +0000 Subject: regmap: Move the handling for max_raw_read into regmap_raw_read Currently regmap_bulk_read will split a read into chunks before calling regmap_raw_read if max_raw_read is set. It is more logical for this handling to be inside regmap_raw_read itself, as this removes the need to keep re-implementing the chunking code, which would be the same for all users of regmap_raw_read. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 90 +++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 55 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index f075c05859b0..0cc7387008c9 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2542,18 +2542,45 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { + int chunk_stride = map->reg_stride; + size_t chunk_size = val_bytes; + size_t chunk_count = val_count; + if (!map->bus->read) { ret = -ENOTSUPP; goto out; } - if (map->max_raw_read && map->max_raw_read < val_len) { - ret = -E2BIG; - goto out; + + if (!map->use_single_read) { + if (map->max_raw_read) + chunk_size = map->max_raw_read; + else + chunk_size = val_len; + if (chunk_size % val_bytes) + chunk_size -= chunk_size % val_bytes; + chunk_count = val_len / chunk_size; + chunk_stride *= chunk_size / val_bytes; } - /* Physical block read if there's no cache involved */ - ret = _regmap_raw_read(map, reg, val, val_len); + /* Read bytes that fit into a multiple of chunk_size */ + for (i = 0; i < chunk_count; i++) { + ret = _regmap_raw_read(map, + reg + (i * chunk_stride), + val + (i * chunk_size), + chunk_size); + if (ret != 0) + return ret; + } + /* Read remaining bytes */ + if (chunk_size * i < val_len) { + ret = _regmap_raw_read(map, + reg + (i * chunk_stride), + val + (i * chunk_size), + val_len - i * chunk_size); + if (ret != 0) + return ret; + } } else { /* Otherwise go word by word for the cache; should be low * cost as we expect to hit the cache. @@ -2655,56 +2682,9 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, return -EINVAL; if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) { - /* - * Some devices does not support bulk read, for - * them we have a series of single read operations. - */ - size_t total_size = val_bytes * val_count; - - if (!map->use_single_read && - (!map->max_raw_read || map->max_raw_read > total_size)) { - ret = regmap_raw_read(map, reg, val, - val_bytes * val_count); - if (ret != 0) - return ret; - } else { - /* - * Some devices do not support bulk read or do not - * support large bulk reads, for them we have a series - * of read operations. - */ - int chunk_stride = map->reg_stride; - size_t chunk_size = val_bytes; - size_t chunk_count = val_count; - - if (!map->use_single_read) { - chunk_size = map->max_raw_read; - if (chunk_size % val_bytes) - chunk_size -= chunk_size % val_bytes; - chunk_count = total_size / chunk_size; - chunk_stride *= chunk_size / val_bytes; - } - - /* Read bytes that fit into a multiple of chunk_size */ - for (i = 0; i < chunk_count; i++) { - ret = regmap_raw_read(map, - reg + (i * chunk_stride), - val + (i * chunk_size), - chunk_size); - if (ret != 0) - return ret; - } - - /* Read remaining bytes */ - if (chunk_size * i < total_size) { - ret = regmap_raw_read(map, - reg + (i * chunk_stride), - val + (i * chunk_size), - total_size - i * chunk_size); - if (ret != 0) - return ret; - } - } + 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_inplace(val + i); -- cgit v1.2.3 From 1b079ca2c2e9a4652051bc4b62a5ef83d59d86bb Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 15 Feb 2018 17:52:17 +0000 Subject: regmap: Tidy up regmap_raw_read chunking code Raw reads may need to be split into small chunks if max_raw_read is set. Tidy up the code implementing this, the new code is slightly clearer, slightly shorter and slightly more efficient. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 0cc7387008c9..ff30a9157de5 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2542,44 +2542,38 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val, if (regmap_volatile_range(map, reg, val_count) || map->cache_bypass || map->cache_type == REGCACHE_NONE) { - int chunk_stride = map->reg_stride; - size_t chunk_size = val_bytes; - size_t chunk_count = val_count; + size_t chunk_count, chunk_bytes; + size_t chunk_regs = val_count; if (!map->bus->read) { ret = -ENOTSUPP; goto out; } - if (!map->use_single_read) { - if (map->max_raw_read) - chunk_size = map->max_raw_read; - else - chunk_size = val_len; - if (chunk_size % val_bytes) - chunk_size -= chunk_size % val_bytes; - chunk_count = val_len / chunk_size; - chunk_stride *= chunk_size / val_bytes; - } + if (map->use_single_read) + chunk_regs = 1; + else if (map->max_raw_read && val_len > map->max_raw_read) + chunk_regs = map->max_raw_read / val_bytes; - /* Read bytes that fit into a multiple of chunk_size */ + chunk_count = val_count / chunk_regs; + chunk_bytes = chunk_regs * val_bytes; + + /* Read bytes that fit into whole chunks */ for (i = 0; i < chunk_count; i++) { - ret = _regmap_raw_read(map, - reg + (i * chunk_stride), - val + (i * chunk_size), - chunk_size); + ret = _regmap_raw_read(map, reg, val, chunk_bytes); if (ret != 0) - return ret; + goto out; + + reg += regmap_get_offset(map, chunk_regs); + val += chunk_bytes; + val_len -= chunk_bytes; } /* Read remaining bytes */ - if (chunk_size * i < val_len) { - ret = _regmap_raw_read(map, - reg + (i * chunk_stride), - val + (i * chunk_size), - val_len - i * chunk_size); + if (val_len) { + ret = _regmap_raw_read(map, reg, val, val_len); if (ret != 0) - return ret; + goto out; } } else { /* Otherwise go word by word for the cache; should be low -- cgit v1.2.3 From 186ba2eec275a5e4ee09d4b6a77c619e46fab9fd Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 15 Feb 2018 17:52:18 +0000 Subject: regmap: Use _regmap_read in regmap_bulk_read Bulk reads may potentially read a lot of registers and regmap_read will take and release the regmap lock for each register. Avoid bouncing the lock so frequently by holding the lock locally and calling _regmap_read instead. This also has the nice side-effect that all the reads will be done atomically so no other threads can sneak a write in during the regmap_bulk_read. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ff30a9157de5..258a40e2a1d3 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2674,6 +2674,8 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; + if (val_count == 0) + return -EINVAL; if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) { ret = regmap_raw_read(map, reg, val, val_bytes * val_count); @@ -2690,13 +2692,15 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, u16 *u16 = val; u8 *u8 = val; + map->lock(map->lock_arg); + for (i = 0; i < val_count; i++) { unsigned int ival; - ret = regmap_read(map, reg + regmap_get_offset(map, i), - &ival); + ret = _regmap_read(map, reg + regmap_get_offset(map, i), + &ival); if (ret != 0) - return ret; + goto out; switch (map->format.val_bytes) { #ifdef CONFIG_64BIT @@ -2714,12 +2718,16 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, u8[i] = ival; break; default: - return -EINVAL; + ret = -EINVAL; + goto out; } } + +out: + map->unlock(map->lock_arg); } - return 0; + return ret; } EXPORT_SYMBOL_GPL(regmap_bulk_read); -- cgit v1.2.3 From 12ae3808c160b7be0de3c633ac4cbe8c5f2937bf Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 19 Feb 2018 15:43:01 -0600 Subject: regmap: Allow missing device in regmap_name_read_file() This fixes a possible NULL pointer dereference oops in regmap_name_read_file() when the regmap does not have a device associated with it. For example syscon regmaps retrieved with syscon_regmap_lookup_by_compatible() don't have a device. Signed-off-by: David Lechner Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index f3266334063e..7eb512ba2828 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -40,6 +40,7 @@ static ssize_t regmap_name_read_file(struct file *file, loff_t *ppos) { struct regmap *map = file->private_data; + const char *name = "nodev"; int ret; char *buf; @@ -47,7 +48,10 @@ static ssize_t regmap_name_read_file(struct file *file, if (!buf) return -ENOMEM; - ret = snprintf(buf, PAGE_SIZE, "%s\n", map->dev->driver->name); + if (map->dev && map->dev->driver) + name = map->dev->driver->name; + + ret = snprintf(buf, PAGE_SIZE, "%s\n", name); if (ret < 0) { kfree(buf); return ret; -- cgit v1.2.3 From 9b947a13e7f6017f18470f665992dbae267852b3 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Mon, 19 Feb 2018 15:43:02 -0600 Subject: regmap: use debugfs even when no device This registers regmaps with debugfs even when they do not have an associated device. For example, this is common for syscon regmaps. Signed-off-by: David Lechner Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index ee302ccdfbc8..f5fa1ddc65e9 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1116,6 +1116,8 @@ skip_format_initialization: ret = regmap_attach_dev(dev, map, config); if (ret != 0) goto err_regcache; + } else { + regmap_debugfs_init(map, config->name); } return map; -- cgit v1.2.3 From 0812d8ffa9955251ba0077488d4408d8987ec091 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 22 Feb 2018 12:59:10 +0000 Subject: regmap: Format data for raw write in regmap_bulk_write In the case were the bulk transaction is split up into smaller chunks data is passed directly to regmap_raw_write. However regmap_bulk_write uses data in host endian and regmap_raw_write expects data in device endian. As such if the host and device differ in endian the wrong data will be written to the device. Correct this issue using a similar approach to the single raw write case below it, duplicate the data into a new buffer and use parse_inplace to format the data correctly. Fixes: adaac459759d ("regmap: Introduce max_raw_read/write for regmap_bulk_read/write") Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index f5d653663626..8fe6e08fa41e 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2003,6 +2003,17 @@ out: int chunk_stride = map->reg_stride; size_t chunk_size = val_bytes; size_t chunk_count = val_count; + void *wval; + + if (!val_count) + return -EINVAL; + + wval = kmemdup(val, val_count * val_bytes, map->alloc_flags); + if (!wval) + return -ENOMEM; + + for (i = 0; i < val_count * val_bytes; i += val_bytes) + map->format.parse_inplace(wval + i); if (!map->use_single_write) { chunk_size = map->max_raw_write; @@ -2017,7 +2028,7 @@ out: for (i = 0; i < chunk_count; i++) { ret = _regmap_raw_write(map, reg + (i * chunk_stride), - val + (i * chunk_size), + wval + (i * chunk_size), chunk_size); if (ret) break; @@ -2026,10 +2037,12 @@ out: /* Write remaining bytes */ if (!ret && chunk_size * i < total_size) { ret = _regmap_raw_write(map, reg + (i * chunk_stride), - val + (i * chunk_size), + wval + (i * chunk_size), total_size - i * chunk_size); } map->unlock(map->lock_arg); + + kfree(wval); } else { void *wval; -- cgit v1.2.3 From b4ecfec5ee3f282a4ac0876de332876fec9b488c Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 22 Feb 2018 12:59:11 +0000 Subject: regmap: Remove unnecessary printk for failed allocation Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 8fe6e08fa41e..707b0450ad72 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -2050,10 +2050,9 @@ out: return -EINVAL; wval = kmemdup(val, val_count * val_bytes, map->alloc_flags); - if (!wval) { - dev_err(map->dev, "Error in memory allocation\n"); + if (!wval) return -ENOMEM; - } + for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_inplace(wval + i); -- cgit v1.2.3 From 7ef2c6b8689a084954cffbd102ee49c2fb72cbd4 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 22 Feb 2018 12:59:12 +0000 Subject: regmap: Move the handling for max_raw_write into regmap_raw_write Currently regmap_bulk_write will split a write into chunks before calling regmap_raw_write if max_raw_write is set. It is more logical for this handling to be inside regmap_raw_write itself, as this removes the need to keep re-implementing the chunking code, which would be the same for all users of regmap_raw_write. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 117 ++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 63 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 707b0450ad72..e82ea77849fb 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1438,8 +1438,8 @@ static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes, buf[i] |= (mask >> (8 * i)) & 0xff; } -int _regmap_raw_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len) +static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) { struct regmap_range_node *range; unsigned long flags; @@ -1490,8 +1490,9 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, while (val_num > win_residue) { 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); + ret = _regmap_raw_write_impl(map, reg, val, + win_residue * + map->format.val_bytes); if (ret != 0) return ret; @@ -1707,11 +1708,11 @@ static int _regmap_bus_raw_write(void *context, unsigned int reg, 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 _regmap_raw_write_impl(map, reg, + map->work_buf + + map->format.reg_bytes + + map->format.pad_bytes, + map->format.val_bytes); } static inline void *_regmap_map_get_context(struct regmap *map) @@ -1806,6 +1807,49 @@ int regmap_write_async(struct regmap *map, unsigned int reg, unsigned int val) } EXPORT_SYMBOL_GPL(regmap_write_async); +int _regmap_raw_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + size_t val_bytes = map->format.val_bytes; + size_t val_count = val_len / val_bytes; + int chunk_stride = map->reg_stride; + size_t chunk_size = val_bytes; + size_t chunk_count = val_count; + int ret, i; + + if (!val_count) + return -EINVAL; + + if (!map->use_single_write) { + if (map->max_raw_write) + chunk_size = map->max_raw_write; + else + chunk_size = val_len; + if (chunk_size % val_bytes) + chunk_size -= chunk_size % val_bytes; + chunk_count = val_len / chunk_size; + chunk_stride *= chunk_size / val_bytes; + } + + /* Write as many bytes as possible with chunk_size */ + for (i = 0; i < chunk_count; i++) { + ret = _regmap_raw_write_impl(map, + reg + (i * chunk_stride), + val + (i * chunk_size), + chunk_size); + if (ret) + return ret; + } + + /* Write remaining bytes */ + if (!ret && chunk_size * i < val_len) + ret = _regmap_raw_write_impl(map, reg + (i * chunk_stride), + val + (i * chunk_size), + val_len - i * chunk_size); + + return ret; +} + /** * regmap_raw_write() - Write raw values to one or more registers * @@ -1831,8 +1875,6 @@ int regmap_raw_write(struct regmap *map, unsigned int reg, return -EINVAL; if (val_len % map->format.val_bytes) return -EINVAL; - if (map->max_raw_write && map->max_raw_write < val_len) - return -E2BIG; map->lock(map->lock_arg); @@ -1923,7 +1965,6 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, { int ret = 0, i; size_t val_bytes = map->format.val_bytes; - size_t total_size = val_bytes * val_count; if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; @@ -1998,57 +2039,9 @@ out: if (ret) return ret; } - } else if (map->use_single_write || - (map->max_raw_write && map->max_raw_write < total_size)) { - int chunk_stride = map->reg_stride; - size_t chunk_size = val_bytes; - size_t chunk_count = val_count; - void *wval; - - if (!val_count) - return -EINVAL; - - wval = kmemdup(val, val_count * val_bytes, map->alloc_flags); - if (!wval) - return -ENOMEM; - - for (i = 0; i < val_count * val_bytes; i += val_bytes) - map->format.parse_inplace(wval + i); - - if (!map->use_single_write) { - chunk_size = map->max_raw_write; - if (chunk_size % val_bytes) - chunk_size -= chunk_size % val_bytes; - chunk_count = total_size / chunk_size; - chunk_stride *= chunk_size / val_bytes; - } - - map->lock(map->lock_arg); - /* Write as many bytes as possible with chunk_size */ - for (i = 0; i < chunk_count; i++) { - ret = _regmap_raw_write(map, - reg + (i * chunk_stride), - wval + (i * chunk_size), - chunk_size); - if (ret) - break; - } - - /* Write remaining bytes */ - if (!ret && chunk_size * i < total_size) { - ret = _regmap_raw_write(map, reg + (i * chunk_stride), - wval + (i * chunk_size), - total_size - i * chunk_size); - } - map->unlock(map->lock_arg); - - kfree(wval); } else { void *wval; - if (!val_count) - return -EINVAL; - wval = kmemdup(val, val_count * val_bytes, map->alloc_flags); if (!wval) return -ENOMEM; @@ -2056,9 +2049,7 @@ out: for (i = 0; i < val_count * val_bytes; i += val_bytes) map->format.parse_inplace(wval + i); - map->lock(map->lock_arg); - ret = _regmap_raw_write(map, reg, wval, val_bytes * val_count); - map->unlock(map->lock_arg); + ret = regmap_raw_write(map, reg, wval, val_bytes * val_count); kfree(wval); } -- cgit v1.2.3 From 364e378b8d1679f91a29a9537a881bba17931cfb Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 22 Feb 2018 12:59:13 +0000 Subject: regmap: Tidy up regmap_raw_write chunking code Raw writes may need to be split into small chunks if max_raw_write is set. Tidy up the code implementing this, the new code is slightly clearer, slightly shorter and slightly more efficient. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index e82ea77849fb..bfdd66dd3766 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1812,40 +1812,35 @@ int _regmap_raw_write(struct regmap *map, unsigned int reg, { size_t val_bytes = map->format.val_bytes; size_t val_count = val_len / val_bytes; - int chunk_stride = map->reg_stride; - size_t chunk_size = val_bytes; - size_t chunk_count = val_count; + size_t chunk_count, chunk_bytes; + size_t chunk_regs = val_count; int ret, i; if (!val_count) return -EINVAL; - if (!map->use_single_write) { - if (map->max_raw_write) - chunk_size = map->max_raw_write; - else - chunk_size = val_len; - if (chunk_size % val_bytes) - chunk_size -= chunk_size % val_bytes; - chunk_count = val_len / chunk_size; - chunk_stride *= chunk_size / val_bytes; - } + if (map->use_single_write) + chunk_regs = 1; + else if (map->max_raw_write && val_len > map->max_raw_write) + chunk_regs = map->max_raw_write / val_bytes; + + chunk_count = val_count / chunk_regs; + chunk_bytes = chunk_regs * val_bytes; /* Write as many bytes as possible with chunk_size */ for (i = 0; i < chunk_count; i++) { - ret = _regmap_raw_write_impl(map, - reg + (i * chunk_stride), - val + (i * chunk_size), - chunk_size); + ret = _regmap_raw_write_impl(map, reg, val, chunk_bytes); if (ret) return ret; + + reg += regmap_get_offset(map, chunk_regs); + val += chunk_bytes; + val_len -= chunk_bytes; } /* Write remaining bytes */ - if (!ret && chunk_size * i < val_len) - ret = _regmap_raw_write_impl(map, reg + (i * chunk_stride), - val + (i * chunk_size), - val_len - i * chunk_size); + if (val_len) + ret = _regmap_raw_write_impl(map, reg, val, val_len); return ret; } -- cgit v1.2.3 From fb44f3cec35c6e71865012fa281ba6d4ff50a99a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 22 Feb 2018 12:59:14 +0000 Subject: regmap: Merge redundant handling in regmap_bulk_write The handling for the first two cases in regmap_bulk_write is essentially identical. The first case is just a better implementation of the second, supporting 8 byte registers and doing the locking manually to avoid bouncing the lock for each register. Drop some redundant code by removing the second of these cases and allowing both situations to be handled by the same code. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/base/regmap/regmap.c | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index bfdd66dd3766..fafee9251d65 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1965,17 +1965,10 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, return -EINVAL; /* - * Some devices don't support bulk write, for - * them we have a series of single write operations in the first two if - * blocks. - * - * The first if block is used for memory mapped io. It does not allow - * val_bytes of 3 for example. - * The second one is for busses that do not provide raw I/O. - * The third one is used for busses which do not have these limitations - * and can write arbitrary value lengths. + * Some devices don't support bulk write, for them we have a series of + * single write operations. */ - if (!map->bus) { + if (!map->bus || !map->format.parse_inplace) { map->lock(map->lock_arg); for (i = 0; i < val_count; i++) { unsigned int ival; @@ -2008,32 +2001,6 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val, } out: map->unlock(map->lock_arg); - } else if (map->bus && !map->format.parse_inplace) { - const u8 *u8 = val; - const u16 *u16 = val; - const u32 *u32 = val; - unsigned int ival; - - for (i = 0; i < val_count; i++) { - switch (map->format.val_bytes) { - case 4: - ival = u32[i]; - break; - case 2: - ival = u16[i]; - break; - case 1: - ival = u8[i]; - break; - default: - return -EINVAL; - } - - ret = regmap_write(map, reg + regmap_get_offset(map, i), - ival); - if (ret) - return ret; - } } else { void *wval; -- cgit v1.2.3 From 31895662f9ba81e8ea9ef05abf8edcb29d4b9c18 Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Wed, 21 Feb 2018 10:20:25 +0100 Subject: regmap: mmio: Add function to attach a clock regmap_init_mmio_clk allows to specify a clock that needs to be enabled while accessing the registers. However, that clock is retrieved through its clock ID, which means it will lookup that clock based on the current device that registers the regmap, and, in the DT case, will only look in that device OF node. This might be problematic if the clock to enable is stored in another node. Let's add a function that allows to attach a clock that has already been retrieved to a regmap in order to fix this. Signed-off-by: Maxime Ripard Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-mmio.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c index 5189fd6182f6..5cadfd3394d8 100644 --- a/drivers/base/regmap/regmap-mmio.c +++ b/drivers/base/regmap/regmap-mmio.c @@ -28,6 +28,8 @@ struct regmap_mmio_context { void __iomem *regs; unsigned val_bytes; + + bool attached_clk; struct clk *clk; void (*reg_write)(struct regmap_mmio_context *ctx, @@ -363,4 +365,26 @@ struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, } EXPORT_SYMBOL_GPL(__devm_regmap_init_mmio_clk); +int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk) +{ + struct regmap_mmio_context *ctx = map->bus_context; + + ctx->clk = clk; + ctx->attached_clk = true; + + return clk_prepare(ctx->clk); +} +EXPORT_SYMBOL_GPL(regmap_mmio_attach_clk); + +void regmap_mmio_detach_clk(struct regmap *map) +{ + struct regmap_mmio_context *ctx = map->bus_context; + + clk_unprepare(ctx->clk); + + ctx->attached_clk = false; + ctx->clk = NULL; +} +EXPORT_SYMBOL_GPL(regmap_mmio_detach_clk); + MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From a430ab205d29e7d1537b220fcf989b8080d8267f Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Mar 2018 15:52:09 -0300 Subject: regmap: debugfs: Disambiguate dummy debugfs file name Since commit 9b947a13e7f6 ("regmap: use debugfs even when no device") allows the usage of regmap debugfs even when there is no device associated, which causes several warnings like this: (NULL device *): Failed to create debugfs directory This happens when the debugfs file name is 'dummy'. The first dummy debugfs creation works fine, but subsequent creations fail as they have all the same name. Disambiguate the 'dummy' debugfs file name by adding a suffix entry, so that the names become dummy0, dummy1, dummy2, etc. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index 7eb512ba2828..e3e7b91cc421 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -25,6 +25,7 @@ struct regmap_debugfs_node { struct list_head link; }; +static unsigned int dummy_index; static struct dentry *regmap_debugfs_root; static LIST_HEAD(regmap_debugfs_early_list); static DEFINE_MUTEX(regmap_debugfs_early_lock); @@ -573,6 +574,11 @@ void regmap_debugfs_init(struct regmap *map, const char *name) name = devname; } + if (!strcmp(name, "dummy")) { + name = kasprintf(GFP_KERNEL, "dummy%d", dummy_index); + dummy_index++; + } + map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); if (!map->debugfs) { dev_warn(map->dev, "Failed to create debugfs directory\n"); -- cgit v1.2.3 From 46589e9c753b7c905e8671a89cbb9728f3b99855 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 5 Mar 2018 20:26:51 +0000 Subject: regmap: debugfs: Don't leak dummy names When allocating dummy names we need to store a pointer to the string we allocate so that we don't leak it on free. Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index e3e7b91cc421..dd3a16894e3c 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -575,7 +575,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) } if (!strcmp(name, "dummy")) { - name = kasprintf(GFP_KERNEL, "dummy%d", dummy_index); + map->debugfs_name = kasprintf(GFP_KERNEL, "dummy%d", + dummy_index); + name = map->debugfs_name; dummy_index++; } -- cgit v1.2.3 From 17cf46cfe975f1dd04db6bd38398923846512d49 Mon Sep 17 00:00:00 2001 From: Jeffy Chen Date: Tue, 6 Mar 2018 19:04:03 +0800 Subject: regmap: debugfs: Free map->debugfs_name when debugfs_create_dir() failed Free map->debugfs_name when debugfs_create_dir() failed to avoid memory leak. Signed-off-by: Jeffy Chen Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index dd3a16894e3c..c84f5ceb015a 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -584,6 +584,9 @@ void regmap_debugfs_init(struct regmap *map, const char *name) map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); if (!map->debugfs) { dev_warn(map->dev, "Failed to create debugfs directory\n"); + + kfree(map->debugfs_name); + map->debugfs_name = NULL; return; } -- cgit v1.2.3 From 59dd2a85040103e95b1c87b1b920d357ee91dada Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 5 Mar 2018 23:13:04 -0300 Subject: regmap: debugfs: Improve warning message on debugfs_create_dir() failure Currently when debugfs_create_dir() fails we receive a warning message that provides no indication as to what was the directory entry that failed to be created. Improve the warning message by printing the directory name that failed in order to help debugging. Signed-off-by: Fabio Estevam Signed-off-by: Mark Brown --- drivers/base/regmap/regmap-debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c index c84f5ceb015a..87b562e49a43 100644 --- a/drivers/base/regmap/regmap-debugfs.c +++ b/drivers/base/regmap/regmap-debugfs.c @@ -583,7 +583,8 @@ void regmap_debugfs_init(struct regmap *map, const char *name) map->debugfs = debugfs_create_dir(name, regmap_debugfs_root); if (!map->debugfs) { - dev_warn(map->dev, "Failed to create debugfs directory\n"); + dev_warn(map->dev, + "Failed to create %s debugfs directory\n", name); kfree(map->debugfs_name); map->debugfs_name = NULL; -- cgit v1.2.3