regmap: Device tree endianness support
This adds generic support for specifying endianess for register map in the DT. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJT8g7NAAoJELSic+t+oim9GC0P+wTr8/UEUvvW8tzE4XuyTWSD B+p/LwGe+ZGFxvq8bOpfVpmKzCxKvRRrjoDiKD6MyarssyO4DtSCnlz1tyJyG+Y8 tqyPX8CPe7yZfx1m8I1POt0wYgI1FphLNZ1hsLFzutNSceaQr4++YA6P3046CMl5 LrYeoekpmaIlhkG+3xMxSYamuTPPqANgMgRP63KVxbjxGAxFjMPV5N4+HY1S2kDc M00FwH+hbwx7T10+Rp0saGRKPCJCWwFfvLAjfbGo1O5dIepj92qOKEdmS+b1DrNv 9tjw0cKmKgemzkZotF3mnVSPjm7Rn1MkV8jisbwBXvvZ2P3oI9rbn83Ot8Dw3bzM X77PogFG1wEhQScfBxfyZxFDc02VKmBiZtzxmDjHrHjZKCZm7vdtg1um8N1tboQW 5A1p3IaML4LtXZrk6S6eDlA78ZFC2aSRbsrzLHMftUOwH3/SQZeR11HqenkABDxN J6liiy+AcDhDX+hcYmtqKffbsSyHcK09RfxBKTKunDMeNFsybHea+wbWaXnRsNfv tWTqJipn8anXVgy3j1cIbCd5mixxVCXleCfIzfhzPoPt3DQSWj6gRtjHntWXsN4R OxAc/zi8smirmrD4R9+0n+/E7OJhgSk8OY8tfDAymnyr2r73Q68uCPCGWHcBODeF 2zTxC8Xag7SN08R5WQcJ =5ERR -----END PGP SIGNATURE----- Merge tag 'dt-endian' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap into spi-fsl-dspi regmap: Device tree endianness support This adds generic support for specifying endianess for register map in the DT.
This commit is contained in:
Коммит
dc8fcd7116
|
@ -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;
|
||||
};
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
@ -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;
|
||||
ret = of_regmap_get_endian(dev, bus, config, REGMAP_ENDIAN_REG,
|
||||
®_endian);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
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_VAL,
|
||||
&val_endian);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
switch (config->reg_bits + map->reg_shift) {
|
||||
case 2:
|
||||
|
|
Загрузка…
Ссылка в новой задаче