diff options
| author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-05-11 13:59:58 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-07-23 02:56:03 -0400 |
| commit | b83a313bf2520183641cf485d68cc273323597d2 (patch) | |
| tree | e478834902bba3d4c774804d55a88c9b50bca2c0 | |
| parent | 620917de59eeb934b9f8cf35cc2d95c1ac8ed0fc (diff) | |
regmap: Add generic non-memory mapped register access API
There are many places in the tree where we implement register access for
devices on non-memory mapped buses, especially I2C and SPI. Since hardware
designers seem to have settled on a relatively consistent set of register
interfaces this can be effectively factored out into shared code. There
are a standard set of formats for marshalling data for exchange with the
device, with the actual I/O mechanisms generally being simple byte
streams.
We create an abstraction for marshaling data into formats which can be
sent on the control interfaces, and create a standard method for
plugging in actual transport underneath that.
This is mostly a refactoring and renaming of the bottom level of the
existing code for sharing register I/O which we have in ASoC. A
subsequent patch in this series converts ASoC to use this. The main
difference in interface is that reads return values by writing to a
location provided by a pointer rather than in the return value, ensuring
we can use the full range of the type for register data. We also use
unsigned types rather than ints for the same reason.
As some of the devices can have very large register maps the existing
ASoC code also contains infrastructure for managing register caches.
This cache work will be moved over in a future stage to allow for
separate review, the current patch only deals with the physical I/O.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@ti.com>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Wolfram Sang <w.sang@pengutronix.de>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
| -rw-r--r-- | MAINTAINERS | 7 | ||||
| -rw-r--r-- | drivers/base/Kconfig | 2 | ||||
| -rw-r--r-- | drivers/base/Makefile | 1 | ||||
| -rw-r--r-- | drivers/base/regmap/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
| -rw-r--r-- | drivers/base/regmap/regmap.c | 455 | ||||
| -rw-r--r-- | include/linux/regmap.h | 74 |
7 files changed, 546 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 187282da9213..b7eaf6393d36 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
| @@ -5313,6 +5313,13 @@ L: reiserfs-devel@vger.kernel.org | |||
| 5313 | S: Supported | 5313 | S: Supported |
| 5314 | F: fs/reiserfs/ | 5314 | F: fs/reiserfs/ |
| 5315 | 5315 | ||
| 5316 | REGISTER MAP ABSTRACTION | ||
| 5317 | M: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
| 5318 | T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git | ||
| 5319 | S: Supported | ||
| 5320 | F: drivers/base/regmap/ | ||
| 5321 | F: include/linux/regmap.h | ||
| 5322 | |||
| 5316 | RFKILL | 5323 | RFKILL |
| 5317 | M: Johannes Berg <johannes@sipsolutions.net> | 5324 | M: Johannes Berg <johannes@sipsolutions.net> |
| 5318 | L: linux-wireless@vger.kernel.org | 5325 | L: linux-wireless@vger.kernel.org |
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index d57e8d0fb823..b605d01f5d45 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig | |||
| @@ -168,4 +168,6 @@ config SYS_HYPERVISOR | |||
| 168 | bool | 168 | bool |
| 169 | default n | 169 | default n |
| 170 | 170 | ||
| 171 | source "drivers/base/regmap/Kconfig" | ||
| 172 | |||
| 171 | endmenu | 173 | endmenu |
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 4c5701c15f53..dd4f9b245fc1 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile | |||
| @@ -18,6 +18,7 @@ ifeq ($(CONFIG_SYSFS),y) | |||
| 18 | obj-$(CONFIG_MODULES) += module.o | 18 | obj-$(CONFIG_MODULES) += module.o |
| 19 | endif | 19 | endif |
| 20 | obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o | 20 | obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o |
| 21 | obj-$(CONFIG_REGMAP) += regmap/ | ||
| 21 | 22 | ||
| 22 | ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG | 23 | ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG |
| 23 | 24 | ||
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig new file mode 100644 index 000000000000..fc0eb1d484dd --- /dev/null +++ b/drivers/base/regmap/Kconfig | |||
| @@ -0,0 +1,6 @@ | |||
| 1 | # Generic register map support. There are no user servicable options here, | ||
| 2 | # this is an API intended to be used by other kernel subsystems. These | ||
| 3 | # subsystems should select the appropriate symbols. | ||
| 4 | |||
| 5 | config REGMAP | ||
| 6 | bool | ||
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile new file mode 100644 index 000000000000..7532e13e0f00 --- /dev/null +++ b/drivers/base/regmap/Makefile | |||
| @@ -0,0 +1 @@ | |||
| obj-$(CONFIG_REGMAP) += regmap.o | |||
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c new file mode 100644 index 000000000000..cf3565cae93d --- /dev/null +++ b/drivers/base/regmap/regmap.c | |||
| @@ -0,0 +1,455 @@ | |||
| 1 | /* | ||
| 2 | * Register map access API | ||
| 3 | * | ||
| 4 | * Copyright 2011 Wolfson Microelectronics plc | ||
| 5 | * | ||
| 6 | * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> | ||
| 7 | * | ||
| 8 | * This program is free software; you can redistribute it and/or modify | ||
| 9 | * it under the terms of the GNU General Public License version 2 as | ||
| 10 | * published by the Free Software Foundation. | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include <linux/slab.h> | ||
| 14 | #include <linux/module.h> | ||
| 15 | #include <linux/mutex.h> | ||
| 16 | #include <linux/err.h> | ||
| 17 | |||
| 18 | #include <linux/regmap.h> | ||
| 19 | |||
| 20 | struct regmap; | ||
| 21 | |||
| 22 | struct regmap_format { | ||
| 23 | size_t buf_size; | ||
| 24 | size_t reg_bytes; | ||
| 25 | size_t val_bytes; | ||
| 26 | void (*format_write)(struct regmap *map, | ||
| 27 | unsigned int reg, unsigned int val); | ||
| 28 | void (*format_reg)(void *buf, unsigned int reg); | ||
| 29 | void (*format_val)(void *buf, unsigned int val); | ||
| 30 | unsigned int (*parse_val)(void *buf); | ||
| 31 | }; | ||
| 32 | |||
| 33 | struct regmap { | ||
| 34 | struct mutex lock; | ||
| 35 | |||
| 36 | struct device *dev; /* Device we do I/O on */ | ||
| 37 | void *work_buf; /* Scratch buffer used to format I/O */ | ||
| 38 | struct regmap_format format; /* Buffer format */ | ||
| 39 | const struct regmap_bus *bus; | ||
| 40 | }; | ||
| 41 | |||
| 42 | static void regmap_format_4_12_write(struct regmap *map, | ||
| 43 | unsigned int reg, unsigned int val) | ||
| 44 | { | ||
| 45 | __be16 *out = map->work_buf; | ||
| 46 | *out = cpu_to_be16((reg << 12) | val); | ||
| 47 | } | ||
| 48 | |||
| 49 | static void regmap_format_7_9_write(struct regmap *map, | ||
| 50 | unsigned int reg, unsigned int val) | ||
| 51 | { | ||
| 52 | __be16 *out = map->work_buf; | ||
| 53 | *out = cpu_to_be16((reg << 9) | val); | ||
| 54 | } | ||
| 55 | |||
| 56 | static void regmap_format_8(void *buf, unsigned int val) | ||
| 57 | { | ||
| 58 | u8 *b = buf; | ||
| 59 | |||
| 60 | b[0] = val; | ||
| 61 | } | ||
| 62 | |||
| 63 | static void regmap_format_16(void *buf, unsigned int val) | ||
| 64 | { | ||
| 65 | __be16 *b = buf; | ||
| 66 | |||
| 67 | b[0] = cpu_to_be16(val); | ||
| 68 | } | ||
| 69 | |||
| 70 | static unsigned int regmap_parse_8(void *buf) | ||
| 71 | { | ||
| 72 | u8 *b = buf; | ||
| 73 | |||
| 74 | return b[0]; | ||
| 75 | } | ||
| 76 | |||
| 77 | static unsigned int regmap_parse_16(void *buf) | ||
| 78 | { | ||
| 79 | __be16 *b = buf; | ||
| 80 | |||
| 81 | b[0] = be16_to_cpu(b[0]); | ||
| 82 | |||
| 83 | return b[0]; | ||
| 84 | } | ||
| 85 | |||
| 86 | /** | ||
| 87 | * regmap_init(): Initialise register map | ||
| 88 | * | ||
| 89 | * @dev: Device that will be interacted with | ||
| 90 | * @bus: Bus-specific callbacks to use with device | ||
| 91 | * @config: Configuration for register map | ||
| 92 | * | ||
| 93 | * The return value will be an ERR_PTR() on error or a valid pointer to | ||
| 94 | * a struct regmap. This function should generally not be called | ||
| 95 | * directly, it should be called by bus-specific init functions. | ||
| 96 | */ | ||
| 97 | struct regmap *regmap_init(struct device *dev, | ||
| 98 | const struct regmap_bus *bus, | ||
| 99 | const struct regmap_config *config) | ||
| 100 | { | ||
| 101 | struct regmap *map; | ||
| 102 | int ret = -EINVAL; | ||
| 103 | |||
| 104 | if (!bus || !config) | ||
| 105 | return NULL; | ||
| 106 | |||
| 107 | map = kzalloc(sizeof(*map), GFP_KERNEL); | ||
| 108 | if (map == NULL) { | ||
| 109 | ret = -ENOMEM; | ||
| 110 | goto err; | ||
| 111 | } | ||
| 112 | |||
| 113 | mutex_init(&map->lock); | ||
| 114 | map->format.buf_size = (config->reg_bits + config->val_bits) / 8; | ||
| 115 | map->format.reg_bytes = config->reg_bits / 8; | ||
| 116 | map->format.val_bytes = config->val_bits / 8; | ||
| 117 | map->dev = dev; | ||
| 118 | map->bus = bus; | ||
| 119 | |||
| 120 | switch (config->reg_bits) { | ||
| 121 | case 4: | ||
| 122 | switch (config->val_bits) { | ||
| 123 | case 12: | ||
| 124 | map->format.format_write = regmap_format_4_12_write; | ||
| 125 | break; | ||
| 126 | default: | ||
| 127 | goto err_map; | ||
| 128 | } | ||
| 129 | break; | ||
| 130 | |||
| 131 | case 7: | ||
| 132 | switch (config->val_bits) { | ||
| 133 | case 9: | ||
| 134 | map->format.format_write = regmap_format_7_9_write; | ||
| 135 | break; | ||
| 136 | default: | ||
| < | |||
