From d647c199510c2c126ac03ecbea51086e10126a40 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Tue, 15 Jul 2014 12:23:02 +0800
Subject: [PATCH 01/14] regmap: add DT endianness binding support.
For many drivers which will support rich endianness of Devices
need define DT properties by itself with the binding support.
The endianness using regmap:
Index Device Properties if needs bytes-swap,
or just ignore it
-------------------------------------------------------------
1 BE 'big-endian'
2 LE 'little-endian'
The properties include all the register values and the buffers.
And these properties are very usful for the MMIO devices:
Such as: a memory-mapped device, on one SoC is in BE mode, while
in another SoC will be in LE mode, and the CPU will always in LE
mode.
For the first case, we must use cpu_to_be32/be32_to_cpu for
32-bit registers accessing, so the 'big-endian' property is needed.
For the second case, we can just ignore the bytes-swap
functions like cpu_to_le32/le32_to_cpu, so the 'little-endian'
property could be abscent.
And vice versa...
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
drivers/base/regmap/regmap-i2c.c | 2 +
drivers/base/regmap/regmap-spi.c | 2 +
drivers/base/regmap/regmap.c | 117 ++++++++++++++++++++++++++++---
3 files changed, 110 insertions(+), 11 deletions(-)
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ca193d1ef47cb..053150a7f9f27 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -168,6 +168,8 @@ static struct regmap_bus regmap_i2c = {
.write = regmap_i2c_write,
.gather_write = regmap_i2c_gather_write,
.read = regmap_i2c_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
};
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
diff --git a/drivers/base/regmap/regmap-spi.c b/drivers/base/regmap/regmap-spi.c
index 0eb3097c0d764..53d1148e80a05 100644
--- a/drivers/base/regmap/regmap-spi.c
+++ b/drivers/base/regmap/regmap-spi.c
@@ -109,6 +109,8 @@ static struct regmap_bus regmap_spi = {
.async_alloc = regmap_spi_async_alloc,
.read = regmap_spi_read,
.read_flag_mask = 0x80,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
};
/**
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 78f43fb2fe846..e4e567e82b848 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -15,6 +15,7 @@
#include
#include
#include
+#include
#include
#include
@@ -448,6 +449,102 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
}
EXPORT_SYMBOL_GPL(regmap_attach_dev);
+enum regmap_endian_type {
+ REGMAP_ENDIAN_REG,
+ REGMAP_ENDIAN_VAL,
+};
+
+static int of_regmap_get_endian(struct device *dev,
+ const struct regmap_bus *bus,
+ const struct regmap_config *config,
+ enum regmap_endian_type type,
+ enum regmap_endian *endian)
+{
+ struct device_node *np = dev->of_node;
+
+ if (!endian || !config)
+ return -EINVAL;
+
+ /*
+ * Firstly, try to parse the endianness from driver's config,
+ * this is to be compatible with the none DT or the old drivers.
+ * From the driver's config the endianness value maybe:
+ * REGMAP_ENDIAN_BIG,
+ * REGMAP_ENDIAN_LITTLE,
+ * REGMAP_ENDIAN_NATIVE,
+ * REGMAP_ENDIAN_DEFAULT.
+ */
+ switch (type) {
+ case REGMAP_ENDIAN_REG:
+ *endian = config->reg_format_endian;
+ break;
+ case REGMAP_ENDIAN_VAL:
+ *endian = config->val_format_endian;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * If the endianness parsed from driver config is
+ * REGMAP_ENDIAN_DEFAULT, that means maybe we are using the DT
+ * node to specify the endianness information.
+ */
+ if (*endian != REGMAP_ENDIAN_DEFAULT)
+ return 0;
+
+ /*
+ * Secondly, try to parse the endianness from DT node if the
+ * driver config does not specify it.
+ * From the DT node the endianness value maybe:
+ * REGMAP_ENDIAN_BIG,
+ * REGMAP_ENDIAN_LITTLE,
+ * REGMAP_ENDIAN_NATIVE,
+ */
+ switch (type) {
+ case REGMAP_ENDIAN_VAL:
+ 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
+ *endian = REGMAP_ENDIAN_NATIVE;
+ break;
+ case REGMAP_ENDIAN_REG:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * If the endianness parsed from DT node is REGMAP_ENDIAN_NATIVE, that
+ * maybe means the DT does not care the endianness or it should use
+ * the regmap bus's default endianness, then we should try to check
+ * whether the regmap bus has specified the default endianness.
+ */
+ if (*endian != REGMAP_ENDIAN_NATIVE)
+ return 0;
+
+ /*
+ * Finally, try to parse the endianness from regmap bus config
+ * if in device's DT node the endianness property is absent.
+ */
+ switch (type) {
+ case REGMAP_ENDIAN_REG:
+ if (bus && bus->reg_format_endian_default)
+ *endian = bus->reg_format_endian_default;
+ break;
+ case REGMAP_ENDIAN_VAL:
+ if (bus && bus->val_format_endian_default)
+ *endian = bus->val_format_endian_default;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
/**
* regmap_init(): Initialise register map
*
@@ -551,17 +648,15 @@ struct regmap *regmap_init(struct device *dev,
map->reg_read = _regmap_bus_read;
}
- reg_endian = config->reg_format_endian;
- if (reg_endian == REGMAP_ENDIAN_DEFAULT)
- reg_endian = bus->reg_format_endian_default;
- if (reg_endian == REGMAP_ENDIAN_DEFAULT)
- reg_endian = REGMAP_ENDIAN_BIG;
-
- val_endian = config->val_format_endian;
- if (val_endian == REGMAP_ENDIAN_DEFAULT)
- val_endian = bus->val_format_endian_default;
- if (val_endian == REGMAP_ENDIAN_DEFAULT)
- val_endian = REGMAP_ENDIAN_BIG;
+ ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG,
+ ®_endian);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL,
+ &val_endian);
+ if (ret)
+ return ERR_PTR(ret);
switch (config->reg_bits + map->reg_shift) {
case 2:
From 275876e208e28abf4b96ec89030e482b1331ee75 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Tue, 15 Jul 2014 12:23:03 +0800
Subject: [PATCH 02/14] regmap: Add the DT binding documentation for endianness
Device-Tree binding for device endianness
Index Device Endianness properties
---------------------------------------------------
1 BE 'big-endian'
2 LE 'little-endian'
For one device driver, which will run in different scenarios above
on different SoCs using the devicetree, we need one way to simplify
this.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
---
.../devicetree/bindings/regmap/regmap.txt | 47 +++++++++++++++++++
1 file changed, 47 insertions(+)
create mode 100644 Documentation/devicetree/bindings/regmap/regmap.txt
diff --git a/Documentation/devicetree/bindings/regmap/regmap.txt b/Documentation/devicetree/bindings/regmap/regmap.txt
new file mode 100644
index 0000000000000..b494f8b8ef72d
--- /dev/null
+++ b/Documentation/devicetree/bindings/regmap/regmap.txt
@@ -0,0 +1,47 @@
+Device-Tree binding for regmap
+
+The endianness mode of CPU & Device scenarios:
+Index Device Endianness properties
+---------------------------------------------------
+1 BE 'big-endian'
+2 LE 'little-endian'
+
+For one device driver, which will run in different scenarios above
+on different SoCs using the devicetree, we need one way to simplify
+this.
+
+Required properties:
+- {big,little}-endian: these are boolean properties, if absent
+ meaning that the CPU and the Device are in the same endianness mode,
+ these properties are for register values and all the buffers only.
+
+Examples:
+Scenario 1 : CPU in LE mode & device in LE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+};
+
+Scenario 2 : CPU in LE mode & device in BE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ big-endian;
+};
+
+Scenario 3 : CPU in BE mode & device in BE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+};
+
+Scenario 4 : CPU in BE mode & device in LE mode.
+dev: dev@40031000 {
+ compatible = "name";
+ reg = <0x40031000 0x1000>;
+ ...
+ little-endian;
+};
From dd060bc92748ce77231b2cd2657510b77cd94dea Mon Sep 17 00:00:00 2001
From: Mark Brown
Date: Sat, 16 Aug 2014 12:30:58 +0100
Subject: [PATCH 03/14] regmap: Restore L: linux-kernel@vger.kernel.org entry
As with commit 981c3a4ff85 (MAINTAINERS: Restore "L:
linux-kernel@vger.kernel.org" entries) restore the mailing list entry
for the regmap framework in order to assist users in finding the list
if they read the file instead of using get_maintainers.pl.
Signed-off-by: Mark Brown
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index aefa94841ff3e..0bb827ac690bf 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7550,6 +7550,7 @@ F: fs/reiserfs/
REGISTER MAP ABSTRACTION
M: Mark Brown
+L: linux-kernel@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git
S: Supported
F: drivers/base/regmap/
From 9ba1e456e1fa3729fc6be73403a7b2083f9590eb Mon Sep 17 00:00:00 2001
From: Geert Uytterhoeven
Date: Sun, 17 Aug 2014 12:08:57 +0200
Subject: [PATCH 04/14] regmap: Add explicit dependencies to catch "select"
misuse
Add explicit dependencies for the various regmap modules, so Kconfig
will print a warning message when another module selects a regmap module
without fulfilling its dependencies.
Without this, it's much more difficult to find out which module did the
offending select.
Signed-off-by: Geert Uytterhoeven
Signed-off-by: Mark Brown
---
drivers/base/regmap/Kconfig | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index 4251570610c9f..8a3f51f7b1b91 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -11,12 +11,15 @@ config REGMAP
config REGMAP_I2C
tristate
+ depends on I2C
config REGMAP_SPI
tristate
+ depends on SPI
config REGMAP_SPMI
tristate
+ depends on SPMI
config REGMAP_MMIO
tristate
From ba1b53feb8cacbd84bcf0e48925e30ad29e141a6 Mon Sep 17 00:00:00 2001
From: Javier Martinez Canillas
Date: Mon, 18 Aug 2014 15:09:02 +0200
Subject: [PATCH 05/14] regmap: Fix DT endianess parsing logic
Commit d647c199510c ("regmap: add DT endianness binding support.")
added support to parse the device endianness from the device tree
but unfortunately the added logic doesn't have the same semantics
than the old code. This leads to a NULL dereference pointer error
when these properties are not provided by the Device Tree:
Unable to handle kernel NULL pointer dereference at virtual address 00000044
pgd = c0004000
[00000044] *pgd=00000000
Internal error: Oops: 5 [#1] PREEMPT SMP ARM
Modules linked in:
CPU: 5 PID: 1 Comm: swapper/0 Not tainted 3.17.0-rc1-next-20140818ccu #671
task: ea412800 ti: ea484000 task.ti: ea484000
PC is at regmap_update_bits+0xc/0x5c
The problem is that platforms that rely on the default value now
gets different values due two related issues in the current code:
a) It only parses the endianness from DT for the regmap registers
and not for the regmap values but it checks unconditionally in
both cases if the resulting endiannes is REGMAP_ENDIAN_NATIVE.
b) REGMAP_ENDIAN_NATIVE is not even a valid DT property according
to the regmap DT binding documentation so it shouldn't be set.
Signed-off-by: Javier Martinez Canillas
Signed-off-by: Mark Brown
---
drivers/base/regmap/regmap.c | 16 ++++------------
1 file changed, 4 insertions(+), 12 deletions(-)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index e4e567e82b848..055a9c3a3b12a 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -499,7 +499,6 @@ static int of_regmap_get_endian(struct device *dev,
* From the DT node the endianness value maybe:
* REGMAP_ENDIAN_BIG,
* REGMAP_ENDIAN_LITTLE,
- * REGMAP_ENDIAN_NATIVE,
*/
switch (type) {
case REGMAP_ENDIAN_VAL:
@@ -507,8 +506,10 @@ static int of_regmap_get_endian(struct device *dev,
*endian = REGMAP_ENDIAN_BIG;
else if (of_property_read_bool(np, "little-endian"))
*endian = REGMAP_ENDIAN_LITTLE;
- else
- *endian = REGMAP_ENDIAN_NATIVE;
+
+ if (*endian != REGMAP_ENDIAN_DEFAULT)
+ return 0;
+
break;
case REGMAP_ENDIAN_REG:
break;
@@ -516,15 +517,6 @@ static int of_regmap_get_endian(struct device *dev,
return -EINVAL;
}
- /*
- * If the endianness parsed from DT node is REGMAP_ENDIAN_NATIVE, that
- * maybe means the DT does not care the endianness or it should use
- * the regmap bus's default endianness, then we should try to check
- * whether the regmap bus has specified the default endianness.
- */
- if (*endian != REGMAP_ENDIAN_NATIVE)
- return 0;
-
/*
* Finally, try to parse the endianness from regmap bus config
* if in device's DT node the endianness property is absent.
From 45e1a279ce1d2ff9b2b2fedf4cdced10c7ca3ab5 Mon Sep 17 00:00:00 2001
From: Stephen Warren
Date: Tue, 19 Aug 2014 10:49:07 -0600
Subject: [PATCH 06/14] regmap: of_regmap_get_endian() cleanup
Commit d647c199510c ("regmap: add DT endianness binding support") had
some issues. Commit ba1b53feb8ca ("regmap: Fix DT endianess parsing
logic") fixed the main problem. This patch fixes the other.
Specifically, restore the overall default of REGMAP_ENDIAN_BIG if none of
the config, DT, or the bus specify any endianness. Without this,
of_regmap_get_endian() could return REGMAP_ENDIAN_DEFAULT, which the
calling code can't handle. Since all busses do specify an endianness in
the current code, this makes no difference right now, but I saw no
justification in the patch description for removing this final default.
Also, clean up the code a bit:
* s/of_regmap_get_endian/regmap_get_endian/ since the function isn't DT-
specific, even if the reason it was originally added was to add some
DT-specific features.
* After potentially reading an endianess specification from DT, the code
checks whether DT did specify an endianness, and if so, returns it. Move
this test outside the whole switch statement so that if the
REGMAP_ENDIAN_REG case ever modifies *endian, this check will pick that
up. This partially reverts part of commit ba1b53feb8ca ("regmap: Fix DT
endianess parsing logic"), while maintaining the bug-fix that commit
made to this code.
* Make the comments briefer, and only refer to the specific action taken
at their location. This makes most of the comments independent of DT,
and easier to follow.
Cc: Xiubo Li
Cc: Javier Martinez Canillas
Cc: Thierry Reding
Fixes: d647c199510c ("regmap: add DT endianness binding support")
Signed-off-by: Stephen Warren
Signed-off-by: Mark Brown
---
drivers/base/regmap/regmap.c | 54 +++++++++++++-----------------------
1 file changed, 20 insertions(+), 34 deletions(-)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 055a9c3a3b12a..bb4502a48be56 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -454,7 +454,7 @@ enum regmap_endian_type {
REGMAP_ENDIAN_VAL,
};
-static int of_regmap_get_endian(struct device *dev,
+static int regmap_get_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config,
enum regmap_endian_type type,
@@ -465,15 +465,7 @@ static int of_regmap_get_endian(struct device *dev,
if (!endian || !config)
return -EINVAL;
- /*
- * Firstly, try to parse the endianness from driver's config,
- * this is to be compatible with the none DT or the old drivers.
- * From the driver's config the endianness value maybe:
- * REGMAP_ENDIAN_BIG,
- * REGMAP_ENDIAN_LITTLE,
- * REGMAP_ENDIAN_NATIVE,
- * REGMAP_ENDIAN_DEFAULT.
- */
+ /* Retrieve the endianness specification from the regmap config */
switch (type) {
case REGMAP_ENDIAN_REG:
*endian = config->reg_format_endian;
@@ -485,31 +477,17 @@ static int of_regmap_get_endian(struct device *dev,
return -EINVAL;
}
- /*
- * If the endianness parsed from driver config is
- * REGMAP_ENDIAN_DEFAULT, that means maybe we are using the DT
- * node to specify the endianness information.
- */
+ /* If the regmap config specified a non-default value, use that */
if (*endian != REGMAP_ENDIAN_DEFAULT)
return 0;
- /*
- * Secondly, try to parse the endianness from DT node if the
- * driver config does not specify it.
- * From the DT node the endianness value maybe:
- * REGMAP_ENDIAN_BIG,
- * REGMAP_ENDIAN_LITTLE,
- */
+ /* Parse the device's DT node for an endianness specification */
switch (type) {
case REGMAP_ENDIAN_VAL:
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;
-
- if (*endian != REGMAP_ENDIAN_DEFAULT)
- return 0;
-
break;
case REGMAP_ENDIAN_REG:
break;
@@ -517,10 +495,11 @@ static int of_regmap_get_endian(struct device *dev,
return -EINVAL;
}
- /*
- * Finally, try to parse the endianness from regmap bus config
- * if in device's DT node the endianness property is absent.
- */
+ /* If the endianness was specified in DT, use that */
+ if (*endian != REGMAP_ENDIAN_DEFAULT)
+ return 0;
+
+ /* Retrieve the endianness specification from the bus config */
switch (type) {
case REGMAP_ENDIAN_REG:
if (bus && bus->reg_format_endian_default)
@@ -534,6 +513,13 @@ static int of_regmap_get_endian(struct device *dev,
return -EINVAL;
}
+ /* If the bus specified a non-default value, use that */
+ if (*endian != REGMAP_ENDIAN_DEFAULT)
+ return 0;
+
+ /* Use this if no other value was found */
+ *endian = REGMAP_ENDIAN_BIG;
+
return 0;
}
@@ -640,13 +626,13 @@ struct regmap *regmap_init(struct device *dev,
map->reg_read = _regmap_bus_read;
}
- ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG,
- ®_endian);
+ ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG,
+ ®_endian);
if (ret)
return ERR_PTR(ret);
- ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL,
- &val_endian);
+ ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL,
+ &val_endian);
if (ret)
return ERR_PTR(ret);
From cf673fbc6342b1c2310cdfdc4ed99f18f866b8e4 Mon Sep 17 00:00:00 2001
From: Geert Uytterhoeven
Date: Wed, 27 Aug 2014 16:36:03 +0200
Subject: [PATCH 07/14] regmap: Split regmap_get_endian() in two functions
Split regmap_get_endian() in two functions, regmap_get_reg_endian() and
regmap_get_val_endian().
This allows to:
- Get rid of the three switch()es on "type", incl. error handling in
three "default" cases,
- Get rid of the regmap_endian_type enum,
- Get rid of the non-NULL check of "config" (regmap_init() already
checks for that),
- Get rid of the "endian" output parameters, and just return the
regmap_endian enum value, as the functions can no longer fail.
This saves 21 lines of code (despite the still-present
one-comment-per-line over-documentation), and 30 bytes of code on ARM
V7.
Signed-off-by: Geert Uytterhoeven
Reviewed-by: Stephen Warren
Signed-off-by: Mark Brown
---
drivers/base/regmap/regmap.c | 107 ++++++++++++++---------------------
1 file changed, 43 insertions(+), 64 deletions(-)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index bb4502a48be56..01ae4b829360e 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -449,78 +449,64 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
}
EXPORT_SYMBOL_GPL(regmap_attach_dev);
-enum regmap_endian_type {
- REGMAP_ENDIAN_REG,
- REGMAP_ENDIAN_VAL,
-};
+static enum regmap_endian regmap_get_reg_endian(const struct regmap_bus *bus,
+ const struct regmap_config *config)
+{
+ enum regmap_endian endian;
-static int regmap_get_endian(struct device *dev,
- const struct regmap_bus *bus,
- const struct regmap_config *config,
- enum regmap_endian_type type,
- 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 enum regmap_endian regmap_get_val_endian(struct device *dev,
+ const struct regmap_bus *bus,
+ const struct regmap_config *config)
{
struct device_node *np = dev->of_node;
-
- if (!endian || !config)
- return -EINVAL;
+ enum regmap_endian endian;
/* Retrieve the endianness specification from the regmap config */
- switch (type) {
- case REGMAP_ENDIAN_REG:
- *endian = config->reg_format_endian;
- break;
- case REGMAP_ENDIAN_VAL:
- *endian = config->val_format_endian;
- break;
- default:
- return -EINVAL;
- }
+ endian = config->val_format_endian;
/* If the regmap config specified a non-default value, use that */
- if (*endian != REGMAP_ENDIAN_DEFAULT)
- return 0;
+ if (endian != REGMAP_ENDIAN_DEFAULT)
+ return endian;
/* Parse the device's DT node for an endianness specification */
- switch (type) {
- case REGMAP_ENDIAN_VAL:
- 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;
- break;
- case REGMAP_ENDIAN_REG:
- break;
- default:
- return -EINVAL;
- }
+ 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;
/* If the endianness was specified in DT, use that */
- if (*endian != REGMAP_ENDIAN_DEFAULT)
- return 0;
+ if (endian != REGMAP_ENDIAN_DEFAULT)
+ return endian;
/* Retrieve the endianness specification from the bus config */
- switch (type) {
- case REGMAP_ENDIAN_REG:
- if (bus && bus->reg_format_endian_default)
- *endian = bus->reg_format_endian_default;
- break;
- case REGMAP_ENDIAN_VAL:
- if (bus && bus->val_format_endian_default)
- *endian = bus->val_format_endian_default;
- break;
- default:
- return -EINVAL;
- }
+ 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 0;
+ if (endian != REGMAP_ENDIAN_DEFAULT)
+ return endian;
/* Use this if no other value was found */
- *endian = REGMAP_ENDIAN_BIG;
-
- return 0;
+ return REGMAP_ENDIAN_BIG;
}
/**
@@ -626,15 +612,8 @@ struct regmap *regmap_init(struct device *dev,
map->reg_read = _regmap_bus_read;
}
- ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG,
- ®_endian);
- if (ret)
- return ERR_PTR(ret);
-
- ret = regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_VAL,
- &val_endian);
- if (ret)
- return ERR_PTR(ret);
+ reg_endian = regmap_get_reg_endian(bus, config);
+ val_endian = regmap_get_val_endian(dev, bus, config);
switch (config->reg_bits + map->reg_shift) {
case 2:
From 336fb81b319ec4d5c09aa6417de7c042cfcd7461 Mon Sep 17 00:00:00 2001
From: "Wang, Yalin"
Date: Thu, 11 Sep 2014 16:19:49 +0800
Subject: [PATCH 08/14] regmap: change struct regmap's internal locks as union
this patch change struct regmap->mutex and struct regmap->spinlock
as an union, because these 2 members are only used one of them,
we change it to shrink the struct size.
Signed-off-by: Yalin Wang
Signed-off-by: Mark Brown
---
drivers/base/regmap/internal.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h
index 7d1326985bee8..f82e8faa5d938 100644
--- a/drivers/base/regmap/internal.h
+++ b/drivers/base/regmap/internal.h
@@ -49,8 +49,10 @@ struct regmap_async {
};
struct regmap {
- struct mutex mutex;
- spinlock_t spinlock;
+ union {
+ struct mutex mutex;
+ spinlock_t spinlock;
+ };
unsigned long spinlock_flags;
regmap_lock lock;
regmap_unlock unlock;
From f29a43206ae1adfadbb344f5ef9bc200784f9d61 Mon Sep 17 00:00:00 2001
From: Jarkko Nikula
Date: Tue, 16 Sep 2014 14:04:14 +0300
Subject: [PATCH 09/14] regmap: cache: Do not fail silently from regcache_sync
calls
Call stack of regcache_sync calls may not emit any error message even if
operation was cancelled due an error in I/O driver. One such a silent error
is for instance if I2C bus driver doesn't receive ACK from the I2C device
and returns -EREMOTEIO.
Since many users of regcache_sync() don't check and print the error there is
no any indication that HW registers are potentially out of sync.
Signed-off-by: Jarkko Nikula
Signed-off-by: Mark Brown
---
drivers/base/regmap/regcache.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/base/regmap/regcache.c b/drivers/base/regmap/regcache.c
index 29b4128da0b08..da7b3bfd4d7fb 100644
--- a/drivers/base/regmap/regcache.c
+++ b/drivers/base/regmap/regcache.c
@@ -269,8 +269,11 @@ static int regcache_default_sync(struct regmap *map, unsigned int min,
map->cache_bypass = 1;
ret = _regmap_write(map, reg, val);
map->cache_bypass = 0;
- if (ret)
+ if (ret) {
+ dev_err(map->dev, "Unable to sync register %#x. %d\n",
+ reg, ret);
return ret;
+ }
dev_dbg(map->dev, "Synced register %#x, value %#x\n", reg, val);
}
@@ -615,8 +618,11 @@ static int regcache_sync_block_single(struct regmap *map, void *block,
ret = _regmap_write(map, regtmp, val);
map->cache_bypass = 0;
- if (ret != 0)
+ if (ret != 0) {
+ dev_err(map->dev, "Unable to sync register %#x. %d\n",
+ regtmp, ret);
return ret;
+ }
dev_dbg(map->dev, "Synced register %#x, value %#x\n",
regtmp, val);
}
@@ -641,6 +647,9 @@ static int regcache_sync_block_raw_flush(struct regmap *map, const void **data,
map->cache_bypass = 1;
ret = _regmap_raw_write(map, base, *data, count * val_bytes);
+ if (ret)
+ dev_err(map->dev, "Unable to sync registers %#x-%#x. %d\n",
+ base, cur - map->reg_stride, ret);
map->cache_bypass = 0;
From 6e64b6ccc1e46932768e3bb8974fc2e5589bca7a Mon Sep 17 00:00:00 2001
From: Pankaj Dubey
Date: Thu, 18 Sep 2014 15:12:20 +0530
Subject: [PATCH 10/14] regmap: fix NULL pointer dereference in
regmap_get_val_endian
Recents commits for getting reg endianness causing NULL pointer
dereference if dev is passed NULL in regmap_init_mmio. This patch
fixes this issue, and allows to parse reg endianness only if dev
and dev->of_node exist.
Signed-off-by: Pankaj Dubey
Signed-off-by: Mark Brown
---
drivers/base/regmap/regmap.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 01ae4b829360e..fd7ae9a10c4d7 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -477,7 +477,7 @@ static enum regmap_endian regmap_get_val_endian(struct device *dev,
const struct regmap_bus *bus,
const struct regmap_config *config)
{
- struct device_node *np = dev->of_node;
+ struct device_node *np;
enum regmap_endian endian;
/* Retrieve the endianness specification from the regmap config */
@@ -487,15 +487,20 @@ static enum regmap_endian regmap_get_val_endian(struct device *dev,
if (endian != REGMAP_ENDIAN_DEFAULT)
return endian;
- /* 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;
+ /* If the dev and dev->of_node exist try to get endianness from DT */
+ if (dev && dev->of_node) {
+ np = dev->of_node;
- /* If the endianness was specified in DT, use that */
- if (endian != REGMAP_ENDIAN_DEFAULT)
- return endian;
+ /* 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;
+
+ /* 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)
From 5336be8416a71b5568d2cf54a2f2066abe9f2a53 Mon Sep 17 00:00:00 2001
From: Pankaj Dubey
Date: Sat, 27 Sep 2014 09:47:55 +0530
Subject: [PATCH 11/14] regmap: fix NULL pointer dereference in
_regmap_write/read
If LOG_DEVICE is defined and map->dev is NULL it will lead to NULL
pointer dereference. This patch fixes this issue by adding check for
dev->NULL in all such places in regmap.c
Signed-off-by: Pankaj Dubey
Signed-off-by: Mark Brown
Cc: stable@vger.kernel.org
---
drivers/base/regmap/regmap.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 78f43fb2fe846..b903aff59af39 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1408,7 +1408,7 @@ int _regmap_write(struct regmap *map, unsigned int reg,
}
#ifdef LOG_DEVICE
- if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
+ if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
dev_info(map->dev, "%x <= %x\n", reg, val);
#endif
@@ -2058,7 +2058,7 @@ static int _regmap_read(struct regmap *map, unsigned int reg,
ret = map->reg_read(context, reg, val);
if (ret == 0) {
#ifdef LOG_DEVICE
- if (strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
+ if (map->dev && strcmp(dev_name(map->dev), LOG_DEVICE) == 0)
dev_info(map->dev, "%x => %x\n", reg, *val);
#endif
From 2c98e0c1cc6b8e86f1978286c3d4e0769ee9d733 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Sun, 28 Sep 2014 11:35:25 +0800
Subject: [PATCH 12/14] regmap: debugfs: fix possbile NULL pointer dereference
If 'map->dev' is NULL and there will lead dev_name() to be NULL pointer
dereference. So before dev_name(), we need to have check of the map->dev
pionter.
We also should make sure that the 'name' pointer shouldn't be NULL for
debugfs_create_dir(). So here using one default "dummy" debugfs name when
the 'name' pointer and 'map->dev' are both NULL.
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
Cc: stable@vger.kernel.org
---
drivers/base/regmap/regmap-debugfs.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index 45d812c0ea775..bdd88708e4d67 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -473,6 +473,7 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
{
struct rb_node *next;
struct regmap_range_node *range_node;
+ const char *devname = "dummy";
/* If we don't have the debugfs root yet, postpone init */
if (!regmap_debugfs_root) {
@@ -491,12 +492,15 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
INIT_LIST_HEAD(&map->debugfs_off_cache);
mutex_init(&map->cache_lock);
+ if (map->dev)
+ devname = dev_name(map->dev);
+
if (name) {
map->debugfs_name = kasprintf(GFP_KERNEL, "%s-%s",
- dev_name(map->dev), name);
+ devname, name);
name = map->debugfs_name;
} else {
- name = dev_name(map->dev);
+ name = devname;
}
map->debugfs = debugfs_create_dir(name, regmap_debugfs_root);
From d6b41cb06044a7d895db82bdd54f6e4219970510 Mon Sep 17 00:00:00 2001
From: Xiubo Li
Date: Sun, 28 Sep 2014 17:09:54 +0800
Subject: [PATCH 13/14] regmap: fix possible ZERO_SIZE_PTR pointer
dereferencing error.
Since we cannot make sure the 'val_count' will always be none zero
here, and then if it equals to zero, the kmemdup() will return
ZERO_SIZE_PTR, which equals to ((void *)16).
So this patch fix this with just doing the zero check before calling
kmemdup().
Signed-off-by: Xiubo Li
Signed-off-by: Mark Brown
Cc: stable@vger.kernel.org
---
drivers/base/regmap/regmap.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index b903aff59af39..e677a1b79f95a 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -1659,6 +1659,9 @@ int regmap_bulk_write(struct regmap *map, unsigned int reg, const void *val,
} else {
void *wval;
+ if (!val_count)
+ return -EINVAL;
+
wval = kmemdup(val, val_count * val_bytes, GFP_KERNEL);
if (!wval) {
dev_err(map->dev, "Error in memory allocation\n");
From 18c0301f9898feaac38135b2cea476bb606830e3 Mon Sep 17 00:00:00 2001
From: Markus Pargmann
Date: Mon, 8 Sep 2014 08:43:37 +0200
Subject: [PATCH 14/14] regmap: Fix debugfs-file 'registers' mode
The macro "REGMAP_ALLOW_WRITE_DEBUGFS" can be used to enable write
support on the registers file in the debugfs. The mode of the file is
fixed to 0400 so it is not possible to write the file ever.
This patch fixes the mode by setting it to the correct value depending
on the macro.
Signed-off-by: Markus Pargmann
Signed-off-by: Mark Brown
---
drivers/base/regmap/regmap-debugfs.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/base/regmap/regmap-debugfs.c b/drivers/base/regmap/regmap-debugfs.c
index bdd88708e4d67..a88cbd42edf0e 100644
--- a/drivers/base/regmap/regmap-debugfs.c
+++ b/drivers/base/regmap/regmap-debugfs.c
@@ -516,7 +516,14 @@ void regmap_debugfs_init(struct regmap *map, const char *name)
map, ®map_reg_ranges_fops);
if (map->max_register || regmap_readable(map, 0)) {
- debugfs_create_file("registers", 0400, map->debugfs,
+ umode_t registers_mode;
+
+ if (IS_ENABLED(REGMAP_ALLOW_WRITE_DEBUGFS))
+ registers_mode = 0600;
+ else
+ registers_mode = 0400;
+
+ debugfs_create_file("registers", registers_mode, map->debugfs,
map, ®map_map_fops);
debugfs_create_file("access", 0400, map->debugfs,
map, ®map_access_fops);