Re: [RFC v2] regmap: Add regmap_pipe_read API

From: Crt Mori
Date: Mon Jul 25 2016 - 03:29:34 EST


Adding linux-iio list to this - especially since there are IIO
drivers which work because of some special case...

Otherwise nice series.
Crt
On 20 July 2016 at 17:08, Crestez Dan Leonard <leonard.crestez@xxxxxxxxx> wrote:
> The regmap API usually assumes that bulk read operations will read a
> range of registers but some I2C/SPI devices have certain registers for
> which a such a read operation will return data from an internal FIFO or
> some other kind of buffer. Add an explicit API to support bulk read with
> "output pipe" rather than range semantics.
>
> Some linux drivers use regmap_bulk_read or regmap_raw_read for such
> registers, for example mpu6050 or bmi150 from IIO. This only happens to
> work because when caching is disabled a single short regmap read op will
> map to a single bus read op and behave as intended. This breaks if
> caching is enabled and reg+1 happens to be a cacheable register.
>
> Without regmap support refactoring a driver to enable regmap caching
> requires separate i2c and spi paths. This is exactly what regmap is
> supposed to help avoid.
>
> Suggested-by: Jonathan Cameron <jic23@xxxxxxxxxx>
> Signed-off-by: Crestez Dan Leonard <leonard.crestez@xxxxxxxxx>
>
> ---
> Changes since V1:
> * Improve comments. I think "pipe" is the correct word here.
> * Rely on void* pointer arithmetic instead of casting to u8*
>
> The SPMI implementations of regmap_bus have read functions which
> auto-increment internally and this will not work correctly with
> regmap_pipe_read. I think the correct fix would be for regmap-spmi to
> only implement reg_read. This can be considered an acceptable limitation
> because in order to run into this somebody would have to write new code
> to call this new API with an SPMI device.
>
> I attempted to also support this when the underlying bus only supports
> reg_read (for example an adapter with only I2C_FUNC_SMBUS_BYTE_DATA) but
> it's not clear how to do this on top of _regmap_read. Apparently this
> returns the value as an "unsigned int" which needs to be formatted
> according to val_bytes. Unfortunately map->format.format_val is not
> always available. Presumably the code dealing with this from regmap_bulk_read
> should be refactored into a separate function?
>
> It's not clear why regmap doesn't just always initialize format_val.
>
> drivers/base/regmap/regmap.c | 64 ++++++++++++++++++++++++++++++++++++++++++++
> include/linux/regmap.h | 9 +++++++
> 2 files changed, 73 insertions(+)
>
> diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
> index df2d2ef..55c3090 100644
> --- a/drivers/base/regmap/regmap.c
> +++ b/drivers/base/regmap/regmap.c
> @@ -2408,6 +2408,70 @@ int regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
> EXPORT_SYMBOL_GPL(regmap_raw_read);
>
> /**
> + * regmap_pipe_read(): Read data from a register with pipe semantics
> + *
> + * @map: Register map to read from
> + * @reg: Register to read from
> + * @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
> + * range of registers. Some devices have certain registers for which a read
> + * operation will read a block of data from an internal buffer.
> + *
> + * The target register must be volatile but registers after it can be
> + * completely unrelated cacheable registers.
> + *
> + * This will attempt multiple reads as required to read val_len bytes.
> + *
> + * This function is not supported over SPMI.
> + *
> + * A value of zero will be returned on success, a negative errno will be
> + * returned in error cases.
> + */
> +int regmap_pipe_read(struct regmap *map, unsigned int reg,
> + void *val, size_t val_len)
> +{
> + 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))
> + return -EINVAL;
> + if (val_len == 0)
> + return -EINVAL;
> +
> + map->lock(map->lock_arg);
> +
> + if (!regmap_volatile(map, reg)) {
> + ret = -EINVAL;
> + goto out_unlock;
> + }
> +
> + while (val_len) {
> + if (map->max_raw_read && map->max_raw_read < val_len)
> + read_len = map->max_raw_read;
> + else
> + read_len = val_len;
> + ret = _regmap_raw_read(map, reg, val, read_len);
> + if (ret)
> + goto out_unlock;
> + val += read_len;
> + val_len -= read_len;
> + }
> +
> +out_unlock:
> + map->unlock(map->lock_arg);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(regmap_pipe_read);
> +
> +/**
> * regmap_field_read(): Read a value to a single register field
> *
> * @field: Register field to read from
> diff --git a/include/linux/regmap.h b/include/linux/regmap.h
> index 3dc08ce..18ee90e 100644
> --- a/include/linux/regmap.h
> +++ b/include/linux/regmap.h
> @@ -719,6 +719,8 @@ int regmap_raw_write_async(struct regmap *map, unsigned int reg,
> 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);
> +int regmap_pipe_read(struct regmap *map, unsigned int reg,
> + void *val, size_t val_len);
> int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
> size_t val_count);
> int regmap_update_bits_base(struct regmap *map, unsigned int reg,
> @@ -955,6 +957,13 @@ static inline int regmap_raw_read(struct regmap *map, unsigned int reg,
> return -EINVAL;
> }
>
> +static inline int regmap_pipe_read(struct regmap *map, unsigned int reg,
> + void *val, size_t val_len)
> +{
> + WARN_ONCE(1, "regmap API is disabled");
> + return -EINVAL;
> +}
> +
> static inline int regmap_bulk_read(struct regmap *map, unsigned int reg,
> void *val, size_t val_count)
> {
> --
> 2.7.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html