[PATCH v4 03/12] bus: mvebu-mbus: Add static window allocation to the DT binding

Jason Cooper jason at lakedaemon.net
Wed Jun 19 16:14:39 EDT 2013


On Wed, Jun 19, 2013 at 05:07:17PM -0300, Ezequiel Garcia wrote:
> This patch adds static window allocation to the device tree binding.
> Each first-child of the mbus-compatible node, with a suitable 'ranges'
> property, declaring an address translation, will trigger an address
> decoding window allocation.
> 
> Signed-off-by: Ezequiel Garcia <ezequiel.garcia at free-electrons.com>
> ---
>  .../devicetree/bindings/bus/mvebu-mbus.txt         | 203 +++++++++++++++++++++
>  drivers/bus/mvebu-mbus.c                           | 121 +++++++++++-
>  2 files changed, 323 insertions(+), 1 deletion(-)
>  create mode 100644 Documentation/devicetree/bindings/bus/mvebu-mbus.txt
> 
> diff --git a/Documentation/devicetree/bindings/bus/mvebu-mbus.txt b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
> new file mode 100644
> index 0000000..3d31fcf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/bus/mvebu-mbus.txt
> @@ -0,0 +1,203 @@
> +
> +* Marvell MBus controller
> +
> +Required properties:
> +
> +- compatible:	 Should be set to one of the following:
> +		 marvell,armada370-mbus
> +		 marvell,armadaxp-mbus
> +
> +- reg:		 Device's register space.
> +		 Two entries are expected, see the examples below.
> +		 The first one controls the devices decoding window and
> +		 the second one controls the SDRAM decoding window.
> +
> +- address-cells: Must be '2'. The first cell for the MBus ID encoding,
> +                 the second cell for the address offset within the window.
> +
> +- size-cells:    Must be '1'.
> +
> +- ranges:        Must be set up to provide a proper translation for each child.
> +	         See the examples below.
> +
> +Example:
> +
> +	soc {
> +		compatible = "marvell,armada370-mbus", "simple-bus";
> +		reg = <0xd0020000 0x100>, <0xd0020180 0x20>;
> +		#address-cells = <2>;
> +		#size-cells = <1>;
> +	};
> +
> +** MBus address decoding window specification
> +
> +The MBus children address space is comprised of two cells: the first one for
> +the window ID and the second one for the offset within the window.
> +In order to allow to describe valid and non-valid window entries, the
> +following encoding is used:
> +
> +  0xSIAA0000 0x00oooooo
> + 
> +Where:
> +
> +  S = 0x0 for a MBus valid window
> +  S = 0xf for a non-valid window (see below)
> +
> +If S = 0x0, then:
> +
> +   I = 4-bit window target ID
> +  AA = windpw attribute 
> +
> +If S = 0xf, then:
> +
> +   I = don't care
> +   AA = 1 for internal register
> +   AA = 2 for PCI-E
> +
> +Following the above encoding, for each ranges entry for a MBus valid window
> +(S = 0x0), an address decoding window is allocated. On the other side,
> +entries for translation that do not correspond to valid windows (S = 0xf)
> +are skipped.
> +
> +	soc {
> +		compatible = "marvell,armada370-mbus", "simple-bus";
> +		reg = <0xd0020000 0x100>, <0xd0020180 0x20>;
> +		#address-cells = <2>;
> +		#size-cells = <1>;
> +
> +		ranges = <0xf0010000 0 0 0xd0000000 0x100000
> +			  0x011d0000 0 0 0xfff00000 0x100000>;
> +
> +		bootrom {
> +			compatible = "marvell,bootrom";
> +			reg = <0 0x100000>;
> +		};
> +
> +		/* other children */
> +		...
> +	};
> +
> +In the shown example, the translation entry in the 'ranges' property is what
> +makes the MBus driver create a static decoding window for the corresponding 
> +given child device. Note that the binding does not require child nodes to be
> +present. Of course, child nodes are needed to probe the devices.
> +
> +Since each window is identified by its target ID and attribute ID there's
> +a special macro that can be use to simplify the translation entries:
> +
> +#define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16))
> +
> +Using this macro, the above example would be:
> +
> +	soc {
> +		compatible = "marvell,armada370-mbus", "simple-bus";
> +		reg = <0xd0020000 0x100>, <0xd0020180 0x20>;
> +		#address-cells = <2>;
> +		#size-cells = <1>;
> +
> +		ranges = < MBUS_ID(0xf0, 0x01) 0 0 0xd0000000 0x100000
> +			   MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000>;
> +
> +		bootrom {
> +			compatible = "marvell,bootrom";
> +			reg = <MBUS_ID(0x01, 0x1d) 0 0x100000>;
> +		};
> +
> +		/* other children */
> +		...
> +	};
> +
> +
> +** About the window base address
> +
> +Remember the MBus controller allows a great deal of flexibility for choosing
> +the decoding window base address. When planning the device tree layout it's
> +possible to choose any address as the base address, provided of course there's
> +a region large enough available, and with the required alignment.
> +
> +Yet in other words: there's nothing preventing us from setting a base address
> +of 0xf0000000, or 0xd0000000 for the NOR device shown above, if such region is
> +unused.
> +
> +** Window allocation policy
> +
> +The mbus-node ranges property defines a set of mbus windows that are expected
> +to be set by the operating system and that are guaranteed to be free of overlaps
> +with one another or with the system memory ranges.
> +
> +Each entry in the property refers to exactly one window. If the operating system
> +choses to use a different set of mbus windows, it must ensure that any address
> +translations performed from downstream devices are adapted accordingly.
> +
> +The operating system may insert additional mbus windows that do not conflict
> +with the ones listed in the ranges, e.g. for mapping PCIe devices.
> +As a special case, the internal register window must be set up by the boot
> +loader at the address listed in the ranges property, since acess to that region

nit.  s/acess/access/, but only if you need to do another version for
other reasons.

thx,

Jason.

> +is needed to set up the other windows.
> +
> +** Example
> +
> +See the example below, where a more complete device tree is shown:
> +
> +	soc {
> +		compatible = "marvell,armadaxp-mbus";
> +		reg = <0 0xd0020000 0 0x100>, <0 0xd0020180 0 0x20>;
> +
> +		ranges = <MBUS_ID(0xf0, 0x01) 0 0 0xd0000000 0x100000   /* internal-regs */
> +			  MBUS_ID(0xf0, 0x02) 0xe0000000 0 0xe0000000 0x8100000  /* pcie */
> +			  MBUS_ID(0x01, 0x1d) 0 0 0xfff00000 0x100000
> +			  MBUS_ID(0x01, 0x2f) 0 0 0xf0000000 0x8000000>;
> +
> +		bootrom {
> +			compatible = "marvell,bootrom";
> +			reg = <MBUS_ID(0x01, 0x1d) 0 0x100000>;
> +		};
> +
> +		devbus-bootcs {
> +			status = "okay";
> +			ranges = <0 MBUS_ID(0x01, 0x2f) 0 0x8000000>;
> +
> +			/* NOR */
> +			nor {
> +				compatible = "cfi-flash";
> +				reg = <0 0x8000000>;
> +				bank-width = <2>;
> +			};
> +		};
> +
> +		pcie-controller {
> +			compatible = "marvell,armada-xp-pcie";
> +			status = "okay";
> +			device_type = "pci";
> +
> +			#address-cells = <3>;
> +			#size-cells = <2>;
> +
> +			ranges =
> +			       <0x82000000 0 0x40000 MBUS_ID(0xf0, 0x01) 0x40000 0 0x00002000   /* Port 0.0 registers */
> +				0x82000000 0 0x42000 MBUS_ID(0xf0, 0x01) 0x42000 0 0x00002000   /* Port 2.0 registers */
> +				0x82000000 0 0x44000 MBUS_ID(0xf0, 0x01) 0x44000 0 0x00002000   /* Port 0.1 registers */
> +				0x82000000 0 0x48000 MBUS_ID(0xf0, 0x01) 0x48000 0 0x00002000   /* Port 0.2 registers */
> +				0x82000000 0 0x4c000 MBUS_ID(0xf0, 0x01) 0x4c000 0 0x00002000   /* Port 0.3 registers */
> +				0x82000000 0 0x80000 MBUS_ID(0xf0, 0x01) 0x80000 0 0x00002000   /* Port 1.0 registers */
> +				0x82000000 0 0x82000 MBUS_ID(0xf0, 0x01) 0x82000 0 0x00002000   /* Port 3.0 registers */
> +				0x82000000 0 0xe0000000 MBUS_ID(0xf0, 0x02) 0xe0000000 0 0x08000000   /* non-prefetchable memory */
> +				0x81000000 0 0 MBUS_ID(0xf0, 0x02) 0xe8000000 0 0x00100000>; /* downstream I/O */
> +
> +			pcie at 1,0 {
> +				/* Port 0, Lane 0 */
> +				status = "okay";
> +			};
> +		};
> +
> +		internal-regs {
> +			compatible = "simple-bus";
> +			#address-cells = <1>;
> +			#size-cells = <1>;
> +			ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
> +
> +			interrupt-controller at 20000 {
> +			      reg = <0x20a00 0x2d0>, <0x21070 0x58>;
> +			};
> +		};
> +	};
> diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c
> index 23f6ae6..c8eb6c6 100644
> --- a/drivers/bus/mvebu-mbus.c
> +++ b/drivers/bus/mvebu-mbus.c
> @@ -885,6 +885,121 @@ int __init mvebu_mbus_init(const char *soc, phys_addr_t mbuswins_phys_base,
>  }
>  
>  #ifdef CONFIG_OF
> +/*
> + * The window IDs in the ranges DT property have the following format:
> + *  - bits 28 to 31: MBus custom field
> + *  - bits 24 to 27: window target ID
> + *  - bits 16 to 23: window attribute ID
> + *  - bits  0 to 15: unused
> + */
> +#define CUSTOM(id) (((id) & 0xF0000000) >> 24)
> +#define TARGET(id) (((id) & 0x0F000000) >> 24)
> +#define ATTR(id)   (((id) & 0x00FF0000) >> 16)
> +
> +static int __init mbus_dt_setup_win(struct mvebu_mbus_state *mbus,
> +				    u32 base, u32 size,
> +				    u8 target, u8 attr)
> +{
> +	const struct mvebu_mbus_mapping *map = mbus->soc->map;
> +	const char *name;
> +	int i;
> +
> +	/* Search for a suitable window in the existing mappings */
> +	for (i = 0; map[i].name; i++)
> +		if (map[i].target == target &&
> +		    map[i].attr == (attr & map[i].attrmask))
> +			break;
> +
> +	name = map[i].name;
> +	if (!name) {
> +		pr_err("window 0x%x:0x%x is unknown, skipping\n",
> +		       target, attr);
> +		return -EINVAL;
> +	}
> +
> +	if (!mvebu_mbus_window_conflicts(mbus, base, size, target, attr)) {
> +		pr_err("cannot add window '%s', conflicts with another window\n",
> +		       name);
> +		return -EBUSY;
> +	}
> +
> +	if (mvebu_mbus_alloc_window(mbus, base, size, MVEBU_MBUS_NO_REMAP,
> +				    target, attr)) {
> +		pr_err("cannot add window '%s', too many windows\n",
> +		       name);
> +		return -ENOMEM;
> +	}
> +	return 0;
> +}
> +
> +static int __init
> +mbus_parse_ranges(struct device_node *node,
> +		  int *addr_cells, int *c_addr_cells, int *c_size_cells,
> +		  int *cell_count, const __be32 **ranges_start,
> +		  const __be32 **ranges_end)
> +{
> +	const __be32 *prop;
> +	int ranges_len, tuple_len;
> +
> +	*addr_cells = of_n_addr_cells(node);
> +
> +	prop = of_get_property(node, "#address-cells", NULL);
> +	*c_addr_cells = be32_to_cpup(prop);
> +
> +	prop = of_get_property(node, "#size-cells", NULL);
> +	*c_size_cells = be32_to_cpup(prop);
> +
> +	*cell_count = *addr_cells + *c_addr_cells + *c_size_cells;
> +	tuple_len = (*cell_count) * sizeof(__be32);
> +
> +	*ranges_start = of_get_property(node, "ranges", &ranges_len);
> +	*ranges_end = *ranges_start + ranges_len / sizeof(__be32);
> +
> +	if (*ranges_start == NULL || ranges_len % tuple_len) {
> +		pr_warn("malformed ranges entry '%s'\n", node->name);
> +		return -EINVAL;
> +	}
> +	return 0;
> +}
> +
> +static int __init mbus_dt_setup(struct mvebu_mbus_state *mbus,
> +				struct device_node *np)
> +{
> +	int addr_cells, c_addr_cells, c_size_cells;
> +	int i, ret, cell_count;
> +	const __be32 *r, *ranges_start, *ranges_end;
> +
> +	ret = mbus_parse_ranges(np, &addr_cells, &c_addr_cells,
> +				&c_size_cells, &cell_count,
> +				&ranges_start, &ranges_end);
> +	if (ret < 0)
> +		return ret;
> +
> +	for (i = 0, r = ranges_start; r < ranges_end; r += cell_count, i++) {
> +		u32 windowid, base, size;
> +		u8 target, attr;
> +
> +		/*
> +		 * An entry with a non-zero custom field do not
> +		 * correspond to a static window, so skip it.
> +		 */
> +		windowid = of_read_number(r, 1);
> +		if (CUSTOM(windowid))
> +			continue;
> +
> +		target = TARGET(windowid);
> +		attr = ATTR(windowid);
> +
> +		base = of_read_number(r + c_addr_cells, addr_cells);
> +		size = of_read_number(r + c_addr_cells + addr_cells,
> +				      c_size_cells);
> +		ret = mbus_dt_setup_win(mbus, base, size, target, attr);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	return 0;
> +}
> +
>  int __init mvebu_mbus_dt_init(void)
>  {
>  	struct resource mbuswins_res, sdramwins_res;
> @@ -916,6 +1031,10 @@ int __init mvebu_mbus_dt_init(void)
>  				     resource_size(&mbuswins_res),
>  				     sdramwins_res.start,
>  				     resource_size(&sdramwins_res));
> -	return ret;
> +	if (ret)
> +		return ret;
> +
> +	/* Setup statically declared windows in the DT */
> +	return mbus_dt_setup(&mbus_state, np);
>  }
>  #endif
> -- 
> 1.8.1.5
> 



More information about the linux-arm-kernel mailing list