diff options
Diffstat (limited to 'drivers/base/regmap/regmap.c')
-rw-r--r-- | drivers/base/regmap/regmap.c | 204 |
1 files changed, 144 insertions, 60 deletions
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index dc7b4f276f..7ad527954c 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Register map access API * @@ -8,19 +9,10 @@ * Copyright 2011 Wolfson Microelectronics plc * * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; version 2. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. */ #include <common.h> -#include <regmap.h> +#include <linux/regmap.h> #include <malloc.h> #include <linux/log2.h> @@ -28,6 +20,67 @@ static LIST_HEAD(regmaps); +enum regmap_endian regmap_get_val_endian(struct device *dev, + const struct regmap_bus *bus, + const struct regmap_config *config) +{ + struct device_node *np; + enum regmap_endian endian; + + /* Retrieve the endianness specification from the regmap config */ + endian = config->val_format_endian; + + /* If the regmap config specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* If the dev and dev->device_node exist try to get endianness from DT */ + if (dev && dev->of_node) { + np = dev->of_node; + + /* Parse the device's DT node for an endianness specification */ + if (of_property_read_bool(np, "big-endian")) + endian = REGMAP_ENDIAN_BIG; + else if (of_property_read_bool(np, "little-endian")) + endian = REGMAP_ENDIAN_LITTLE; + else if (of_property_read_bool(np, "native-endian")) + endian = REGMAP_ENDIAN_NATIVE; + + /* If the endianness was specified in DT, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + } + + /* Retrieve the endianness specification from the bus config */ + if (bus && bus->val_format_endian_default) + endian = bus->val_format_endian_default; + + /* If the bus specified a non-default value, use that */ + if (endian != REGMAP_ENDIAN_DEFAULT) + return endian; + + /* Use this if no other value was found */ + return REGMAP_ENDIAN_BIG; +} +EXPORT_SYMBOL_GPL(regmap_get_val_endian); + +static int _regmap_bus_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + struct regmap *map = context; + + return map->bus->reg_read(map->bus_context, reg, val); +} + + +static int _regmap_bus_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct regmap *map = context; + + return map->bus->reg_write(map->bus_context, reg, val); +} + /* * regmap_init - initialize and register a regmap * @@ -38,27 +91,39 @@ static LIST_HEAD(regmaps); * * Returns a pointer to the new map or a ERR_PTR value on failure */ -struct regmap *regmap_init(struct device_d *dev, +struct regmap *regmap_init(struct device *dev, const struct regmap_bus *bus, void *bus_context, const struct regmap_config *config) { struct regmap *map; + int ret; map = xzalloc(sizeof(*map)); map->dev = dev; map->bus = bus; map->name = config->name; map->bus_context = bus_context; - map->reg_bits = config->reg_bits; + map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); map->reg_stride = config->reg_stride; if (!map->reg_stride) map->reg_stride = 1; - map->pad_bits = config->pad_bits; - map->val_bits = config->val_bits; - map->val_bytes = DIV_ROUND_UP(config->val_bits, 8); + map->format.pad_bytes = config->pad_bits / 8; + map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); + map->reg_shift = config->pad_bits % 8; map->max_register = config->max_register; + if (!bus->read || !bus->write) { + map->reg_read = _regmap_bus_reg_read; + map->reg_write = _regmap_bus_reg_write; + } else { + ret = regmap_formatted_init(map, config); + if (ret) { + free(map); + return ERR_PTR(ret); + } + } + list_add_tail(&map->list, ®maps); return map; @@ -72,7 +137,7 @@ struct regmap *regmap_init(struct device_d *dev, * * Returns a pointer to the regmap or a ERR_PTR value on failure */ -struct regmap *dev_get_regmap(struct device_d *dev, const char *name) +struct regmap *dev_get_regmap(struct device *dev, const char *name) { struct regmap *map; @@ -88,24 +153,9 @@ struct regmap *dev_get_regmap(struct device_d *dev, const char *name) return ERR_PTR(-ENOENT); } -/* - * of_node_to_regmap - get a regmap from a device node - * - * node: The device node - * - * Returns a pointer to the regmap or a ERR_PTR if the node has no - * regmap attached. - */ -struct regmap *of_node_to_regmap(struct device_node *node) +struct device *regmap_get_device(struct regmap *map) { - struct regmap *map; - - list_for_each_entry(map, ®maps, list) { - if (map->dev->device_node == node) - return map; - } - - return ERR_PTR(-ENOENT); + return map->dev; } /* @@ -119,7 +169,7 @@ struct regmap *of_node_to_regmap(struct device_node *node) */ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) { - return map->bus->reg_write(map->bus_context, reg, val); + return map->reg_write(map, reg, val); } /* @@ -133,7 +183,7 @@ int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) */ int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) { - return map->bus->reg_read(map->bus_context, reg, val); + return map->reg_read(map, reg, val); } /** @@ -198,27 +248,27 @@ int regmap_write_bits(struct regmap *map, unsigned int reg, * @map: Register map to read from * @reg: First register to be read from * @val: Pointer to store read value - * @val_len: Size of data to read + * @val_count: Number of registers to read * * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, - size_t val_len) + size_t val_count) { - size_t val_bytes = map->val_bytes; - size_t val_count = val_len / val_bytes; unsigned int v; int ret, i; - if (val_len % val_bytes) - return -EINVAL; if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; if (val_count == 0) return -EINVAL; for (i = 0; i < val_count; i++) { + +#ifdef CONFIG_64BIT + u64 *u64 = val; +#endif u32 *u32 = val; u16 *u16 = val; u8 *u8 = val; @@ -227,7 +277,12 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, if (ret != 0) goto out; - switch (map->val_bytes) { + switch (map->format.val_bytes) { +#ifdef CONFIG_64BIT + case 8: + u64[i] = v; + break; +#endif case 4: u32[i] = v; break; @@ -253,20 +308,17 @@ int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, * @reg: Initial register to write to * @val: Block of data to be written, laid out for direct transmission to the * device - * @val_len: Length of data pointed to by val. + * @val_len: Number of registers to write * * A value of zero will be returned on success, a negative errno will * be returned in error cases. */ int regmap_bulk_write(struct regmap *map, unsigned int reg, - const void *val, size_t val_len) + const void *val, size_t val_count) { - size_t val_bytes = map->val_bytes; - size_t val_count = val_len / val_bytes; + size_t val_bytes = map->format.val_bytes; int ret, i; - if (val_len % val_bytes) - return -EINVAL; if (!IS_ALIGNED(reg, map->reg_stride)) return -EINVAL; if (val_count == 0) @@ -285,6 +337,11 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, case 4: ival = *(u32 *)(val + (i * val_bytes)); break; +#ifdef CONFIG_64BIT + case 8: + ival = *(u64 *)(val + (i * val_bytes)); + break; +#endif default: ret = -EINVAL; goto out; @@ -302,7 +359,7 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, int regmap_get_val_bytes(struct regmap *map) { - return map->val_bytes; + return map->format.val_bytes; } int regmap_get_max_register(struct regmap *map) @@ -317,22 +374,18 @@ int regmap_get_reg_stride(struct regmap *map) static int regmap_round_val_bytes(struct regmap *map) { - int val_bytes; - - val_bytes = roundup_pow_of_two(map->val_bits) >> 3; - if (!val_bytes) - val_bytes = 1; - - return val_bytes; + return map->format.val_bytes ?: 1; } static ssize_t regmap_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, unsigned long flags) { struct regmap *map = container_of(cdev, struct regmap, cdev); + size_t val_bytes = map->format.val_bytes; int ret; - ret = regmap_bulk_read(map, offset, buf, count); + count = ALIGN_DOWN(count, val_bytes); + ret = regmap_bulk_read(map, offset, buf, count / val_bytes); if (ret) return ret; @@ -343,9 +396,11 @@ static ssize_t regmap_cdev_write(struct cdev *cdev, const void *buf, size_t coun unsigned long flags) { struct regmap *map = container_of(cdev, struct regmap, cdev); + size_t val_bytes = map->format.val_bytes; int ret; - ret = regmap_bulk_write(map, offset, buf, count); + count = ALIGN_DOWN(count, val_bytes); + ret = regmap_bulk_write(map, offset, buf, count / val_bytes); if (ret) return ret; @@ -358,6 +413,36 @@ static struct cdev_operations regmap_fops = { }; /* + * regmap_count_registers - returns the total number of registers + * + * @map: The map + * + * Returns the total number of registers in a regmap + */ +static size_t regmap_count_registers(struct regmap *map) +{ + /* + * max_register is in units of reg_stride, so we need to divide + * by the register stride before adding one to arrive at the + * total number of registers. + */ + return (map->max_register / map->reg_stride) + 1; +} + +/* + * regmap_size_bytes - computes the size of the regmap in bytes + * + * @map: The map + * + * Returns the number of bytes needed to hold all values in the + * regmap. + */ +size_t regmap_size_bytes(struct regmap *map) +{ + return regmap_round_val_bytes(map) * regmap_count_registers(map); +} + +/* * regmap_register_cdev - register a devfs file for a regmap * * @map: The map @@ -384,8 +469,7 @@ int regmap_register_cdev(struct regmap *map, const char *name) map->cdev.name = xstrdup(dev_name(map->dev)); } - map->cdev.size = regmap_round_val_bytes(map) * (map->max_register + 1) / - map->reg_stride; + map->cdev.size = regmap_size_bytes(map); map->cdev.dev = map->dev; map->cdev.ops = ®map_fops; |