From: Sascha Hauer <s.hauer@pengutronix.de>
To: Barebox List <barebox@lists.infradead.org>
Subject: [PATCH 2/4] Add initial regmap support
Date: Wed, 3 Feb 2016 17:20:09 +0100 [thread overview]
Message-ID: <1454516411-22858-3-git-send-email-s.hauer@pengutronix.de> (raw)
In-Reply-To: <1454516411-22858-1-git-send-email-s.hauer@pengutronix.de>
This adds initial regmap support. Function prototypes are from
the Kernel, the implemention is mostly rewritten. Currently the
regmap support is limited to the bare minimum to get started.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
drivers/base/Makefile | 1 +
drivers/base/regmap/Makefile | 1 +
drivers/base/regmap/internal.h | 18 ++
drivers/base/regmap/regmap.c | 398 +++++++++++++++++++++++++++++++++++++++++
include/regmap.h | 62 +++++++
5 files changed, 480 insertions(+)
create mode 100644 drivers/base/regmap/Makefile
create mode 100644 drivers/base/regmap/internal.h
create mode 100644 drivers/base/regmap/regmap.c
create mode 100644 include/regmap.h
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index e1f1c7a..4bd4217 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -2,3 +2,4 @@ obj-y += bus.o
obj-y += driver.o
obj-y += platform.o
obj-y += resource.o
+obj-y += regmap/
\ No newline at end of file
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
new file mode 100644
index 0000000..4dc3d8c
--- /dev/null
+++ b/drivers/base/regmap/Makefile
@@ -0,0 +1 @@
+obj-y += regmap.o
\ No newline at end of file
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
new file mode 100644
index 0000000..52df529
--- /dev/null
+++ b/drivers/base/regmap/internal.h
@@ -0,0 +1,18 @@
+
+#include <linux/list.h>
+
+struct regmap {
+ struct device_d *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;
+ unsigned int max_register;
+
+ struct cdev cdev;
+};
\ No newline at end of file
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
new file mode 100644
index 0000000..b626f9a
--- /dev/null
+++ b/drivers/base/regmap/regmap.c
@@ -0,0 +1,398 @@
+/*
+ * Register map access API
+ *
+ * Copyright 2016 Sascha Hauer <s.hauer@pengutronix.de>
+ *
+ * based on Kernel code:
+ *
+ * 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 <malloc.h>
+#include <linux/log2.h>
+
+#include "internal.h"
+
+static LIST_HEAD(regmaps);
+
+/*
+ * regmap_init - initialize and register a regmap
+ *
+ * @dev: The device the new regmap belongs to
+ * @bus: The regmap_bus providing read/write access to the map
+ * @bus_context: Context pointer for the bus ops
+ * @config: Configuration options for this map
+ *
+ * Returns a pointer to the new map or a ERR_PTR value on failure
+ */
+struct regmap *regmap_init(struct device_d *dev,
+ const struct regmap_bus *bus,
+ void *bus_context,
+ const struct regmap_config *config)
+{
+ struct regmap *map;
+
+ 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->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->max_register = config->max_register;
+
+ list_add_tail(&map->list, ®maps);
+
+ return map;
+}
+
+/*
+ * regmap_init - initialize and register a regmap
+ *
+ * @dev: The device the maps is attached to
+ * @name: Optional name for the map. If given it must match.
+ *
+ * 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 *map;
+
+ list_for_each_entry(map, ®maps, list) {
+ if (map->dev != dev)
+ continue;
+ if (!name)
+ return map;
+ if (!strcmp(map->name, name))
+ return map;
+ }
+
+ 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 regmap *map;
+
+ list_for_each_entry(map, ®maps, list) {
+ if (map->dev->device_node == node)
+ return map;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
+/*
+ * regmap_write - write a register in a map
+ *
+ * @map: The map
+ * @reg: The register offset of the register
+ * @val: The value to be written
+ *
+ * Returns 0 for success or negative error code on failure
+ */
+int regmap_write(struct regmap *map, unsigned int reg, unsigned int val)
+{
+ return map->bus->reg_write(map->bus_context, reg, val);
+}
+
+/*
+ * regmap_write - read a register from a map
+ *
+ * @map: The map
+ * @reg: The register offset of the register
+ * @val: pointer to value read
+ *
+ * Returns 0 for success or negative error code on failure
+ */
+int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val)
+{
+ return map->bus->reg_read(map->bus_context, reg, val);
+}
+
+/**
+ * regmap_bulk_read(): Read data from the device
+ *
+ * @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
+ *
+ * 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_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++) {
+ u32 *u32 = val;
+ u16 *u16 = val;
+ u8 *u8 = val;
+
+ ret = regmap_read(map, reg + (i * map->reg_stride), &v);
+ if (ret != 0)
+ goto out;
+
+ switch (map->val_bytes) {
+ case 4:
+ u32[i] = v;
+ break;
+ case 2:
+ u16[i] = v;
+ break;
+ case 1:
+ u8[i] = v;
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ out:
+ return ret;
+}
+
+/**
+ * regmap_bulk_write(): Write values to one or more registers
+ *
+ * @map: Register map to write to
+ * @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.
+ *
+ * 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)
+{
+ size_t val_bytes = map->val_bytes;
+ size_t val_count = val_len / 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)
+ return -EINVAL;
+
+ for (i = 0; i < val_count; i++) {
+ unsigned int ival;
+
+ switch (val_bytes) {
+ case 1:
+ ival = *(u8 *)(val + (i * val_bytes));
+ break;
+ case 2:
+ ival = *(u16 *)(val + (i * val_bytes));
+ break;
+ case 4:
+ ival = *(u32 *)(val + (i * val_bytes));
+ break;
+ default:
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = regmap_write(map, reg + (i * map->reg_stride),
+ ival);
+ if (ret != 0)
+ goto out;
+ }
+
+ out:
+ return ret;
+}
+
+int regmap_get_val_bytes(struct regmap *map)
+{
+ return map->val_bytes;
+}
+
+int regmap_get_max_register(struct regmap *map)
+{
+ return map->max_register;
+}
+
+int regmap_get_reg_stride(struct regmap *map)
+{
+ return map->reg_stride;
+}
+
+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;
+}
+
+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);
+ int val_bytes = regmap_round_val_bytes(map);
+ int n = count;
+ int err;
+ unsigned int ofs = offset;
+
+ if (n < val_bytes || ofs & (val_bytes - 1))
+ return -EINVAL;
+
+ ofs /= val_bytes;
+ ofs *= map->reg_stride;
+
+ while (n > 0) {
+ err = regmap_read(map, ofs, buf);
+ if (err)
+ return (ssize_t)err;
+ buf += val_bytes;
+ n -= val_bytes;
+ ofs += map->reg_stride;
+ }
+
+ return count;
+}
+
+static ssize_t regmap_cdev_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset,
+ unsigned long flags)
+{
+ struct regmap *map = container_of(cdev, struct regmap, cdev);
+ int val_bytes = regmap_round_val_bytes(map);
+ int n = count;
+ int err;
+ unsigned int ofs = offset;
+
+ if (n < val_bytes || ofs & (val_bytes - 1))
+ return -EINVAL;
+
+ ofs /= val_bytes;
+ ofs *= map->reg_stride;
+
+ while (n > 0) {
+ u32 val;
+
+ switch (val_bytes) {
+ case 1:
+ val = *(u8 *)buf;
+ break;
+ case 2:
+ val = *(u16 *)buf;
+ break;
+ case 4:
+ val = *(u32 *)buf;
+ break;
+ default:
+ return -EINVAL;
+ };
+
+ err = regmap_write(map, ofs, val);
+ if (err)
+ return (ssize_t)err;
+ buf += val_bytes;
+ n -= val_bytes;
+ ofs += map->reg_stride;
+ }
+
+ return count;
+}
+
+static struct file_operations regmap_fops = {
+ .lseek = dev_lseek_default,
+ .read = regmap_cdev_read,
+ .write = regmap_cdev_write,
+};
+
+/*
+ * regmap_register_cdev - register a devfs file for a regmap
+ *
+ * @map: The map
+ * @name: Optional name of the device file
+ *
+ * Returns 0 for success or negative error code on failure
+ */
+int regmap_register_cdev(struct regmap *map, const char *name)
+{
+ int ret;
+
+ if (map->cdev.name)
+ return -EBUSY;
+
+ if (!map->max_register)
+ return -EINVAL;
+
+ if (name) {
+ map->cdev.name = xstrdup(name);
+ } else {
+ if (map->name)
+ map->cdev.name = xasprintf("%s-%s", dev_name(map->dev), map->name);
+ else
+ 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.dev = map->dev;
+ map->cdev.ops = ®map_fops;
+
+ ret = devfs_create(&map->cdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void regmap_exit(struct regmap *map)
+{
+ list_del(&map->list);
+
+ if (map->cdev.name) {
+ devfs_remove(&map->cdev);
+ free(map->cdev.name);
+ }
+
+ free(map);
+}
\ No newline at end of file
diff --git a/include/regmap.h b/include/regmap.h
new file mode 100644
index 0000000..4cb473c
--- /dev/null
+++ b/include/regmap.h
@@ -0,0 +1,62 @@
+#ifndef __REGMAP_H
+#define __REGMAP_H
+
+/**
+ * Configuration for the register map of a device.
+ *
+ * @name: Optional name of the regmap. Useful when a device has multiple
+ * register regions.
+ *
+ * @reg_bits: Number of bits in a register address, mandatory.
+ * @reg_stride: The register address stride. Valid register addresses are a
+ * multiple of this value. If set to 0, a value of 1 will be
+ * used.
+ * @pad_bits: Number of bits of padding between register and value.
+ * @val_bits: Number of bits in a register value, mandatory.
+ *
+ * @max_register: Optional, specifies the maximum valid register index.
+ */
+struct regmap_config {
+ const char *name;
+
+ int reg_bits;
+ int reg_stride;
+ int pad_bits;
+ int val_bits;
+
+ unsigned int max_register;
+};
+
+typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
+ unsigned int *val);
+typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
+ unsigned int val);
+
+struct regmap_bus {
+ regmap_hw_reg_write reg_write;
+ regmap_hw_reg_read reg_read;
+};
+
+struct regmap *regmap_init(struct device_d *dev,
+ const struct regmap_bus *bus,
+ void *bus_context,
+ const struct regmap_config *config);
+
+struct regmap *dev_get_regmap(struct device_d *dev, const char *name);
+struct regmap *of_node_to_regmap(struct device_node *node);
+
+int regmap_register_cdev(struct regmap *map, const char *name);
+
+int regmap_write(struct regmap *map, unsigned int reg, unsigned int val);
+int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val);
+
+int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
+ size_t val_len);
+int regmap_bulk_write(struct regmap *map, unsigned int reg,
+ const void *val, size_t val_len);
+
+int regmap_get_val_bytes(struct regmap *map);
+int regmap_get_max_register(struct regmap *map);
+int regmap_get_reg_stride(struct regmap *map);
+
+#endif /* __REGMAP_H */
\ No newline at end of file
--
2.7.0.rc3
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
next prev parent reply other threads:[~2016-02-03 16:20 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-02-03 16:20 [PATCH] Add " Sascha Hauer
2016-02-03 16:20 ` [PATCH 1/4] bitops: Fix shift overflow in GENMASK macros Sascha Hauer
2016-02-03 16:20 ` Sascha Hauer [this message]
2016-02-03 16:20 ` [PATCH 3/4] mfd: mc13xxx: Switch to regmap support Sascha Hauer
2016-02-03 16:20 ` [PATCH 4/4] ARM: i.MX ocotp: " Sascha Hauer
2016-02-04 7:38 ` Stefan Christ
2016-02-04 7:43 ` Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1454516411-22858-3-git-send-email-s.hauer@pengutronix.de \
--to=s.hauer@pengutronix.de \
--cc=barebox@lists.infradead.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox