summaryrefslogtreecommitdiffstats
path: root/drivers/base/regmap
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r--drivers/base/regmap/Kconfig14
-rw-r--r--drivers/base/regmap/Makefile8
-rw-r--r--drivers/base/regmap/internal.h51
-rw-r--r--drivers/base/regmap/regmap-fmt.c574
-rw-r--r--drivers/base/regmap/regmap-i2c.c101
-rw-r--r--drivers/base/regmap/regmap-mmio.c320
-rw-r--r--drivers/base/regmap/regmap-multi.c104
-rw-r--r--drivers/base/regmap/regmap-spi.c42
-rw-r--r--drivers/base/regmap/regmap.c204
9 files changed, 1351 insertions, 67 deletions
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
new file mode 100644
index 0000000000..c76908952a
--- /dev/null
+++ b/drivers/base/regmap/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config REGMAP_FORMATTED
+ bool
+
+config REGMAP_I2C
+ bool "I2C regmaps" if COMPILE_TEST
+ depends on I2C
+ select REGMAP_FORMATTED
+
+config REGMAP_SPI
+ bool "SPI regmaps" if COMPILE_TEST
+ depends on SPI
+ select REGMAP_FORMATTED
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index 4dc3d8c510..6911e07f0e 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -1 +1,7 @@
-obj-y += regmap.o \ No newline at end of file
+# SPDX-License-Identifier: GPL-2.0-only
+obj-y += regmap.o
+obj-y += regmap-multi.o
+obj-y += regmap-mmio.o
+obj-$(CONFIG_REGMAP_FORMATTED) += regmap-fmt.o
+obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
+obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 52df5290a1..ac3f0d3c0f 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -1,18 +1,57 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef REGMAP_INTERNAL_H_
+#define REGMAP_INTERNAL_H_
#include <linux/list.h>
+#include <driver.h>
+
+struct regmap_bus;
+
+struct regmap_format {
+ size_t buf_size;
+ size_t reg_bytes;
+ size_t pad_bytes;
+ size_t val_bytes;
+ void (*format_write)(struct regmap *map,
+ unsigned int reg, unsigned int val);
+ void (*format_reg)(void *buf, unsigned int reg, unsigned int shift);
+ void (*format_val)(void *buf, unsigned int val, unsigned int shift);
+ unsigned int (*parse_val)(const void *buf);
+};
struct regmap {
- struct device_d *dev;
+ struct device *dev;
const struct regmap_bus *bus;
const char *name;
void *bus_context;
struct list_head list;
- int reg_bits;
int reg_stride;
- int pad_bits;
- int val_bits;
- int val_bytes;
+ void *work_buf; /* Scratch buffer used to format I/O */
+ struct regmap_format format;
+ unsigned int read_flag_mask;
+ unsigned int write_flag_mask;
+ int reg_shift;
unsigned int max_register;
struct cdev cdev;
-}; \ No newline at end of file
+
+ int (*reg_read)(void *context, unsigned int reg,
+ unsigned int *val);
+ int (*reg_write)(void *context, unsigned int reg,
+ unsigned int val);
+};
+
+enum regmap_endian regmap_get_val_endian(struct device *dev,
+ const struct regmap_bus *bus,
+ const struct regmap_config *config);
+
+#ifdef CONFIG_REGMAP_FORMATTED
+int regmap_formatted_init(struct regmap *map, const struct regmap_config *);
+#else
+static inline int regmap_formatted_init(struct regmap *map, const struct regmap_config *cfg)
+{
+ return -ENOSYS;
+}
+#endif
+
+#endif
diff --git a/drivers/base/regmap/regmap-fmt.c b/drivers/base/regmap/regmap-fmt.c
new file mode 100644
index 0000000000..e7f6a8da80
--- /dev/null
+++ b/drivers/base/regmap/regmap-fmt.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Formatted register map access API
+ *
+ * Copyright 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ *
+ * based on Kernel code:
+ *
+ * Copyright 2011 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ */
+
+#include <common.h>
+#include <linux/regmap.h>
+#include <linux/log2.h>
+#include <asm/unaligned.h>
+
+#include "internal.h"
+
+static void regmap_format_12_20_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ out[0] = reg >> 4;
+ out[1] = (reg << 4) | (val >> 16);
+ out[2] = val >> 8;
+ out[3] = val;
+}
+
+
+static void regmap_format_2_6_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ *out = (reg << 6) | val;
+}
+
+static void regmap_format_4_12_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ __be16 *out = map->work_buf;
+ *out = cpu_to_be16((reg << 12) | val);
+}
+
+static void regmap_format_7_9_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ __be16 *out = map->work_buf;
+ *out = cpu_to_be16((reg << 9) | val);
+}
+
+static void regmap_format_7_17_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ out[2] = val;
+ out[1] = val >> 8;
+ out[0] = (val >> 16) | (reg << 1);
+}
+
+static void regmap_format_10_14_write(struct regmap *map,
+ unsigned int reg, unsigned int val)
+{
+ u8 *out = map->work_buf;
+
+ out[2] = val;
+ out[1] = (val >> 8) | (reg << 6);
+ out[0] = reg >> 2;
+}
+
+static void regmap_format_8(void *buf, unsigned int val, unsigned int shift)
+{
+ u8 *b = buf;
+
+ b[0] = val << shift;
+}
+
+static void regmap_format_16_be(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_be16(val << shift, buf);
+}
+
+static void regmap_format_16_le(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_le16(val << shift, buf);
+}
+
+static void regmap_format_16_native(void *buf, unsigned int val,
+ unsigned int shift)
+{
+ u16 v = val << shift;
+
+ memcpy(buf, &v, sizeof(v));
+}
+
+static void regmap_format_24(void *buf, unsigned int val, unsigned int shift)
+{
+ u8 *b = buf;
+
+ val <<= shift;
+
+ b[0] = val >> 16;
+ b[1] = val >> 8;
+ b[2] = val;
+}
+
+static void regmap_format_32_be(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_be32(val << shift, buf);
+}
+
+static void regmap_format_32_le(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_le32(val << shift, buf);
+}
+
+static void regmap_format_32_native(void *buf, unsigned int val,
+ unsigned int shift)
+{
+ u32 v = val << shift;
+
+ memcpy(buf, &v, sizeof(v));
+}
+
+#ifdef CONFIG_64BIT
+static void regmap_format_64_be(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_be64((u64) val << shift, buf);
+}
+
+static void regmap_format_64_le(void *buf, unsigned int val, unsigned int shift)
+{
+ put_unaligned_le64((u64) val << shift, buf);
+}
+
+static void regmap_format_64_native(void *buf, unsigned int val,
+ unsigned int shift)
+{
+ u64 v = (u64) val << shift;
+
+ memcpy(buf, &v, sizeof(v));
+}
+#endif
+
+static unsigned int regmap_parse_8(const void *buf)
+{
+ const u8 *b = buf;
+
+ return b[0];
+}
+
+static unsigned int regmap_parse_16_be(const void *buf)
+{
+ return get_unaligned_be16(buf);
+}
+
+static unsigned int regmap_parse_16_le(const void *buf)
+{
+ return get_unaligned_le16(buf);
+}
+
+static unsigned int regmap_parse_16_native(const void *buf)
+{
+ u16 v;
+
+ memcpy(&v, buf, sizeof(v));
+ return v;
+}
+
+static unsigned int regmap_parse_24(const void *buf)
+{
+ const u8 *b = buf;
+ unsigned int ret = b[2];
+ ret |= ((unsigned int)b[1]) << 8;
+ ret |= ((unsigned int)b[0]) << 16;
+
+ return ret;
+}
+
+static unsigned int regmap_parse_32_be(const void *buf)
+{
+ return get_unaligned_be32(buf);
+}
+
+static unsigned int regmap_parse_32_le(const void *buf)
+{
+ return get_unaligned_le32(buf);
+}
+
+static unsigned int regmap_parse_32_native(const void *buf)
+{
+ u32 v;
+
+ memcpy(&v, buf, sizeof(v));
+ return v;
+}
+
+#ifdef CONFIG_64BIT
+static unsigned int regmap_parse_64_be(const void *buf)
+{
+ return get_unaligned_be64(buf);
+}
+
+static unsigned int regmap_parse_64_le(const void *buf)
+{
+ return get_unaligned_le64(buf);
+}
+
+static unsigned int regmap_parse_64_native(const void *buf)
+{
+ u64 v;
+
+ memcpy(&v, buf, sizeof(v));
+ return v;
+}
+#endif
+
+
+static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
+ const struct regmap_config *config)
+{
+ enum regmap_endian endian;
+
+ /* Retrieve the endianness specification from the regmap config */
+ endian = config->reg_format_endian;
+
+ /* If the regmap config specified a non-default value, use that */
+ if (endian != REGMAP_ENDIAN_DEFAULT)
+ return endian;
+
+ /* Retrieve the endianness specification from the bus config */
+ if (bus && bus->reg_format_endian_default)
+ endian = bus->reg_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;
+}
+
+static void regmap_set_work_buf_flag_mask(struct regmap *map, int max_bytes,
+ unsigned long mask)
+{
+ u8 *buf;
+ int i;
+
+ if (!mask || !map->work_buf)
+ return;
+
+ buf = map->work_buf;
+
+ for (i = 0; i < max_bytes; i++)
+ buf[i] |= (mask >> (8 * i)) & 0xff;
+}
+
+static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
+ unsigned int val_len, bool noinc)
+{
+ const struct regmap_bus *bus = map->bus;
+
+ if (!bus->read)
+ return -EINVAL;
+
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+ map->read_flag_mask);
+
+ return bus->read(map->bus_context, map->work_buf,
+ map->format.reg_bytes + map->format.pad_bytes,
+ val, val_len);
+
+}
+
+static int _regmap_bus_read(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ int ret;
+ struct regmap *map = context;
+ void *work_val = map->work_buf + map->format.reg_bytes +
+ map->format.pad_bytes;
+
+ if (!map->format.parse_val)
+ return -EINVAL;
+
+ ret = _regmap_raw_read(map, reg, work_val, map->format.val_bytes, false);
+ if (ret == 0)
+ *val = map->format.parse_val(work_val);
+
+ return ret;
+}
+
+static int _regmap_bus_formatted_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct regmap *map = context;
+
+ map->format.format_write(map, reg, val);
+
+ return map->bus->write(map->bus_context, map->work_buf,
+ map->format.buf_size);
+}
+
+static int _regmap_raw_write_impl(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len, bool noinc)
+{
+ void *work_val = map->work_buf + map->format.reg_bytes +
+ map->format.pad_bytes;
+
+ map->format.format_reg(map->work_buf, reg, map->reg_shift);
+ regmap_set_work_buf_flag_mask(map, map->format.reg_bytes,
+ map->write_flag_mask);
+
+ /*
+ * Essentially all I/O mechanisms will be faster with a single
+ * buffer to write. Since register syncs often generate raw
+ * writes of single registers optimise that case.
+ */
+ if (val != work_val && val_len == map->format.val_bytes) {
+ memcpy(work_val, val, map->format.val_bytes);
+ val = work_val;
+ }
+
+ /* If we're doing a single register write we can probably just
+ * send the work_buf directly, otherwise try to do a gather
+ * write.
+ */
+ return map->bus->write(map->bus_context, map->work_buf,
+ map->format.reg_bytes +
+ map->format.pad_bytes +
+ val_len);
+
+}
+
+static int _regmap_bus_raw_write(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct regmap *map = context;
+
+ WARN_ON(!map->format.format_val);
+
+ map->format.format_val(map->work_buf + map->format.reg_bytes
+ + map->format.pad_bytes, val, 0);
+ return _regmap_raw_write_impl(map, reg,
+ map->work_buf +
+ map->format.reg_bytes +
+ map->format.pad_bytes,
+ map->format.val_bytes,
+ false);
+}
+
+int regmap_formatted_init(struct regmap *map, const struct regmap_config *config)
+{
+ enum regmap_endian reg_endian, val_endian;
+ const struct regmap_bus *bus = map->bus;
+
+ map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
+ config->val_bits + config->pad_bits, 8);
+
+ map->work_buf = xzalloc(map->format.buf_size);
+
+ if (config->read_flag_mask || config->write_flag_mask) {
+ map->read_flag_mask = config->read_flag_mask;
+ map->write_flag_mask = config->write_flag_mask;
+ } else {
+ map->read_flag_mask = bus->read_flag_mask;
+ }
+
+ map->reg_read = _regmap_bus_read;
+
+ reg_endian = regmap_get_reg_endian(bus, config);
+ val_endian = regmap_get_val_endian(map->dev, bus, config);
+
+ switch (config->reg_bits + config->pad_bits % 8) {
+ case 2:
+ switch (config->val_bits) {
+ case 6:
+ map->format.format_write = regmap_format_2_6_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 4:
+ switch (config->val_bits) {
+ case 12:
+ map->format.format_write = regmap_format_4_12_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 7:
+ switch (config->val_bits) {
+ case 9:
+ map->format.format_write = regmap_format_7_9_write;
+ break;
+ case 17:
+ map->format.format_write = regmap_format_7_17_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 10:
+ switch (config->val_bits) {
+ case 14:
+ map->format.format_write = regmap_format_10_14_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 12:
+ switch (config->val_bits) {
+ case 20:
+ map->format.format_write = regmap_format_12_20_write;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 8:
+ map->format.format_reg = regmap_format_8;
+ break;
+
+ case 16:
+ switch (reg_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_reg = regmap_format_16_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_reg = regmap_format_16_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_reg = regmap_format_16_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+ case 24:
+ if (reg_endian != REGMAP_ENDIAN_BIG)
+ return -EINVAL;
+ map->format.format_reg = regmap_format_24;
+ break;
+
+ case 32:
+ switch (reg_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_reg = regmap_format_32_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_reg = regmap_format_32_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_reg = regmap_format_32_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+
+#ifdef CONFIG_64BIT
+ case 64:
+ switch (reg_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_reg = regmap_format_64_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_reg = regmap_format_64_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_reg = regmap_format_64_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+#endif
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (config->val_bits) {
+ case 8:
+ map->format.format_val = regmap_format_8;
+ map->format.parse_val = regmap_parse_8;
+ break;
+ case 16:
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_16_be;
+ map->format.parse_val = regmap_parse_16_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_val = regmap_format_16_le;
+ map->format.parse_val = regmap_parse_16_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_val = regmap_format_16_native;
+ map->format.parse_val = regmap_parse_16_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case 24:
+ if (val_endian != REGMAP_ENDIAN_BIG)
+ return -EINVAL;
+ map->format.format_val = regmap_format_24;
+ map->format.parse_val = regmap_parse_24;
+ break;
+ case 32:
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_32_be;
+ map->format.parse_val = regmap_parse_32_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_val = regmap_format_32_le;
+ map->format.parse_val = regmap_parse_32_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_val = regmap_format_32_native;
+ map->format.parse_val = regmap_parse_32_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+#ifdef CONFIG_64BIT
+ case 64:
+ switch (val_endian) {
+ case REGMAP_ENDIAN_BIG:
+ map->format.format_val = regmap_format_64_be;
+ map->format.parse_val = regmap_parse_64_be;
+ break;
+ case REGMAP_ENDIAN_LITTLE:
+ map->format.format_val = regmap_format_64_le;
+ map->format.parse_val = regmap_parse_64_le;
+ break;
+ case REGMAP_ENDIAN_NATIVE:
+ map->format.format_val = regmap_format_64_native;
+ map->format.parse_val = regmap_parse_64_native;
+ break;
+ default:
+ return -EINVAL;
+ }
+ break;
+#endif
+ }
+
+ if (map->format.format_write)
+ map->reg_write = _regmap_bus_formatted_write;
+ else if (map->format.format_val)
+ map->reg_write = _regmap_bus_raw_write;
+ else
+ return -EOPNOTSUPP;
+
+ return 0;
+}
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
new file mode 100644
index 0000000000..13ba5866c0
--- /dev/null
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2021, Ahmad Fatoum, Pengutronix
+ */
+
+#include <i2c/i2c.h>
+#include <linux/regmap.h>
+
+
+static int regmap_i2c_read(void *context,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct i2c_msg xfer[2];
+ int ret;
+
+ xfer[0].addr = i2c->addr;
+ xfer[0].flags = 0;
+ xfer[0].len = reg_size;
+ xfer[0].buf = (void *)reg;
+
+ xfer[1].addr = i2c->addr;
+ xfer[1].flags = I2C_M_RD;
+ xfer[1].len = val_size;
+ xfer[1].buf = val;
+
+ ret = i2c_transfer(i2c->adapter, xfer, 2);
+ if (ret == 2)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int regmap_i2c_write(void *context, const void *data, size_t count)
+{
+ struct device *dev = context;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ int ret;
+
+ ret = i2c_master_send(i2c, data, count);
+ if (ret == count)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static const struct regmap_bus regmap_regmap_i2c_bus = {
+ .write = regmap_i2c_write,
+ .read = regmap_i2c_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+struct regmap *regmap_init_i2c(struct i2c_client *client,
+ const struct regmap_config *config)
+{
+ return regmap_init(&client->dev, &regmap_regmap_i2c_bus, client, config);
+}
+
+static int regmap_smbus_byte_reg_read(void *client, unsigned int reg, unsigned int *val)
+{
+ int ret;
+
+ if (reg > 0xff)
+ return -EINVAL;
+
+ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return 0;
+}
+
+static int regmap_smbus_byte_reg_write(void *client, unsigned int reg, unsigned int val)
+{
+ if (val > 0xff || reg > 0xff)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(client, reg, val);
+}
+
+static const struct regmap_bus regmap_smbus_byte = {
+ .reg_write = regmap_smbus_byte_reg_write,
+ .reg_read = regmap_smbus_byte_reg_read,
+};
+
+struct regmap *regmap_init_i2c_smbus(struct i2c_client *client,
+ const struct regmap_config *config)
+{
+ if (config->val_bits != 8 || config->reg_bits != 8)
+ return ERR_PTR(-ENOSYS);
+ return regmap_init(&client->dev, &regmap_smbus_byte, client, config);
+}
diff --git a/drivers/base/regmap/regmap-mmio.c b/drivers/base/regmap/regmap-mmio.c
new file mode 100644
index 0000000000..01b0a99631
--- /dev/null
+++ b/drivers/base/regmap/regmap-mmio.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Register map access API - MMIO support
+//
+// Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <io.h>
+#include <linux/regmap.h>
+
+#include "internal.h"
+
+struct regmap_mmio_context {
+ void __iomem *regs;
+ unsigned val_bytes;
+
+ struct clk *clk;
+
+ void (*reg_write)(struct regmap_mmio_context *ctx,
+ unsigned int reg, unsigned int val);
+ unsigned int (*reg_read)(struct regmap_mmio_context *ctx,
+ unsigned int reg);
+};
+
+static int regmap_mmio_regbits_check(size_t reg_bits)
+{
+ switch (reg_bits) {
+ case 8:
+ case 16:
+ case 32:
+#ifdef CONFIG_64BIT
+ case 64:
+#endif
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int regmap_mmio_get_min_stride(size_t val_bits)
+{
+ int min_stride;
+
+ switch (val_bits) {
+ case 8:
+ /* The core treats 0 as 1 */
+ min_stride = 0;
+ break;
+ case 16:
+ min_stride = 2;
+ break;
+ case 32:
+ min_stride = 4;
+ break;
+#ifdef CONFIG_64BIT
+ case 64:
+ min_stride = 8;
+ break;
+#endif
+ default:
+ return -EINVAL;
+ }
+
+ return min_stride;
+}
+
+static void regmap_mmio_write8(struct regmap_mmio_context *ctx,
+ unsigned int reg,
+ unsigned int val)
+{
+ writeb(val, ctx->regs + reg);
+}
+
+static void regmap_mmio_write16le(struct regmap_mmio_context *ctx,
+ unsigned int reg,
+ unsigned int val)
+{
+ writew(val, ctx->regs + reg);
+}
+
+static void regmap_mmio_write32le(struct regmap_mmio_context *ctx,
+ unsigned int reg,
+ unsigned int val)
+{
+ writel(val, ctx->regs + reg);
+}
+
+static void regmap_mmio_write16be(struct regmap_mmio_context *ctx,
+ unsigned int reg,
+ unsigned int val)
+{
+ iowrite16be(val, ctx->regs + reg);
+}
+
+static void regmap_mmio_write32be(struct regmap_mmio_context *ctx,
+ unsigned int reg,
+ unsigned int val)
+{
+ iowrite32be(val, ctx->regs + reg);
+}
+
+#ifdef CONFIG_64BIT
+static void regmap_mmio_write64le(struct regmap_mmio_context *ctx,
+ unsigned int reg,
+ unsigned int val)
+{
+ writeq(val, ctx->regs + reg);
+}
+#endif
+
+static int regmap_mmio_write(void *context, unsigned int reg, unsigned int val)
+{
+ struct regmap_mmio_context *ctx = context;
+ int ret;
+
+ ret = clk_enable(ctx->clk);
+ if (ret < 0)
+ return ret;
+
+ ctx->reg_write(ctx, reg, val);
+
+ clk_disable(ctx->clk);
+
+ return 0;
+}
+
+static unsigned int regmap_mmio_read8(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return readb(ctx->regs + reg);
+}
+
+static unsigned int regmap_mmio_read16le(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return readw(ctx->regs + reg);
+}
+
+static unsigned int regmap_mmio_read32le(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return readl(ctx->regs + reg);
+}
+
+#ifdef CONFIG_64BIT
+static unsigned int regmap_mmio_read64le(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return readq(ctx->regs + reg);
+}
+#endif
+
+static unsigned int regmap_mmio_read16be(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return ioread16be(ctx->regs + reg);
+}
+
+static unsigned int regmap_mmio_read32be(struct regmap_mmio_context *ctx,
+ unsigned int reg)
+{
+ return ioread32be(ctx->regs + reg);
+}
+
+static int regmap_mmio_read(void *context, unsigned int reg, unsigned int *val)
+{
+ struct regmap_mmio_context *ctx = context;
+ int ret;
+
+ ret = clk_enable(ctx->clk);
+ if (ret < 0)
+ return ret;
+
+ *val = ctx->reg_read(ctx, reg);
+
+ clk_disable(ctx->clk);
+
+ return 0;
+}
+
+static const struct regmap_bus regmap_mmio = {
+ .reg_write = regmap_mmio_write,
+ .reg_read = regmap_mmio_read,
+ .val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+static struct regmap_mmio_context *regmap_mmio_gen_context(struct device *dev,
+ void __iomem *regs,
+ const struct regmap_config *config)
+{
+ struct regmap_mmio_context *ctx;
+ int min_stride;
+ int ret;
+
+ ret = regmap_mmio_regbits_check(config->reg_bits);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (config->pad_bits)
+ return ERR_PTR(-EINVAL);
+
+ min_stride = regmap_mmio_get_min_stride(config->val_bits);
+ if (min_stride < 0)
+ return ERR_PTR(min_stride);
+
+ if (config->reg_stride < min_stride)
+ return ERR_PTR(-EINVAL);
+
+ ctx = xzalloc(sizeof(*ctx));
+
+ ctx->regs = regs;
+ ctx->val_bytes = config->val_bits / 8;
+
+ switch (regmap_get_val_endian(dev, &regmap_mmio, config)) {
+ case REGMAP_ENDIAN_DEFAULT:
+ case REGMAP_ENDIAN_LITTLE:
+#ifdef __LITTLE_ENDIAN
+ case REGMAP_ENDIAN_NATIVE:
+#endif
+ switch (config->val_bits) {
+ case 8:
+ ctx->reg_read = regmap_mmio_read8;
+ ctx->reg_write = regmap_mmio_write8;
+ break;
+ case 16:
+ ctx->reg_read = regmap_mmio_read16le;
+ ctx->reg_write = regmap_mmio_write16le;
+ break;
+ case 32:
+ ctx->reg_read = regmap_mmio_read32le;
+ ctx->reg_write = regmap_mmio_write32le;
+ break;
+#ifdef CONFIG_64BIT
+ case 64:
+ ctx->reg_read = regmap_mmio_read64le;
+ ctx->reg_write = regmap_mmio_write64le;
+ break;
+#endif
+ default:
+ ret = -EINVAL;
+ goto err_free;
+ }
+ break;
+ case REGMAP_ENDIAN_BIG:
+#ifdef __BIG_ENDIAN
+ case REGMAP_ENDIAN_NATIVE:
+#endif
+ switch (config->val_bits) {
+ case 8:
+ ctx->reg_read = regmap_mmio_read8;
+ ctx->reg_write = regmap_mmio_write8;
+ break;
+ case 16:
+ ctx->reg_read = regmap_mmio_read16be;
+ ctx->reg_write = regmap_mmio_write16be;
+ break;
+ case 32:
+ ctx->reg_read = regmap_mmio_read32be;
+ ctx->reg_write = regmap_mmio_write32be;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_free;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_free;
+ }
+
+ return ctx;
+
+err_free:
+ kfree(ctx);
+
+ return ERR_PTR(ret);
+}
+
+struct regmap *regmap_init_mmio_clk(struct device *dev,
+ const char *clk_id,
+ void __iomem *regs,
+ const struct regmap_config *config)
+{
+ struct regmap_mmio_context *ctx;
+
+ ctx = regmap_mmio_gen_context(dev, regs, config);
+ if (IS_ERR(ctx))
+ return ERR_CAST(ctx);
+
+ if (clk_id) {
+ struct clk *clk;
+
+ clk = clk_get(dev, clk_id);
+ if (IS_ERR(clk)) {
+ kfree(ctx);
+ return ERR_CAST(clk);
+ }
+
+ ctx->clk = clk;
+ }
+
+ return regmap_init(dev, &regmap_mmio, ctx, config);
+}
+
+int regmap_mmio_attach_clk(struct regmap *map, struct clk *clk)
+{
+ struct regmap_mmio_context *ctx = map->bus_context;
+
+ ctx->clk = clk;
+
+ return 0;
+}
+
+void regmap_mmio_detach_clk(struct regmap *map)
+{
+ struct regmap_mmio_context *ctx = map->bus_context;
+
+ ctx->clk = NULL;
+}
diff --git a/drivers/base/regmap/regmap-multi.c b/drivers/base/regmap/regmap-multi.c
new file mode 100644
index 0000000000..74f3648eb4
--- /dev/null
+++ b/drivers/base/regmap/regmap-multi.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2022 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <fcntl.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+#include <linux/export.h>
+
+#include "internal.h"
+
+enum { MULTI_MAP_8, MULTI_MAP_16, MULTI_MAP_32, MULTI_MAP_64, MULTI_MAP_COUNT };
+struct regmap_multi {
+ struct cdev cdev;
+ struct regmap *map[MULTI_MAP_COUNT];
+};
+
+static struct regmap *regmap_multi_cdev_get_map(struct cdev *cdev, unsigned rwsize)
+{
+ struct regmap_multi *multi = container_of(cdev, struct regmap_multi, cdev);
+
+ switch (rwsize) {
+ case 8:
+ return multi->map[MULTI_MAP_64];
+ case 4:
+ return multi->map[MULTI_MAP_32];
+ case 2:
+ return multi->map[MULTI_MAP_16];
+ case 1:
+ return multi->map[MULTI_MAP_8];
+ }
+
+ return NULL;
+}
+
+static ssize_t regmap_multi_cdev_read(struct cdev *cdev, void *buf, size_t count,
+ loff_t offset, unsigned long flags)
+{
+ unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags);
+ struct regmap *map;
+
+ map = regmap_multi_cdev_get_map(cdev, rwsize);
+ if (!map)
+ return -EINVAL;
+
+ count = ALIGN_DOWN(count, rwsize);
+ return regmap_bulk_read(map, offset, buf, count / rwsize) ?: count;
+}
+
+static ssize_t regmap_multi_cdev_write(struct cdev *cdev, const void *buf, size_t count,
+ loff_t offset, unsigned long flags)
+{
+ unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags);
+ struct regmap *map;
+
+ map = regmap_multi_cdev_get_map(cdev, rwsize);
+ if (!map)
+ return -EINVAL;
+
+ count = ALIGN_DOWN(count, rwsize);
+ return regmap_bulk_write(map, offset, buf, count / rwsize) ?: count;
+}
+
+static struct cdev_operations regmap_multi_fops = {
+ .read = regmap_multi_cdev_read,
+ .write = regmap_multi_cdev_write,
+};
+
+int regmap_multi_register_cdev(struct regmap *map8,
+ struct regmap *map16,
+ struct regmap *map32,
+ struct regmap *map64)
+{
+ struct regmap *maps[MULTI_MAP_COUNT] = { map8, map16, map32, map64 };
+ struct regmap_multi *multi;
+ struct cdev *cdev;
+ int i;
+
+ multi = xzalloc(sizeof(*multi));
+ cdev = &multi->cdev;
+
+ cdev->ops = &regmap_multi_fops;
+ cdev->size = LLONG_MAX;
+
+ for (i = 0; i < MULTI_MAP_COUNT; i++) {
+ if (!maps[i])
+ continue;
+
+ multi->map[i] = maps[i];
+ cdev->size = min_t(loff_t, regmap_size_bytes(maps[i]), cdev->size);
+ cdev->dev = cdev->dev ?: maps[i]->dev;
+ }
+
+ if (!cdev->dev) {
+ free(multi);
+ return -EINVAL;
+ }
+
+ cdev->name = xstrdup(dev_name(cdev->dev));
+
+ return devfs_create(cdev);
+}
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
new file mode 100644
index 0000000000..d15d59f635
--- /dev/null
+++ b/drivers/base/regmap/regmap-spi.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Register map access API - SPI support
+//
+// Copyright 2011 Wolfson Microelectronics plc
+//
+// Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+
+#include <linux/regmap.h>
+#include <spi/spi.h>
+
+static int regmap_spi_write(void *context, const void *data, size_t count)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+
+ return spi_write(spi, data, count);
+}
+
+static int regmap_spi_read(void *context,
+ const void *reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ struct device *dev = context;
+ struct spi_device *spi = to_spi_device(dev);
+
+ return spi_write_then_read(spi, reg, reg_size, val, val_size);
+}
+
+static const struct regmap_bus regmap_spi = {
+ .write = regmap_spi_write,
+ .read = regmap_spi_read,
+ .read_flag_mask = 0x80,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+struct regmap *regmap_init_spi(struct spi_device *spi,
+ const struct regmap_config *config)
+{
+ return regmap_init(&spi->dev, &regmap_spi, &spi->dev, config);
+}
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;