From patchwork Fri Jun 5 20:30:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241832 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:18 +0530 Subject: [PATCH v2 1/8] regmap: Add devm_regmap_init() In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-2-p.yadav@ti.com> From: Jean-Jacques Hiblot 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 Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav --- 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 #include #include +#include #include +#include +#include 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 * From patchwork Fri Jun 5 20:30:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241834 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:19 +0530 Subject: [PATCH v2 2/8] regmap: zero out the regmap on allocation In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-3-p.yadav@ti.com> 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 Reviewed-by: Simon Glass --- 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; From patchwork Fri Jun 5 20:30:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241835 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:20 +0530 Subject: [PATCH v2 3/8] regmap: Allow specifying read/write width In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-4-p.yadav@ti.com> 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 Reviewed-by: Simon Glass --- 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 From patchwork Fri Jun 5 20:30:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241836 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:21 +0530 Subject: [PATCH v2 4/8] regmap: Allow left shifting register offset before access In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-5-p.yadav@ti.com> Drivers can configure it to adjust the final read/write location. Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- 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]; }; From patchwork Fri Jun 5 20:30:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241837 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:22 +0530 Subject: [PATCH v2 5/8] regmap: Add regmap_init_mem_range() In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-6-p.yadav@ti.com> 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 Reviewed-by: Simon Glass --- 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) * From patchwork Fri Jun 5 20:30:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241838 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:23 +0530 Subject: [PATCH v2 6/8] regmap: Allow devices to specify regmap range start and size in config In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-7-p.yadav@ti.com> 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 Reviewed-by: Simon Glass --- 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; }; /** From patchwork Fri Jun 5 20:30:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241839 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:24 +0530 Subject: [PATCH v2 7/8] regmap: Add support for regmap fields In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-8-p.yadav@ti.com> From: Jean-Jacques Hiblot 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 Reviewed-by: Simon Glass Signed-off-by: Pratyush Yadav --- 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 #include #include +#include + +/* + * 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 From patchwork Fri Jun 5 20:30:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pratyush Yadav X-Patchwork-Id: 241840 List-Id: U-Boot discussion From: p.yadav at ti.com (Pratyush Yadav) Date: Sat, 6 Jun 2020 02:00:25 +0530 Subject: [PATCH v2 8/8] test: dm: Add tests for regmap managed API and regmap fields In-Reply-To: <20200605203025.15466-1-p.yadav@ti.com> References: <20200605203025.15466-1-p.yadav@ti.com> Message-ID: <20200605203025.15466-9-p.yadav@ti.com> From: Jean-Jacques Hiblot 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 Signed-off-by: Pratyush Yadav Reviewed-by: Simon Glass --- 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 5ce5e28476..5b2c6ca79f 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -1029,6 +1029,19 @@ mdio: mdio-test { compatible = "sandbox,mdio"; }; + + 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 809494d585..3bd197a94a 100644 --- a/test/dm/regmap.c +++ b/test/dm/regmap.c @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -186,3 +188,199 @@ static int dm_test_regmap_poll(struct unit_test_state *uts) } DM_TEST(dm_test_regmap_poll, DM_TESTF_SCAN_PDATA | DM_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, DM_TESTF_SCAN_PDATA | DM_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, DM_TESTF_SCAN_PDATA | DM_TESTF_SCAN_FDT);