For many drivers which will support rich endianness of CPU<-->Dev
need define DT properties by itself without the binding support.

The value endianness using regmap-mmio, for example:
Index    CPU       Device     Endianess flag for DT property
------------------------------------------------------------
1        LE        LE         -
2        LE        BE         'little-endian'
3        BE        BE         -
4        BE        LE         'big-endian'

============

Here add DT endianness binding support will define two string
properties of the register and value endiannesses:

'reg-endian' and 'val-endian'.

And the value of them will be:
'LE' : REGMAP_ENDIAN_LITTLE
'BE' : REGMAP_ENDIAN_BIG
'NT' : REGMAP_ENDIAN_NATIVE
Absent : REGMAP_ENDIAN_DEFAULT

The value endianness using regmap-mmio, for example:
Index    CPU       Device     Endianess flag for DT property
------------------------------------------------------------
1        LE        LE         'NT' or absent
2        LE        BE         'LE'
3        BE        BE         'NT' or absent
4        BE        LE         'BE'

Please see the following documetation for detail usage:
    Documentation/devicetree/bindings/regmap/regmap-endianness.txt

Signed-off-by: Xiubo Li <[email protected]>
---
 drivers/base/regmap/regmap.c | 91 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 80 insertions(+), 11 deletions(-)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index 8e8cea1..36a96cc 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -15,6 +15,7 @@
 #include <linux/export.h>
 #include <linux/mutex.h>
 #include <linux/err.h>
+#include <linux/of.h>
 #include <linux/rbtree.h>
 #include <linux/sched.h>
 
@@ -423,6 +424,78 @@ static void regmap_range_exit(struct regmap *map)
 }
 
 /**
+ * of_regmap_endian_get_by_name() - Parse and lookup the endianness referenced
+ * by a device node
+ * @np: pointer to clock consumer node
+ * @name: name of consumer's endianness input
+ *
+ * This function parses the device endianness property, and uses them to
+ * determine the endianness of the registers and values.
+ */
+static int of_regmap_endian_get_by_name(struct device_node *np,
+                                       const char *endian_name,
+                                       enum regmap_endian *out_endian)
+{
+       const char *endianness;
+       int ret;
+
+       if (!out_endian)
+               return -EINVAL;
+
+       /* Set the default endianness */
+       *out_endian = REGMAP_ENDIAN_DEFAULT;
+
+       /* Absent or being to use the flag from config of the drivers */
+       if (!of_find_property(np, endian_name, NULL))
+               return 0;
+
+       ret = of_property_read_string(np, endian_name, &endianness);
+       if (ret)
+               return ret;
+
+       if (!strcmp("LE", endianness))
+               *out_endian = REGMAP_ENDIAN_LITTLE;
+       else if (!strcmp("BE", endianness))
+               *out_endian = REGMAP_ENDIAN_BIG;
+       else if (!strcmp("NT", endianness))
+               *out_endian = REGMAP_ENDIAN_NATIVE;
+
+       return 0;
+}
+
+static int of_regmap_get_endian(struct device *dev,
+                               const struct regmap_bus *bus,
+                               const struct regmap_config *config,
+                               const char *endian_name,
+                               enum regmap_endian *out_endian)
+{
+       int ret;
+
+       if (!out_endian)
+               return -EINVAL;
+
+       if (dev) {
+               ret = of_regmap_endian_get_by_name(dev->of_node, endian_name,
+                                                  out_endian);
+               if (ret)
+                       return ret;
+       }
+
+       /* To be compatible with the none DT or the old drivers */
+       if (*out_endian != REGMAP_ENDIAN_DEFAULT)
+               return 0;
+
+       /* Parsing the endianness from driver's config or bus */
+       *out_endian = config->reg_format_endian;
+       if (*out_endian == REGMAP_ENDIAN_DEFAULT)
+               *out_endian = bus->reg_format_endian_default;
+       if (*out_endian == REGMAP_ENDIAN_DEFAULT)
+               *out_endian = REGMAP_ENDIAN_BIG;
+
+       return 0;
+}
+
+/**
  * regmap_init(): Initialise register map
  *
  * @dev: Device that will be interacted with
@@ -518,17 +591,13 @@ 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, "reg_endian", &reg_endian);
+       if (ret)
+               return ERR_PTR(ret);
+
+       ret = of_regmap_get_endian(dev, bus, config, "val_endian", &val_endian);
+       if (ret)
+               return ERR_PTR(ret);
 
        switch (config->reg_bits + map->reg_shift) {
        case 2:
-- 
1.8.4


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to