Skip to content

Commit

Permalink
Merge tag 'dt-endian' of git://git.kernel.org/pub/scm/linux/kernel/gi…
Browse files Browse the repository at this point in the history
…t/broonie/regmap into spi-fsl-dspi

regmap: Device tree endianness support

This adds generic support for specifying endianess for register map in
the DT.
  • Loading branch information
Mark Brown committed Aug 18, 2014
2 parents 7d1311b + 275876e commit dc8fcd7
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 11 deletions.
47 changes: 47 additions & 0 deletions Documentation/devicetree/bindings/regmap/regmap.txt
Original file line number Diff line number Diff line change
@@ -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;
};
2 changes: 2 additions & 0 deletions drivers/base/regmap/regmap-i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions drivers/base/regmap/regmap-spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

/**
Expand Down
117 changes: 106 additions & 11 deletions drivers/base/regmap/regmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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,
&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:
Expand Down

0 comments on commit dc8fcd7

Please sign in to comment.