[PATCH v3 0/9] regmap: Add managed API, regmap fields, regmap config

Hi,
This series is a re-spin of Jean-Jacques' earlier effort [0], the goal of which was to facilitate porting drivers from the Linux kernel. It adds the managed API, using the same API as Linux. It also adds support for regmap fields.
Jean-Jacques' series added support for custom regmap read/write callbacks. The design was argued against by Simon [1]. He argued that using the driver model was a better idea instead of the custom functions. That would mean slightly more memory usage and a more involved change.
The main aim of adding the custom functions is to support the Cadence Sierra PHY driver from Linux [2]. The driver's custom functions aren't very complicated, so they can be replaced by some simple formatting options. So, add the struct regmap_config which contains fields to alter the behaviour of the regmap. This includes specifying the size of the read/write operations via 'width', specifying the start and size of range from code instead of device tree via 'r_start' and 'r_size', and specifying a left shift of the register offset before access via 'reg_offset_shift'. The driver can't be ported verbatim now, but this allows the changes to be very isolated and minimal.
These config options allow us to avoid converting to driver model until we really need it.
The Travis CI run can be found at [3].
[0] https://patchwork.ozlabs.org/project/uboot/cover/20191105114700.24989-1-jjhi... [1] https://patchwork.ozlabs.org/comment/2426186/ [2] https://elixir.bootlin.com/linux/latest/source/drivers/phy/cadence/phy-caden... [3] https://travis-ci.org/github/prati0100/uboot/builds/729588518
Changes in v3 (also mentioned in individual patches): - Allow multi-digit index in in_tree(). Fixes test failures in test/bind.py
- s/DM_TESTF/UT_TESTF/g
- Fix merge conflicts in test.dts
- Rebase on latest master.
Changes in v2: - Add comments explaining the need for regmap_field.
- Set regmap->width to a default of REGMAP_SIZE_32 in regmap_alloc() to avoid the checks in regmap_{read,write}().
- Use calloc() instead of using malloc() and memset() to initialize a regmap to zero.
- Update comments on regmap_{read,write}() and regmap_raw_{read,write}().
- Drop comments explaining two non-existent fields in struct reg_field.
- Add a comment with example explaining REG_FIELD().
- Add Simon's Reviewed-by trailers.
Jean-Jacques Hiblot (3): regmap: Add devm_regmap_init() regmap: Add support for regmap fields test: dm: Add tests for regmap managed API and regmap fields
Pratyush Yadav (6): regmap: zero out the regmap on allocation regmap: Allow specifying read/write width regmap: Allow left shifting register offset before access regmap: Add regmap_init_mem_range() regmap: Allow devices to specify regmap range start and size in config test/py: allow multi-digit index in in_tree()
arch/sandbox/dts/test.dts | 13 +++ drivers/core/regmap.c | 163 ++++++++++++++++++++++++++++- include/regmap.h | 205 ++++++++++++++++++++++++++++++++++--- test/dm/regmap.c | 198 +++++++++++++++++++++++++++++++++++ test/py/tests/test_bind.py | 2 +- 5 files changed, 563 insertions(+), 18 deletions(-)
-- 2.28.0

From: Jean-Jacques Hiblot jjhiblot@ti.com
Most of new linux drivers are using managed-API to allocate resources. To ease porting drivers from linux to U-Boot, introduce devm_regmap_init() as a managed API to get a regmap from the device tree.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Pratyush Yadav p.yadav@ti.com ---
Notes: No changes in v3.
drivers/core/regmap.c | 29 +++++++++++++++++++++++++++++ include/regmap.h | 18 ++++++++++++++++++ 2 files changed, 47 insertions(+)
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index a67a237b88..74225361fd 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -14,7 +14,10 @@ #include <regmap.h> #include <asm/io.h> #include <dm/of_addr.h> +#include <dm/devres.h> #include <linux/ioport.h> +#include <linux/compat.h> +#include <linux/err.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -228,6 +231,32 @@ err:
return ret; } + +static void devm_regmap_release(struct udevice *dev, void *res) +{ + regmap_uninit(*(struct regmap **)res); +} + +struct regmap *devm_regmap_init(struct udevice *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config) +{ + int rc; + struct regmap **mapp; + + mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), + __GFP_ZERO); + if (unlikely(!mapp)) + return ERR_PTR(-ENOMEM); + + rc = regmap_init_mem(dev_ofnode(dev), mapp); + if (rc) + return ERR_PTR(rc); + + devres_add(dev, mapp); + return *mapp; +} #endif
void *regmap_get_range(struct regmap *map, unsigned int range_num) diff --git a/include/regmap.h b/include/regmap.h index 30183c5e71..c7dd240a74 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -75,6 +75,9 @@ struct regmap_range { ulong size; };
+struct regmap_bus; +struct regmap_config; + /** * struct regmap - a way of accessing hardware/bus registers * @@ -335,6 +338,21 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index);
+/** + * devm_regmap_init() - Initialise register map (device managed) + * + * @dev: Device that will be interacted with + * @bus: Bus-specific callbacks to use with device (IGNORED) + * @bus_context: Data passed to bus-specific callbacks (IGNORED) + * @config: Configuration for register map (IGNORED) + * + * @Return a valid pointer to a struct regmap or a ERR_PTR() on error. + * The structure is automatically freed when the device is unbound + */ +struct regmap *devm_regmap_init(struct udevice *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config); /** * regmap_get_range() - Obtain the base memory address of a regmap range *

On Thu, Sep 24, 2020 at 10:04:10AM +0530, Pratyush Yadav wrote:
From: Jean-Jacques Hiblot jjhiblot@ti.com
Most of new linux drivers are using managed-API to allocate resources. To ease porting drivers from linux to U-Boot, introduce devm_regmap_init() as a managed API to get a regmap from the device tree.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Pratyush Yadav p.yadav@ti.com
Applied to u-boot/next, thanks!

Some fields will be introduced in the regmap structure that should be set to 0 by default. So, once we allocate a regmap, make sure it is zeroed out to avoid unexpected defaults for those values.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org ---
Notes: No changes in v3.
drivers/core/regmap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 74225361fd..809f58489f 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -30,8 +30,9 @@ DECLARE_GLOBAL_DATA_PTR; static struct regmap *regmap_alloc(int count) { struct regmap *map; + size_t size = sizeof(*map) + sizeof(map->ranges[0]) * count;
- map = malloc(sizeof(*map) + sizeof(map->ranges[0]) * count); + map = calloc(1, size); if (!map) return NULL; map->range_count = count;

On Thu, Sep 24, 2020 at 10:04:11AM +0530, Pratyush Yadav wrote:
Some fields will be introduced in the regmap structure that should be set to 0 by default. So, once we allocate a regmap, make sure it is zeroed out to avoid unexpected defaults for those values.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/next, thanks!

Right now, regmap_read() and regmap_write() read/write a 32-bit value only. To write other lengths, regmap_raw_read() and regmap_raw_write() need to be used.
This means that any driver ported from Linux that relies on regmap_{read,write}() to know the size already has to be updated at each callsite. This makes the port harder to maintain.
So, allow specifying the read/write width to make it easier to port the drivers, since now the only change needed is when initializing the regmap.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org ---
Notes: No changes in v3.
drivers/core/regmap.c | 15 ++++++++++++--- include/regmap.h | 38 ++++++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 19 deletions(-)
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 809f58489f..c71b961234 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -25,6 +25,10 @@ DECLARE_GLOBAL_DATA_PTR; * regmap_alloc() - Allocate a regmap with a given number of ranges. * * @count: Number of ranges to be allocated for the regmap. + * + * The default regmap width is set to REGMAP_SIZE_32. Callers can override it + * if they need. + * * Return: A pointer to the newly allocated regmap, or NULL on error. */ static struct regmap *regmap_alloc(int count) @@ -36,6 +40,7 @@ static struct regmap *regmap_alloc(int count) if (!map) return NULL; map->range_count = count; + map->width = REGMAP_SIZE_32;
return map; } @@ -244,7 +249,7 @@ struct regmap *devm_regmap_init(struct udevice *dev, const struct regmap_config *config) { int rc; - struct regmap **mapp; + struct regmap **mapp, *map;
mapp = devres_alloc(devm_regmap_release, sizeof(struct regmap *), __GFP_ZERO); @@ -255,6 +260,10 @@ struct regmap *devm_regmap_init(struct udevice *dev, if (rc) return ERR_PTR(rc);
+ map = *mapp; + if (config) + map->width = config->width; + devres_add(dev, mapp); return *mapp; } @@ -377,7 +386,7 @@ int regmap_raw_read(struct regmap *map, uint offset, void *valp, size_t val_len)
int regmap_read(struct regmap *map, uint offset, uint *valp) { - return regmap_raw_read(map, offset, valp, REGMAP_SIZE_32); + return regmap_raw_read(map, offset, valp, map->width); }
static inline void __write_8(u8 *addr, const u8 *val, @@ -487,7 +496,7 @@ int regmap_raw_write(struct regmap *map, uint offset, const void *val,
int regmap_write(struct regmap *map, uint offset, uint val) { - return regmap_raw_write(map, offset, &val, REGMAP_SIZE_32); + return regmap_raw_write(map, offset, &val, map->width); }
int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val) diff --git a/include/regmap.h b/include/regmap.h index c7dd240a74..19474e6de1 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -76,16 +76,28 @@ struct regmap_range { };
struct regmap_bus; -struct regmap_config; + +/** + * struct regmap_config - Configure the behaviour of a regmap + * + * @width: Width of the read/write operations. Defaults to + * REGMAP_SIZE_32 if set to 0. + */ +struct regmap_config { + enum regmap_size_t width; +};
/** * struct regmap - a way of accessing hardware/bus registers * + * @width: Width of the read/write operations. Defaults to + * REGMAP_SIZE_32 if set to 0. * @range_count: Number of ranges available within the map * @ranges: Array of ranges */ struct regmap { enum regmap_endianness_t endianness; + enum regmap_size_t width; int range_count; struct regmap_range ranges[0]; }; @@ -96,32 +108,24 @@ struct regmap { */
/** - * regmap_write() - Write a 32-bit value to a regmap + * regmap_write() - Write a value to a regmap * * @map: Regmap to write to * @offset: Offset in the regmap to write to * @val: Data to write to the regmap at the specified offset * - * Note that this function will only write values of 32 bit width to the - * regmap; if the size of data to be read is different, the regmap_raw_write - * function can be used. - * * Return: 0 if OK, -ve on error */ int regmap_write(struct regmap *map, uint offset, uint val);
/** - * regmap_read() - Read a 32-bit value from a regmap + * regmap_read() - Read a value from a regmap * * @map: Regmap to read from * @offset: Offset in the regmap to read from * @valp: Pointer to the buffer to receive the data read from the regmap * at the specified offset * - * Note that this function will only read values of 32 bit width from the - * regmap; if the size of data to be read is different, the regmap_raw_read - * function can be used. - * * Return: 0 if OK, -ve on error */ int regmap_read(struct regmap *map, uint offset, uint *valp); @@ -135,8 +139,9 @@ int regmap_read(struct regmap *map, uint offset, uint *valp); * @val_len: Length of the data to be written to the regmap * * Note that this function will, as opposed to regmap_write, write data of - * arbitrary length to the regmap, and not just 32-bit values, and is thus a - * generalized version of regmap_write. + * arbitrary length to the regmap, and not just the size configured in the + * regmap (defaults to 32-bit) and is thus a generalized version of + * regmap_write. * * Return: 0 if OK, -ve on error */ @@ -153,8 +158,9 @@ int regmap_raw_write(struct regmap *map, uint offset, const void *val, * @val_len: Length of the data to be read from the regmap * * Note that this function will, as opposed to regmap_read, read data of - * arbitrary length from the regmap, and not just 32-bit values, and is thus a - * generalized version of regmap_read. + * arbitrary length from the regmap, and not just the size configured in the + * regmap (defaults to 32-bit) and is thus a generalized version of + * regmap_read. * * Return: 0 if OK, -ve on error */ @@ -344,7 +350,7 @@ int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index); * @dev: Device that will be interacted with * @bus: Bus-specific callbacks to use with device (IGNORED) * @bus_context: Data passed to bus-specific callbacks (IGNORED) - * @config: Configuration for register map (IGNORED) + * @config: Configuration for register map * * @Return a valid pointer to a struct regmap or a ERR_PTR() on error. * The structure is automatically freed when the device is unbound

On Thu, Sep 24, 2020 at 10:04:12AM +0530, Pratyush Yadav wrote:
Right now, regmap_read() and regmap_write() read/write a 32-bit value only. To write other lengths, regmap_raw_read() and regmap_raw_write() need to be used.
This means that any driver ported from Linux that relies on regmap_{read,write}() to know the size already has to be updated at each callsite. This makes the port harder to maintain.
So, allow specifying the read/write width to make it easier to port the drivers, since now the only change needed is when initializing the regmap.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/next, thanks!

Drivers can configure it to adjust the final read/write location.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org ---
Notes: No changes in v3.
drivers/core/regmap.c | 6 +++++- include/regmap.h | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index c71b961234..173ae80890 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -261,8 +261,10 @@ struct regmap *devm_regmap_init(struct udevice *dev, return ERR_PTR(rc);
map = *mapp; - if (config) + if (config) { map->width = config->width; + map->reg_offset_shift = config->reg_offset_shift; + }
devres_add(dev, mapp); return *mapp; @@ -349,6 +351,7 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, } range = &map->ranges[range_num];
+ offset <<= map->reg_offset_shift; if (offset + val_len > range->size) { debug("%s: offset/size combination invalid\n", __func__); return -ERANGE; @@ -458,6 +461,7 @@ int regmap_raw_write_range(struct regmap *map, uint range_num, uint offset, } range = &map->ranges[range_num];
+ offset <<= map->reg_offset_shift; if (offset + val_len > range->size) { debug("%s: offset/size combination invalid\n", __func__); return -ERANGE; diff --git a/include/regmap.h b/include/regmap.h index 19474e6de1..e6c59dfbce 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -82,9 +82,12 @@ struct regmap_bus; * * @width: Width of the read/write operations. Defaults to * REGMAP_SIZE_32 if set to 0. + * @reg_offset_shift Left shift the register offset by this value before + * performing read or write. */ struct regmap_config { enum regmap_size_t width; + u32 reg_offset_shift; };
/** @@ -92,12 +95,15 @@ struct regmap_config { * * @width: Width of the read/write operations. Defaults to * REGMAP_SIZE_32 if set to 0. + * @reg_offset_shift Left shift the register offset by this value before + * performing read or write. * @range_count: Number of ranges available within the map * @ranges: Array of ranges */ struct regmap { enum regmap_endianness_t endianness; enum regmap_size_t width; + u32 reg_offset_shift; int range_count; struct regmap_range ranges[0]; };

On Thu, Sep 24, 2020 at 10:04:13AM +0530, Pratyush Yadav wrote:
Drivers can configure it to adjust the final read/write location.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/next, thanks!

Right now, the base of a regmap can only be obtained from the device tree. This makes it impossible for devices which calculate the base at runtime to use a regmap. An example of such a device is the Cadence Sierra PHY.
Allow creating a regmap with one range whose start and size can be specified by the driver based on calculations at runtime.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org ---
Notes: No changes in v3.
drivers/core/regmap.c | 27 +++++++++++++++++++++++++++ include/regmap.h | 19 +++++++++++++++++++ 2 files changed, 46 insertions(+)
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index 173ae80890..a9087df32b 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -164,6 +164,33 @@ err: return ret; }
+int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size, + struct regmap **mapp) +{ + struct regmap *map; + struct regmap_range *range; + + map = regmap_alloc(1); + if (!map) + return -ENOMEM; + + range = &map->ranges[0]; + range->start = r_start; + range->size = r_size; + + if (ofnode_read_bool(node, "little-endian")) + map->endianness = REGMAP_LITTLE_ENDIAN; + else if (ofnode_read_bool(node, "big-endian")) + map->endianness = REGMAP_BIG_ENDIAN; + else if (ofnode_read_bool(node, "native-endian")) + map->endianness = REGMAP_NATIVE_ENDIAN; + else /* Default: native endianness */ + map->endianness = REGMAP_NATIVE_ENDIAN; + + *mapp = map; + return 0; +} + int regmap_init_mem(ofnode node, struct regmap **mapp) { struct regmap_range *range; diff --git a/include/regmap.h b/include/regmap.h index e6c59dfbce..7c8ad04759 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -350,6 +350,25 @@ int regmap_init_mem_platdata(struct udevice *dev, fdt_val_t *reg, int count,
int regmap_init_mem_index(ofnode node, struct regmap **mapp, int index);
+/** + * regmap_init_mem_range() - Set up a new memory region for ofnode with the + * specified range. + * + * @node: The ofnode for the map. + * @r_start: Start of the range. + * @r_size: Size of the range. + * @mapp: Returns allocated map. + * + * Return: 0 in success, -errno otherwise + * + * This creates a regmap with one range where instead of extracting the range + * from 'node', it is created based on the parameters specified. This is + * useful when a driver needs to calculate the base of the regmap at runtime, + * and can't specify it in device tree. + */ +int regmap_init_mem_range(ofnode node, ulong r_start, ulong r_size, + struct regmap **mapp); + /** * devm_regmap_init() - Initialise register map (device managed) *

On Thu, Sep 24, 2020 at 10:04:14AM +0530, Pratyush Yadav wrote:
Right now, the base of a regmap can only be obtained from the device tree. This makes it impossible for devices which calculate the base at runtime to use a regmap. An example of such a device is the Cadence Sierra PHY.
Allow creating a regmap with one range whose start and size can be specified by the driver based on calculations at runtime.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/next, thanks!

Some devices need to calculate the regmap base address at runtime. This makes it impossible to use device tree to get the regmap base. Instead, allow devices to specify it in the regmap config. This will create a regmap with a single range that corresponds to the start and size given by the driver.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org ---
Notes: No changes in v3.
drivers/core/regmap.c | 6 +++++- include/regmap.h | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index a9087df32b..a3da0cf7c3 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -283,7 +283,11 @@ struct regmap *devm_regmap_init(struct udevice *dev, if (unlikely(!mapp)) return ERR_PTR(-ENOMEM);
- rc = regmap_init_mem(dev_ofnode(dev), mapp); + if (config && config->r_size != 0) + rc = regmap_init_mem_range(dev_ofnode(dev), config->r_start, + config->r_size, mapp); + else + rc = regmap_init_mem(dev_ofnode(dev), mapp); if (rc) return ERR_PTR(rc);
diff --git a/include/regmap.h b/include/regmap.h index 7c8ad04759..7a6fcc7f53 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -84,10 +84,16 @@ struct regmap_bus; * REGMAP_SIZE_32 if set to 0. * @reg_offset_shift Left shift the register offset by this value before * performing read or write. + * @r_start: If specified, the regmap is created with one range + * which starts at this address, instead of finding the + * start from device tree. + * @r_size: Same as above for the range size */ struct regmap_config { enum regmap_size_t width; u32 reg_offset_shift; + ulong r_start; + ulong r_size; };
/**

On Thu, Sep 24, 2020 at 10:04:15AM +0530, Pratyush Yadav wrote:
Some devices need to calculate the regmap base address at runtime. This makes it impossible to use device tree to get the regmap base. Instead, allow devices to specify it in the regmap config. This will create a regmap with a single range that corresponds to the start and size given by the driver.
Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/next, thanks!

From: Jean-Jacques Hiblot jjhiblot@ti.com
A regmap field is an abstraction available in Linux. It provides to access bitfields in a regmap without having to worry about shifts and masks.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Pratyush Yadav p.yadav@ti.com ---
Notes: No changes in v3.
drivers/core/regmap.c | 83 ++++++++++++++++++++++++++++ include/regmap.h | 122 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+)
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c index a3da0cf7c3..c2bed88eac 100644 --- a/drivers/core/regmap.c +++ b/drivers/core/regmap.c @@ -18,6 +18,20 @@ #include <linux/ioport.h> #include <linux/compat.h> #include <linux/err.h> +#include <linux/bitops.h> + +/* + * Internal representation of a regmap field. Instead of storing the MSB and + * LSB, store the shift and mask. This makes the code a bit cleaner and faster + * because the shift and mask don't have to be calculated every time. + */ +struct regmap_field { + struct regmap *regmap; + unsigned int mask; + /* lsb */ + unsigned int shift; + unsigned int reg; +};
DECLARE_GLOBAL_DATA_PTR;
@@ -547,3 +561,72 @@ int regmap_update_bits(struct regmap *map, uint offset, uint mask, uint val)
return regmap_write(map, offset, reg | (val & mask)); } + +int regmap_field_read(struct regmap_field *field, unsigned int *val) +{ + int ret; + unsigned int reg_val; + + ret = regmap_read(field->regmap, field->reg, ®_val); + if (ret != 0) + return ret; + + reg_val &= field->mask; + reg_val >>= field->shift; + *val = reg_val; + + return ret; +} + +int regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_update_bits(field->regmap, field->reg, field->mask, + val << field->shift); +} + +static void regmap_field_init(struct regmap_field *rm_field, + struct regmap *regmap, + struct reg_field reg_field) +{ + rm_field->regmap = regmap; + rm_field->reg = reg_field.reg; + rm_field->shift = reg_field.lsb; + rm_field->mask = GENMASK(reg_field.msb, reg_field.lsb); +} + +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, + struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = devm_kzalloc(dev, sizeof(*rm_field), + GFP_KERNEL); + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field) +{ + devm_kfree(dev, field); +} + +struct regmap_field *regmap_field_alloc(struct regmap *regmap, + struct reg_field reg_field) +{ + struct regmap_field *rm_field = kzalloc(sizeof(*rm_field), GFP_KERNEL); + + if (!rm_field) + return ERR_PTR(-ENOMEM); + + regmap_field_init(rm_field, regmap, reg_field); + + return rm_field; +} + +void regmap_field_free(struct regmap_field *field) +{ + kfree(field); +} diff --git a/include/regmap.h b/include/regmap.h index 7a6fcc7f53..c6258face3 100644 --- a/include/regmap.h +++ b/include/regmap.h @@ -312,6 +312,43 @@ int regmap_raw_read_range(struct regmap *map, uint range_num, uint offset, regmap_read_poll_timeout_test(map, addr, val, cond, sleep_us, \ timeout_ms, 0) \
+/** + * regmap_field_read_poll_timeout - Poll until a condition is met or a timeout + * occurs + * + * @field: Regmap field to read from + * @val: Unsigned integer variable to read the value into + * @cond: Break condition (usually involving @val) + * @sleep_us: Maximum time to sleep between reads in us (0 tight-loops). + * @timeout_ms: Timeout in ms, 0 means never timeout + * + * Returns 0 on success and -ETIMEDOUT upon a timeout or the regmap_field_read + * error return value in case of a error read. In the two former cases, + * the last read value at @addr is stored in @val. + * + * This is modelled after the regmap_read_poll_timeout macros in linux but + * with millisecond timeout. + */ +#define regmap_field_read_poll_timeout(field, val, cond, sleep_us, timeout_ms) \ +({ \ + unsigned long __start = get_timer(0); \ + int __ret; \ + for (;;) { \ + __ret = regmap_field_read((field), &(val)); \ + if (__ret) \ + break; \ + if (cond) \ + break; \ + if ((timeout_ms) && get_timer(__start) > (timeout_ms)) { \ + __ret = regmap_field_read((field), &(val)); \ + break; \ + } \ + if ((sleep_us)) \ + udelay((sleep_us)); \ + } \ + __ret ?: ((cond) ? 0 : -ETIMEDOUT); \ +}) + /** * regmap_update_bits() - Perform a read/modify/write using a mask * @@ -407,4 +444,89 @@ void *regmap_get_range(struct regmap *map, unsigned int range_num); */ int regmap_uninit(struct regmap *map);
+/** + * struct reg_field - Description of an register field + * + * @reg: Offset of the register within the regmap bank + * @lsb: lsb of the register field. + * @msb: msb of the register field. + */ +struct reg_field { + unsigned int reg; + unsigned int lsb; + unsigned int msb; +}; + +struct regmap_field; + +/** + * REG_FIELD() - A convenient way to initialize a 'struct reg_feild'. + * + * @_reg: Offset of the register within the regmap bank + * @_lsb: lsb of the register field. + * @_msb: msb of the register field. + * + * Register fields are often described in terms of 3 things: the register it + * belongs to, its LSB, and its MSB. This macro can be used by drivers to + * clearly and easily initialize a 'struct regmap_field'. + * + * For example, say a device has a register at offset DEV_REG1 (0x100) and a + * field of DEV_REG1 is on bits [7:3]. So a driver can initialize a regmap + * field for this by doing: + * struct reg_field field = REG_FIELD(DEV_REG1, 3, 7); + */ +#define REG_FIELD(_reg, _lsb, _msb) { \ + .reg = _reg, \ + .lsb = _lsb, \ + .msb = _msb, \ + } + +/** + * devm_regmap_field_alloc() - Allocate and initialise a register field. + * + * @dev: Device that will be interacted with + * @regmap: regmap bank in which this register field is located. + * @reg_field: Register field with in the bank. + * + * The return value will be an ERR_PTR() on error or a valid pointer + * to a struct regmap_field. The regmap_field will be automatically freed + * by the device management code. + */ +struct regmap_field *devm_regmap_field_alloc(struct udevice *dev, + struct regmap *regmap, + struct reg_field reg_field); +/** + * devm_regmap_field_free() - Free a register field allocated using + * devm_regmap_field_alloc. + * + * @dev: Device that will be interacted with + * @field: regmap field which should be freed. + * + * Free register field allocated using devm_regmap_field_alloc(). Usually + * drivers need not call this function, as the memory allocated via devm + * will be freed as per device-driver life-cyle. + */ +void devm_regmap_field_free(struct udevice *dev, struct regmap_field *field); + +/** + * regmap_field_write() - Write a value to a regmap field + * + * @field: Regmap field to write to + * @val: Data to write to the regmap at the specified offset + * + * Return: 0 if OK, -ve on error + */ +int regmap_field_write(struct regmap_field *field, unsigned int val); + +/** + * regmap_read() - Read a 32-bit value from a regmap + * + * @field: Regmap field to write to + * @valp: Pointer to the buffer to receive the data read from the regmap + * field + * + * Return: 0 if OK, -ve on error + */ +int regmap_field_read(struct regmap_field *field, unsigned int *val); + #endif

On Thu, Sep 24, 2020 at 10:04:16AM +0530, Pratyush Yadav wrote:
From: Jean-Jacques Hiblot jjhiblot@ti.com
A regmap field is an abstraction available in Linux. It provides to access bitfields in a regmap without having to worry about shifts and masks.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Reviewed-by: Simon Glass sjg@chromium.org Signed-off-by: Pratyush Yadav p.yadav@ti.com
Applied to u-boot/next, thanks!

When more nodes are added for a uclass the index might go into two or more digits. This means that there are less spaces printed because they are used up by the extra digits. Update the regular expression to allow variable-length spacing between the class name and and index.
This was discovered when adding a simple_bus node in test.dts made test_bind_unbind_with_uclass() fail because the index went up to 10.
Signed-off-by: Pratyush Yadav p.yadav@ti.com ---
Notes: New in v3.
test/py/tests/test_bind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/py/tests/test_bind.py b/test/py/tests/test_bind.py index 5e73d40361..6703325c0b 100644 --- a/test/py/tests/test_bind.py +++ b/test/py/tests/test_bind.py @@ -16,7 +16,7 @@ def in_tree(response, name, uclass, drv, depth, last_child): leaf = leaf + '`'
leaf = leaf + '-- ' + name - line = (r' *{:10.10} [0-9]* [ [ +] ] {:20.20} [` |]{}$' + line = (r' *{:10.10} *[0-9]* [ [ +] ] {:20.20} [` |]{}$' .format(uclass, drv, leaf)) prog = re.compile(line) for l in lines:

On Thu, Sep 24, 2020 at 10:04:17AM +0530, Pratyush Yadav wrote:
When more nodes are added for a uclass the index might go into two or more digits. This means that there are less spaces printed because they are used up by the extra digits. Update the regular expression to allow variable-length spacing between the class name and and index.
This was discovered when adding a simple_bus node in test.dts made test_bind_unbind_with_uclass() fail because the index went up to 10.
Signed-off-by: Pratyush Yadav p.yadav@ti.com
Applied to u-boot/next, thanks!

From: Jean-Jacques Hiblot jjhiblot@ti.com
The tests rely on a dummy driver to allocate and initialize the regmaps and the regmap fields using the managed API. The first test checks if the regmap config fields like width, reg_offset_shift, range specifiers, etc work. The second test checks if regmap fields behave properly (mask and shift are ok) by peeking into the regmap.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org ---
Notes: Changes in v3:
- s/DM_TESTF/UT_TESTF/g - Fix merge conflicts in test.dts
arch/sandbox/dts/test.dts | 13 +++ test/dm/regmap.c | 198 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 211 insertions(+)
diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 9f45c48e4e..52215f83f6 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1122,6 +1122,19 @@ resets = <&resetc2 15>, <&resetc2 30>, <&resetc2 60>; reset-names = "valid", "no_mask", "out_of_range"; }; + + some_regmapped-bus { + #address-cells = <0x1>; + #size-cells = <0x1>; + + ranges = <0x0 0x0 0x10>; + compatible = "simple-bus"; + + regmap-test_0 { + reg = <0 0x10>; + compatible = "sandbox,regmap_test"; + }; + }; };
#include "sandbox_pmic.dtsi" diff --git a/test/dm/regmap.c b/test/dm/regmap.c index bd21c8365d..2effef3c1c 100644 --- a/test/dm/regmap.c +++ b/test/dm/regmap.c @@ -9,8 +9,10 @@ #include <mapmem.h> #include <regmap.h> #include <syscon.h> +#include <rand.h> #include <asm/test.h> #include <dm/test.h> +#include <dm/devres.h> #include <linux/err.h> #include <test/test.h> #include <test/ut.h> @@ -187,3 +189,199 @@ static int dm_test_regmap_poll(struct unit_test_state *uts) }
DM_TEST(dm_test_regmap_poll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +struct regmaptest_priv { + struct regmap *cfg_regmap; /* For testing regmap_config options. */ + struct regmap *fld_regmap; /* For testing regmap fields. */ + struct regmap_field **fields; +}; + +static const struct reg_field field_cfgs[] = { + { + .reg = 0, + .lsb = 0, + .msb = 6, + }, + { + .reg = 2, + .lsb = 4, + .msb = 12, + }, + { + .reg = 2, + .lsb = 12, + .msb = 15, + } +}; + +#define REGMAP_TEST_BUF_START 0 +#define REGMAP_TEST_BUF_SZ 5 + +static int remaptest_probe(struct udevice *dev) +{ + struct regmaptest_priv *priv = dev_get_priv(dev); + struct regmap *regmap; + struct regmap_field *field; + struct regmap_config cfg; + int i; + static const int n = ARRAY_SIZE(field_cfgs); + + /* + * To exercise all the regmap config options, create a regmap that + * points to a custom memory area instead of the one defined in device + * tree. Use 2-byte elements. To allow directly indexing into the + * elements, use an offset shift of 1. So, accessing offset 1 gets the + * element at index 1 at memory location 2. + * + * REGMAP_TEST_BUF_SZ is the number of elements, so we need to multiply + * it by 2 because r_size expects number of bytes. + */ + cfg.reg_offset_shift = 1; + cfg.r_start = REGMAP_TEST_BUF_START; + cfg.r_size = REGMAP_TEST_BUF_SZ * 2; + cfg.width = REGMAP_SIZE_16; + + regmap = devm_regmap_init(dev, NULL, NULL, &cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + priv->cfg_regmap = regmap; + + memset(&cfg, 0, sizeof(struct regmap_config)); + cfg.width = REGMAP_SIZE_16; + + regmap = devm_regmap_init(dev, NULL, NULL, &cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + priv->fld_regmap = regmap; + + priv->fields = devm_kzalloc(dev, sizeof(struct regmap_field *) * n, + GFP_KERNEL); + if (!priv->fields) + return -ENOMEM; + + for (i = 0 ; i < n; i++) { + field = devm_regmap_field_alloc(dev, priv->fld_regmap, + field_cfgs[i]); + if (IS_ERR(field)) + return PTR_ERR(field); + priv->fields[i] = field; + } + + return 0; +} + +static const struct udevice_id regmaptest_ids[] = { + { .compatible = "sandbox,regmap_test" }, + { } +}; + +U_BOOT_DRIVER(regmap_test) = { + .name = "regmaptest_drv", + .of_match = regmaptest_ids, + .id = UCLASS_NOP, + .probe = remaptest_probe, + .priv_auto_alloc_size = sizeof(struct regmaptest_priv), +}; + +static int dm_test_devm_regmap(struct unit_test_state *uts) +{ + int i = 0; + u32 val; + u16 pattern[REGMAP_TEST_BUF_SZ]; + u16 *buffer; + struct udevice *dev; + struct regmaptest_priv *priv; + + sandbox_set_enable_memio(true); + + /* + * Map the memory area the regmap should point to so we can make sure + * the writes actually go to that location. + */ + buffer = map_physmem(REGMAP_TEST_BUF_START, + REGMAP_TEST_BUF_SZ * 2, MAP_NOCACHE); + + ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0", + &dev)); + priv = dev_get_priv(dev); + + srand(get_ticks() + rand()); + for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) { + pattern[i] = rand(); + ut_assertok(regmap_write(priv->cfg_regmap, i, pattern[i])); + } + for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) { + ut_assertok(regmap_read(priv->cfg_regmap, i, &val)); + ut_asserteq(val, buffer[i]); + ut_asserteq(val, pattern[i]); + } + + ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, REGMAP_TEST_BUF_SZ, + val)); + ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, REGMAP_TEST_BUF_SZ, + &val)); + ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, -1, val)); + ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, -1, &val)); + + return 0; +} +DM_TEST(dm_test_devm_regmap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int test_one_field(struct unit_test_state *uts, + struct regmap *regmap, + struct regmap_field *field, + struct reg_field field_cfg) +{ + int j; + unsigned int val; + int mask = (1 << (field_cfg.msb - field_cfg.lsb + 1)) - 1; + int shift = field_cfg.lsb; + + ut_assertok(regmap_write(regmap, field_cfg.reg, 0)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(0, val); + + for (j = 0; j <= mask; j++) { + ut_assertok(regmap_field_write(field, j)); + ut_assertok(regmap_field_read(field, &val)); + ut_asserteq(j, val); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(j << shift, val); + } + + ut_assertok(regmap_field_write(field, mask + 1)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(0, val); + + ut_assertok(regmap_field_write(field, 0xFFFF)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(mask << shift, val); + + ut_assertok(regmap_write(regmap, field_cfg.reg, 0xFFFF)); + ut_assertok(regmap_field_write(field, 0)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(0xFFFF & ~(mask << shift), val); + return 0; +} + +static int dm_test_devm_regmap_field(struct unit_test_state *uts) +{ + int i, rc; + struct udevice *dev; + struct regmaptest_priv *priv; + + ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0", + &dev)); + priv = dev_get_priv(dev); + + sandbox_set_enable_memio(true); + for (i = 0 ; i < ARRAY_SIZE(field_cfgs); i++) { + rc = test_one_field(uts, priv->fld_regmap, priv->fields[i], + field_cfgs[i]); + if (rc) + break; + } + + return 0; +} +DM_TEST(dm_test_devm_regmap_field, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);

On Thu, Sep 24, 2020 at 10:04:18AM +0530, Pratyush Yadav wrote:
From: Jean-Jacques Hiblot jjhiblot@ti.com
The tests rely on a dummy driver to allocate and initialize the regmaps and the regmap fields using the managed API. The first test checks if the regmap config fields like width, reg_offset_shift, range specifiers, etc work. The second test checks if regmap fields behave properly (mask and shift are ok) by peeking into the regmap.
Signed-off-by: Jean-Jacques Hiblot jjhiblot@ti.com Signed-off-by: Pratyush Yadav p.yadav@ti.com Reviewed-by: Simon Glass sjg@chromium.org
Applied to u-boot/next, thanks!

On 24/09/20 10:04AM, Pratyush Yadav wrote:
Hi,
This series is a re-spin of Jean-Jacques' earlier effort [0], the goal of which was to facilitate porting drivers from the Linux kernel. It adds the managed API, using the same API as Linux. It also adds support for regmap fields.
Jean-Jacques' series added support for custom regmap read/write callbacks. The design was argued against by Simon [1]. He argued that using the driver model was a better idea instead of the custom functions. That would mean slightly more memory usage and a more involved change.
The main aim of adding the custom functions is to support the Cadence Sierra PHY driver from Linux [2]. The driver's custom functions aren't very complicated, so they can be replaced by some simple formatting options. So, add the struct regmap_config which contains fields to alter the behaviour of the regmap. This includes specifying the size of the read/write operations via 'width', specifying the start and size of range from code instead of device tree via 'r_start' and 'r_size', and specifying a left shift of the register offset before access via 'reg_offset_shift'. The driver can't be ported verbatim now, but this allows the changes to be very isolated and minimal.
These config options allow us to avoid converting to driver model until we really need it.
The Travis CI run can be found at [3].
[0] https://patchwork.ozlabs.org/project/uboot/cover/20191105114700.24989-1-jjhi... [1] https://patchwork.ozlabs.org/comment/2426186/ [2] https://elixir.bootlin.com/linux/latest/source/drivers/phy/cadence/phy-caden... [3] https://travis-ci.org/github/prati0100/uboot/builds/729588518
Ping.
Tom, do you have any comments? If not can you please pick this series up?
Changes in v3 (also mentioned in individual patches):
Allow multi-digit index in in_tree(). Fixes test failures in test/bind.py
s/DM_TESTF/UT_TESTF/g
Fix merge conflicts in test.dts
Rebase on latest master.
Changes in v2:
Add comments explaining the need for regmap_field.
Set regmap->width to a default of REGMAP_SIZE_32 in regmap_alloc() to avoid the checks in regmap_{read,write}().
Use calloc() instead of using malloc() and memset() to initialize a regmap to zero.
Update comments on regmap_{read,write}() and regmap_raw_{read,write}().
Drop comments explaining two non-existent fields in struct reg_field.
Add a comment with example explaining REG_FIELD().
Add Simon's Reviewed-by trailers.
Jean-Jacques Hiblot (3): regmap: Add devm_regmap_init() regmap: Add support for regmap fields test: dm: Add tests for regmap managed API and regmap fields
Pratyush Yadav (6): regmap: zero out the regmap on allocation regmap: Allow specifying read/write width regmap: Allow left shifting register offset before access regmap: Add regmap_init_mem_range() regmap: Allow devices to specify regmap range start and size in config test/py: allow multi-digit index in in_tree()
arch/sandbox/dts/test.dts | 13 +++ drivers/core/regmap.c | 163 ++++++++++++++++++++++++++++- include/regmap.h | 205 ++++++++++++++++++++++++++++++++++--- test/dm/regmap.c | 198 +++++++++++++++++++++++++++++++++++ test/py/tests/test_bind.py | 2 +- 5 files changed, 563 insertions(+), 18 deletions(-)
-- 2.28.0

On Wed, Sep 30, 2020 at 07:28:42PM +0530, Pratyush Yadav wrote:
On 24/09/20 10:04AM, Pratyush Yadav wrote:
Hi,
This series is a re-spin of Jean-Jacques' earlier effort [0], the goal of which was to facilitate porting drivers from the Linux kernel. It adds the managed API, using the same API as Linux. It also adds support for regmap fields.
Jean-Jacques' series added support for custom regmap read/write callbacks. The design was argued against by Simon [1]. He argued that using the driver model was a better idea instead of the custom functions. That would mean slightly more memory usage and a more involved change.
The main aim of adding the custom functions is to support the Cadence Sierra PHY driver from Linux [2]. The driver's custom functions aren't very complicated, so they can be replaced by some simple formatting options. So, add the struct regmap_config which contains fields to alter the behaviour of the regmap. This includes specifying the size of the read/write operations via 'width', specifying the start and size of range from code instead of device tree via 'r_start' and 'r_size', and specifying a left shift of the register offset before access via 'reg_offset_shift'. The driver can't be ported verbatim now, but this allows the changes to be very isolated and minimal.
These config options allow us to avoid converting to driver model until we really need it.
The Travis CI run can be found at [3].
[0] https://patchwork.ozlabs.org/project/uboot/cover/20191105114700.24989-1-jjhi... [1] https://patchwork.ozlabs.org/comment/2426186/ [2] https://elixir.bootlin.com/linux/latest/source/drivers/phy/cadence/phy-caden... [3] https://travis-ci.org/github/prati0100/uboot/builds/729588518
Ping.
Tom, do you have any comments? If not can you please pick this series up?
I will review this and the gpio/reset APIs for next shortly.
participants (2)
-
Pratyush Yadav
-
Tom Rini