Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dc8fcd71 authored by Mark Brown's avatar Mark Brown
Browse files

Merge tag 'dt-endian' of...

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.
parents 7d1311b9 275876e2
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
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 −0
Original line number Diff line number Diff line
@@ -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,
+2 −0
Original line number Diff line number Diff line
@@ -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,
};

/**
+106 −11
Original line number Diff line number Diff line
@@ -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,
				   &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: