diff mbox

[U-Boot,v3,20/54] dm: Add support for register maps (regmap)

Message ID 1435095556-15924-21-git-send-email-sjg@chromium.org
State Accepted
Delegated to: Simon Glass
Headers show

Commit Message

Simon Glass June 23, 2015, 9:38 p.m. UTC
Add a simple implementaton of register maps, supporting only direct I/O
for now. This can be enhanced later to support buses which have registers,
such as I2C, SPI and PCI.

It allows drivers which can operate with multiple buses to avoid dealing
with the particulars of register access on that bus.

Signed-off-by: Simon Glass <sjg@chromium.org>
---

Changes in v3: None
Changes in v2: None

 drivers/core/Makefile |  1 +
 drivers/core/regmap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++
 include/regmap.h      | 72 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 159 insertions(+)
 create mode 100644 drivers/core/regmap.c
 create mode 100644 include/regmap.h

Comments

Simon Glass July 17, 2015, 11:56 p.m. UTC | #1
On 23 June 2015 at 15:38, Simon Glass <sjg@chromium.org> wrote:
> Add a simple implementaton of register maps, supporting only direct I/O
> for now. This can be enhanced later to support buses which have registers,
> such as I2C, SPI and PCI.
>
> It allows drivers which can operate with multiple buses to avoid dealing
> with the particulars of register access on that bus.
>
> Signed-off-by: Simon Glass <sjg@chromium.org>
> ---
>
> Changes in v3: None
> Changes in v2: None
>
>  drivers/core/Makefile |  1 +
>  drivers/core/regmap.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  include/regmap.h      | 72 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 159 insertions(+)
>  create mode 100644 drivers/core/regmap.c
>  create mode 100644 include/regmap.h

Applied to u-boot-dm.
diff mbox

Patch

diff --git a/drivers/core/Makefile b/drivers/core/Makefile
index ed21fed..7851824 100644
--- a/drivers/core/Makefile
+++ b/drivers/core/Makefile
@@ -10,3 +10,4 @@  obj-$(CONFIG_OF_CONTROL) += simple-bus.o
 endif
 obj-$(CONFIG_DM_DEVICE_REMOVE)	+= device-remove.o
 obj-$(CONFIG_DM)	+= dump.o
+obj-$(CONFIG_OF_CONTROL)	+= regmap.o
diff --git a/drivers/core/regmap.c b/drivers/core/regmap.c
new file mode 100644
index 0000000..519832f
--- /dev/null
+++ b/drivers/core/regmap.c
@@ -0,0 +1,86 @@ 
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <libfdt.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <regmap.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int regmap_init_mem(struct udevice *dev, struct regmap **mapp)
+{
+	const void *blob = gd->fdt_blob;
+	struct regmap_range *range;
+	const fdt32_t *cell;
+	struct regmap *map;
+	int count;
+	int addr_len, size_len, both_len;
+	int parent;
+	int len;
+
+	parent = dev->parent->of_offset;
+	addr_len = fdt_address_cells(blob, parent);
+	size_len = fdt_size_cells(blob, parent);
+	both_len = addr_len + size_len;
+
+	cell = fdt_getprop(blob, dev->of_offset, "reg", &len);
+	len /= sizeof(*cell);
+	count = len / both_len;
+	if (!cell || !count)
+		return -EINVAL;
+
+	map = malloc(sizeof(struct regmap));
+	if (!map)
+		return -ENOMEM;
+
+	if (count <= 1) {
+		map->range = &map->base_range;
+	} else {
+		map->range = malloc(count * sizeof(struct regmap_range));
+		if (!map->range) {
+			free(map);
+			return -ENOMEM;
+		}
+	}
+
+	map->base = fdtdec_get_number(cell, addr_len);
+	map->range_count = count;
+
+	for (range = map->range; count > 0;
+	     count--, cell += both_len, range++) {
+		range->start = fdtdec_get_number(cell, addr_len);
+		range->size = fdtdec_get_number(cell + addr_len, size_len);
+	}
+
+	*mapp = map;
+
+	return 0;
+}
+
+void *regmap_get_range(struct regmap *map, unsigned int range_num)
+{
+	struct regmap_range *range;
+
+	if (range_num >= map->range_count)
+		return NULL;
+	range = &map->range[range_num];
+
+	return map_sysmem(range->start, range->size);
+}
+
+int regmap_uninit(struct regmap *map)
+{
+	if (map->range_count > 1)
+		free(map->range);
+	free(map);
+
+	return 0;
+}
diff --git a/include/regmap.h b/include/regmap.h
new file mode 100644
index 0000000..eccf770
--- /dev/null
+++ b/include/regmap.h
@@ -0,0 +1,72 @@ 
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __REGMAP_H
+#define __REGMAP_H
+
+/**
+ * struct regmap_range - a register map range
+ *
+ * @start:	Start address
+ * @size:	Size in bytes
+ */
+struct regmap_range {
+	ulong start;
+	ulong size;
+};
+
+/**
+ * struct regmap - a way of accessing hardware/bus registers
+ *
+ * @base:	Base address of register map
+ * @range_count: Number of ranges available within the map
+ * @range:	Pointer to the list of ranges, allocated if @range_count > 1
+ * @base_range:	If @range_count is <= 1, @range points here
+ */
+struct regmap {
+	phys_addr_t base;
+	int range_count;
+	struct regmap_range *range, base_range;
+};
+
+/*
+ * Interface to provide access to registers either through a direct memory
+ * bus or through a peripheral bus like I2C, SPI.
+ */
+int regmap_write(struct regmap *map, uint offset, uint val);
+int regmap_read(struct regmap *map, uint offset, uint *valp);
+
+#define regmap_write32(map, ptr, member, val) \
+	regmap_write(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), val)
+
+#define regmap_read32(map, ptr, member, valp) \
+	regmap_read(map, (uint32_t *)(ptr)->member - (uint32_t *)(ptr), valp)
+
+/**
+ * regmap_init_mem() - Set up a new register map that uses memory access
+ *
+ * Use regmap_uninit() to free it.
+ *
+ * @dev:	Device that uses this map
+ * @mapp:	Returns allocated map
+ */
+int regmap_init_mem(struct udevice *dev, struct regmap **mapp);
+
+/**
+ * regmap_get_range() - Obtain the base memory address of a regmap range
+ *
+ * @map:	Regmap to query
+ * @range_num:	Range to look up
+ */
+void *regmap_get_range(struct regmap *map, unsigned int range_num);
+
+/**
+ * regmap_uninit() - free a previously inited regmap
+ */
+int regmap_uninit(struct regmap *map);
+
+#endif