summaryrefslogtreecommitdiffstats
path: root/drivers/base/regmap/regmap.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/regmap/regmap.c')
-rw-r--r--drivers/base/regmap/regmap.c204
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, &regmaps);
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, &regmaps, 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 = &regmap_fops;