diff options
Diffstat (limited to 'drivers/base/regmap/regmap.c')
-rw-r--r-- | drivers/base/regmap/regmap.c | 149 |
1 files changed, 102 insertions, 47 deletions
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index 1af0c15a7b..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,7 +20,7 @@ static LIST_HEAD(regmaps); -enum regmap_endian regmap_get_val_endian(struct device_d *dev, +enum regmap_endian regmap_get_val_endian(struct device *dev, const struct regmap_bus *bus, const struct regmap_config *config) { @@ -43,8 +35,8 @@ enum regmap_endian regmap_get_val_endian(struct device_d *dev, return endian; /* If the dev and dev->device_node exist try to get endianness from DT */ - if (dev && dev->device_node) { - np = dev->device_node; + 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")) @@ -72,6 +64,23 @@ enum regmap_endian regmap_get_val_endian(struct device_d *dev, } 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 * @@ -82,27 +91,39 @@ EXPORT_SYMBOL_GPL(regmap_get_val_endian); * * 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; @@ -116,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; @@ -132,7 +153,7 @@ struct regmap *dev_get_regmap(struct device_d *dev, const char *name) return ERR_PTR(-ENOENT); } -struct device_d *regmap_get_device(struct regmap *map) +struct device *regmap_get_device(struct regmap *map) { return map->dev; } @@ -148,7 +169,7 @@ struct device_d *regmap_get_device(struct regmap *map) */ 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); } /* @@ -162,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); } /** @@ -227,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; @@ -256,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; @@ -282,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) @@ -314,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; @@ -331,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) @@ -346,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; @@ -372,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; @@ -387,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 @@ -413,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; |