diff mbox series

[1/2,RFC] regmap: Add bulk read/write callbacks into regmap_config

Message ID 20220430025145.640305-1-marex@denx.de (mailing list archive)
State New, archived
Headers show
Series [1/2,RFC] regmap: Add bulk read/write callbacks into regmap_config | expand

Commit Message

Marek Vasut April 30, 2022, 2:51 a.m. UTC
Currently the regmap_config structure only allows the user to implement
single element register read/write using .reg_read/.reg_write callbacks.
The regmap_bus already implements bulk counterparts of both, and is being
misused as a workaround for the missing bulk read/write callbacks in
regmap_config by a couple of drivers. To stop this misuse, add the bulk
read/write callbacks to regmap_config and call them from the regmap core
code.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Jagan Teki <jagan@amarulasolutions.com>
Cc: Mark Brown <broonie@kernel.org>
Cc: Maxime Ripard <maxime@cerno.tech>
Cc: Robert Foss <robert.foss@linaro.org>
Cc: Sam Ravnborg <sam@ravnborg.org>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
To: dri-devel@lists.freedesktop.org
---
 drivers/base/regmap/internal.h |  4 ++
 drivers/base/regmap/regmap.c   | 76 ++++++++++++++++++----------------
 include/linux/regmap.h         | 12 ++++++
 3 files changed, 56 insertions(+), 36 deletions(-)

Comments

Mark Brown May 5, 2022, 3:12 p.m. UTC | #1
On Sat, 30 Apr 2022 04:51:44 +0200, Marek Vasut wrote:
> Currently the regmap_config structure only allows the user to implement
> single element register read/write using .reg_read/.reg_write callbacks.
> The regmap_bus already implements bulk counterparts of both, and is being
> misused as a workaround for the missing bulk read/write callbacks in
> regmap_config by a couple of drivers. To stop this misuse, add the bulk
> read/write callbacks to regmap_config and call them from the regmap core
> code.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git for-next

Thanks!

[1/2] regmap: Add bulk read/write callbacks into regmap_config
      commit: d77e745613680c54708470402e2b623dcd769681

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark
Marek Vasut May 5, 2022, 5:32 p.m. UTC | #2
On 5/5/22 17:12, Mark Brown wrote:
> On Sat, 30 Apr 2022 04:51:44 +0200, Marek Vasut wrote:
>> Currently the regmap_config structure only allows the user to implement
>> single element register read/write using .reg_read/.reg_write callbacks.
>> The regmap_bus already implements bulk counterparts of both, and is being
>> misused as a workaround for the missing bulk read/write callbacks in
>> regmap_config by a couple of drivers. To stop this misuse, add the bulk
>> read/write callbacks to regmap_config and call them from the regmap core
>> code.
>>
>> [...]
> 
> Applied to
> 
>     https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git for-next
> 
> Thanks!
> 
> [1/2] regmap: Add bulk read/write callbacks into regmap_config
>        commit: d77e745613680c54708470402e2b623dcd769681

I was really hoping this would get a lot more review / comments before 
this is applied.
Mark Brown May 5, 2022, 9:08 p.m. UTC | #3
On Thu, May 05, 2022 at 07:32:23PM +0200, Marek Vasut wrote:
> On 5/5/22 17:12, Mark Brown wrote:
> > On Sat, 30 Apr 2022 04:51:44 +0200, Marek Vasut wrote:

> > > Currently the regmap_config structure only allows the user to implement
> > > single element register read/write using .reg_read/.reg_write callbacks.

> > [1/2] regmap: Add bulk read/write callbacks into regmap_config
> >        commit: d77e745613680c54708470402e2b623dcd769681

> I was really hoping this would get a lot more review / comments before this
> is applied.

I can easily punt for this release, though TBH I'm not anticipating huge
numbers of comments on a regmap patch unless it breaks things for
people, they tend to be very quiet.  I did go through it and didn't spot
any issues so it seemed like the testing coverage would be useful here.
Are there specific things you're worried about that you'd like feedback
on?
Marek Vasut May 5, 2022, 11:55 p.m. UTC | #4
On 5/5/22 23:08, Mark Brown wrote:
> On Thu, May 05, 2022 at 07:32:23PM +0200, Marek Vasut wrote:
>> On 5/5/22 17:12, Mark Brown wrote:
>>> On Sat, 30 Apr 2022 04:51:44 +0200, Marek Vasut wrote:
> 
>>>> Currently the regmap_config structure only allows the user to implement
>>>> single element register read/write using .reg_read/.reg_write callbacks.
> 
>>> [1/2] regmap: Add bulk read/write callbacks into regmap_config
>>>         commit: d77e745613680c54708470402e2b623dcd769681
> 
>> I was really hoping this would get a lot more review / comments before this
>> is applied.
> 
> I can easily punt for this release, though TBH I'm not anticipating huge
> numbers of comments on a regmap patch unless it breaks things for
> people, they tend to be very quiet.

In that case, let's wait and see ...

> I did go through it and didn't spot
> any issues so it seemed like the testing coverage would be useful here.
> Are there specific things you're worried about that you'd like feedback
> on?

Plumbing on core code is worrying.
Jani Nikula May 6, 2022, 10:58 a.m. UTC | #5
On Thu, 05 May 2022, Mark Brown <broonie@kernel.org> wrote:
> On Sat, 30 Apr 2022 04:51:44 +0200, Marek Vasut wrote:
>> Currently the regmap_config structure only allows the user to implement
>> single element register read/write using .reg_read/.reg_write callbacks.
>> The regmap_bus already implements bulk counterparts of both, and is being
>> misused as a workaround for the missing bulk read/write callbacks in
>> regmap_config by a couple of drivers. To stop this misuse, add the bulk
>> read/write callbacks to regmap_config and call them from the regmap core
>> code.
>> 
>> [...]

Hey Mark, sorry for hijacking the thread a bit. regmap.h seems to have
comprehensive API documentation, but there's very little in terms of
higher level documentation that I could find. Is there any?

I've been toying with the idea of adding a regmap based interface for
accessing Display Port DPCD registers, with caching, and regmap seems
like it's got the kitchen sink but I find it a bit difficult to
navigate...

BR,
Jani.
Mark Brown May 6, 2022, 12:42 p.m. UTC | #6
On Fri, May 06, 2022 at 01:58:18PM +0300, Jani Nikula wrote:

> Hey Mark, sorry for hijacking the thread a bit. regmap.h seems to have
> comprehensive API documentation, but there's very little in terms of
> higher level documentation that I could find. Is there any?

Not outside of the source.  I did a presentation at ELC-E ages
ago which you can probably find but I'm not sure how much it
would add.

> I've been toying with the idea of adding a regmap based interface for
> accessing Display Port DPCD registers, with caching, and regmap seems
> like it's got the kitchen sink but I find it a bit difficult to
> navigate...

The bus code is generally very thin so you shouldn't need to
worry about what the core is up to much if you just want to
support some bus.  If this is a bus that has registers at
hardware level then looking at something like regmap-sdw.c should
be helpful, for something that's just a bit stream at the bus
level then something like regmap-i3c.c is a simple example.
Mark Brown May 6, 2022, 12:48 p.m. UTC | #7
On Fri, May 06, 2022 at 01:55:57AM +0200, Marek Vasut wrote:
> On 5/5/22 23:08, Mark Brown wrote:

> > I did go through it and didn't spot
> > any issues so it seemed like the testing coverage would be useful here.
> > Are there specific things you're worried about that you'd like feedback
> > on?

> Plumbing on core code is worrying.

Yeah, that's a big part of why I want things in testing.  With
something like this a lot of it is being aware of all the random
corner cases out there.
Daniel Vetter May 9, 2022, 1:54 p.m. UTC | #8
On Fri, May 06, 2022 at 01:42:37PM +0100, Mark Brown wrote:
> On Fri, May 06, 2022 at 01:58:18PM +0300, Jani Nikula wrote:
> 
> > Hey Mark, sorry for hijacking the thread a bit. regmap.h seems to have
> > comprehensive API documentation, but there's very little in terms of
> > higher level documentation that I could find. Is there any?
> 
> Not outside of the source.  I did a presentation at ELC-E ages
> ago which you can probably find but I'm not sure how much it
> would add.

I watched that one!

Anyway no recording, but I think I've found the pdf. Was in 2012
apparently.

https://elinux.org/images/a/a3/Regmap-_The_Power_of_Subsystems_and_Abstractions.pdf

Cheers, Daniel

> 
> > I've been toying with the idea of adding a regmap based interface for
> > accessing Display Port DPCD registers, with caching, and regmap seems
> > like it's got the kitchen sink but I find it a bit difficult to
> > navigate...
> 
> The bus code is generally very thin so you shouldn't need to
> worry about what the core is up to much if you just want to
> support some bus.  If this is a bus that has registers at
> hardware level then looking at something like regmap-sdw.c should
> be helpful, for something that's just a bit stream at the bus
> level then something like regmap-i3c.c is a simple example.
Mark Brown May 9, 2022, 3:26 p.m. UTC | #9
On Mon, May 09, 2022 at 03:54:11PM +0200, Daniel Vetter wrote:
> On Fri, May 06, 2022 at 01:42:37PM +0100, Mark Brown wrote:

> > Not outside of the source.  I did a presentation at ELC-E ages
> > ago which you can probably find but I'm not sure how much it
> > would add.

> I watched that one!

I'm so sorry.

> Anyway no recording, but I think I've found the pdf. Was in 2012
> apparently.

> https://elinux.org/images/a/a3/Regmap-_The_Power_of_Subsystems_and_Abstractions.pdf

That's the one I was thinking of, yes!
diff mbox series

Patch

diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index b4df36c7b17d..da8996e7a1f1 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -110,6 +110,10 @@  struct regmap {
 	int (*reg_write)(void *context, unsigned int reg, unsigned int val);
 	int (*reg_update_bits)(void *context, unsigned int reg,
 			       unsigned int mask, unsigned int val);
+	/* Bulk read/write */
+	int (*read)(void *context, const void *reg_buf, size_t reg_size,
+		    void *val_buf, size_t val_size);
+	int (*write)(void *context, const void *data, size_t count);
 
 	bool defer_caching;
 
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 5e12f7cb5147..879a87a6461b 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -838,12 +838,15 @@  struct regmap *__regmap_init(struct device *dev,
 		map->reg_stride_order = ilog2(map->reg_stride);
 	else
 		map->reg_stride_order = -1;
-	map->use_single_read = config->use_single_read || !bus || !bus->read;
-	map->use_single_write = config->use_single_write || !bus || !bus->write;
-	map->can_multi_write = config->can_multi_write && bus && bus->write;
+	map->use_single_read = config->use_single_read || !(config->read || (bus && bus->read));
+	map->use_single_write = config->use_single_write || !(config->write || (bus && bus->write));
+	map->can_multi_write = config->can_multi_write && (config->write || (bus && bus->write));
 	if (bus) {
 		map->max_raw_read = bus->max_raw_read;
 		map->max_raw_write = bus->max_raw_write;
+	} else if (config->max_raw_read && config->max_raw_write) {
+		map->max_raw_read = config->max_raw_read;
+		map->max_raw_write = config->max_raw_write;
 	}
 	map->dev = dev;
 	map->bus = bus;
@@ -877,7 +880,16 @@  struct regmap *__regmap_init(struct device *dev,
 		map->read_flag_mask = bus->read_flag_mask;
 	}
 
-	if (!bus) {
+	if (config && config->read && config->write) {
+		map->reg_read  = _regmap_bus_read;
+
+		/* Bulk read/write */
+		map->read = config->read;
+		map->write = config->write;
+
+		reg_endian = REGMAP_ENDIAN_NATIVE;
+		val_endian = REGMAP_ENDIAN_NATIVE;
+	} else if (!bus) {
 		map->reg_read  = config->reg_read;
 		map->reg_write = config->reg_write;
 		map->reg_update_bits = config->reg_update_bits;
@@ -894,10 +906,13 @@  struct regmap *__regmap_init(struct device *dev,
 	} else {
 		map->reg_read  = _regmap_bus_read;
 		map->reg_update_bits = bus->reg_update_bits;
-	}
+		/* Bulk read/write */
+		map->read = bus->read;
+		map->write = bus->write;
 
-	reg_endian = regmap_get_reg_endian(bus, config);
-	val_endian = regmap_get_val_endian(dev, bus, config);
+		reg_endian = regmap_get_reg_endian(bus, config);
+		val_endian = regmap_get_val_endian(dev, bus, config);
+	}
 
 	switch (config->reg_bits + map->reg_shift) {
 	case 2:
@@ -1671,8 +1686,6 @@  static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
 	size_t len;
 	int i;
 
-	WARN_ON(!map->bus);
-
 	/* Check for unwritable or noinc registers in range
 	 * before we start
 	 */
@@ -1754,7 +1767,7 @@  static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
 		val = work_val;
 	}
 
-	if (map->async && map->bus->async_write) {
+	if (map->async && map->bus && map->bus->async_write) {
 		struct regmap_async *async;
 
 		trace_regmap_async_write_start(map, reg, val_len);
@@ -1822,10 +1835,10 @@  static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
 	 * write.
 	 */
 	if (val == work_val)
-		ret = map->bus->write(map->bus_context, map->work_buf,
-				      map->format.reg_bytes +
-				      map->format.pad_bytes +
-				      val_len);
+		ret = map->write(map->bus_context, map->work_buf,
+				 map->format.reg_bytes +
+				 map->format.pad_bytes +
+				 val_len);
 	else if (map->bus->gather_write)
 		ret = map->bus->gather_write(map->bus_context, map->work_buf,
 					     map->format.reg_bytes +
@@ -1844,7 +1857,7 @@  static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
 		memcpy(buf, map->work_buf, map->format.reg_bytes);
 		memcpy(buf + map->format.reg_bytes + map->format.pad_bytes,
 		       val, val_len);
-		ret = map->bus->write(map->bus_context, buf, len);
+		ret = map->write(map->bus_context, buf, len);
 
 		kfree(buf);
 	} else if (ret != 0 && !map->cache_bypass && map->format.parse_val) {
@@ -1901,7 +1914,7 @@  static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 	struct regmap_range_node *range;
 	struct regmap *map = context;
 
-	WARN_ON(!map->bus || !map->format.format_write);
+	WARN_ON(!map->format.format_write);
 
 	range = _regmap_range_lookup(map, reg);
 	if (range) {
@@ -1916,8 +1929,7 @@  static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 
 	trace_regmap_hw_write_start(map, reg, 1);
 
-	ret = map->bus->write(map->bus_context, map->work_buf,
-			      map->format.buf_size);
+	ret = map->write(map->bus_context, map->work_buf, map->format.buf_size);
 
 	trace_regmap_hw_write_done(map, reg, 1);
 
@@ -1937,7 +1949,7 @@  static int _regmap_bus_raw_write(void *context, unsigned int reg,
 {
 	struct regmap *map = context;
 
-	WARN_ON(!map->bus || !map->format.format_val);
+	WARN_ON(!map->format.format_val);
 
 	map->format.format_val(map->work_buf + map->format.reg_bytes
 			       + map->format.pad_bytes, val, 0);
@@ -1951,7 +1963,7 @@  static int _regmap_bus_raw_write(void *context, unsigned int reg,
 
 static inline void *_regmap_map_get_context(struct regmap *map)
 {
-	return (map->bus) ? map : map->bus_context;
+	return (map->bus || (!map->bus && map->read)) ? map : map->bus_context;
 }
 
 int _regmap_write(struct regmap *map, unsigned int reg,
@@ -2363,7 +2375,7 @@  static int _regmap_raw_multi_reg_write(struct regmap *map,
 	u8 = buf;
 	*u8 |= map->write_flag_mask;
 
-	ret = map->bus->write(map->bus_context, buf, len);
+	ret = map->write(map->bus_context, buf, len);
 
 	kfree(buf);
 
@@ -2669,9 +2681,7 @@  static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 	struct regmap_range_node *range;
 	int ret;
 
-	WARN_ON(!map->bus);
-
-	if (!map->bus || !map->bus->read)
+	if (!map->read)
 		return -EINVAL;
 
 	range = _regmap_range_lookup(map, reg);
@@ -2689,9 +2699,9 @@  static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 				      map->read_flag_mask);
 	trace_regmap_hw_read_start(map, reg, val_len / map->format.val_bytes);
 
-	ret = map->bus->read(map->bus_context, map->work_buf,
-			     map->format.reg_bytes + map->format.pad_bytes,
-			     val, val_len);
+	ret = map->read(map->bus_context, map->work_buf,
+			map->format.reg_bytes + map->format.pad_bytes,
+			val, val_len);
 
 	trace_regmap_hw_read_done(map, reg, val_len / map->format.val_bytes);
 
@@ -2802,8 +2812,6 @@  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 (!IS_ALIGNED(reg, map->reg_stride))
@@ -2818,7 +2826,7 @@  int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 		size_t chunk_count, chunk_bytes;
 		size_t chunk_regs = val_count;
 
-		if (!map->bus->read) {
+		if (!map->read) {
 			ret = -ENOTSUPP;
 			goto out;
 		}
@@ -2878,7 +2886,7 @@  EXPORT_SYMBOL_GPL(regmap_raw_read);
  * @val: Pointer to data buffer
  * @val_len: Length of output buffer in bytes.
  *
- * The regmap API usually assumes that bulk bus read operations will read a
+ * The regmap API usually assumes that bulk read operations will read a
  * range of registers. Some devices have certain registers for which a read
  * operation read will read from an internal FIFO.
  *
@@ -2896,10 +2904,6 @@  int regmap_noinc_read(struct regmap *map, unsigned int reg,
 	size_t read_len;
 	int ret;
 
-	if (!map->bus)
-		return -EINVAL;
-	if (!map->bus->read)
-		return -ENOTSUPP;
 	if (val_len % map->format.val_bytes)
 		return -EINVAL;
 	if (!IS_ALIGNED(reg, map->reg_stride))
@@ -3013,7 +3017,7 @@  int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
 	if (val_count == 0)
 		return -EINVAL;
 
-	if (map->bus && map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) {
+	if (map->format.parse_inplace && (vol || map->cache_type == REGCACHE_NONE)) {
 		ret = regmap_raw_read(map, reg, val, val_bytes * val_count);
 		if (ret != 0)
 			return ret;
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index de81a94d7b30..8952fa3d0d59 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -299,6 +299,12 @@  typedef void (*regmap_unlock)(void *);
  *		     if the function require special handling with lock and reg
  *		     handling and the operation cannot be represented as a simple
  *		     update_bits operation on a bus such as SPI, I2C, etc.
+ * @read: Optional callback that if filled will be used to perform all the
+ *        bulk reads from the registers. Data is returned in the buffer used
+ *        to transmit data.
+ * @write: Same as above for writing.
+ * @max_raw_read: Max raw read size that can be used on the device.
+ * @max_raw_write: Max raw write size that can be used on the device.
  * @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).
@@ -385,6 +391,12 @@  struct regmap_config {
 	int (*reg_write)(void *context, unsigned int reg, unsigned int val);
 	int (*reg_update_bits)(void *context, unsigned int reg,
 			       unsigned int mask, unsigned int val);
+	/* Bulk read/write */
+	int (*read)(void *context, const void *reg_buf, size_t reg_size,
+		    void *val_buf, size_t val_size);
+	int (*write)(void *context, const void *data, size_t count);
+	size_t max_raw_read;
+	size_t max_raw_write;
 
 	bool fast_io;