[1/4] bus: uniphier-system-bus: add UniPhier System Bus Controller driver
diff mbox

Message ID 1448357962-12259-2-git-send-email-yamada.masahiro@socionext.com
State New, archived
Headers show

Commit Message

Masahiro Yamada Nov. 24, 2015, 9:39 a.m. UTC
The UniPhier System Bus is an external bus where on-board devices are
connected to the UniPhier SoC.  This driver parses the "ranges"
property of the System Bus and set up the registers of the System Bus
Controller for the correct bus routing.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
---

 .../bindings/arm/uniphier/uniphier-system-bus.txt  |  70 +++++
 MAINTAINERS                                        |   1 +
 drivers/bus/Kconfig                                |   9 +
 drivers/bus/Makefile                               |   1 +
 drivers/bus/uniphier-system-bus.c                  | 284 +++++++++++++++++++++
 5 files changed, 365 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt
 create mode 100644 drivers/bus/uniphier-system-bus.c

Comments

Mark Rutland Nov. 24, 2015, 11:50 a.m. UTC | #1
On Tue, Nov 24, 2015 at 06:39:19PM +0900, Masahiro Yamada wrote:
> The UniPhier System Bus is an external bus where on-board devices are
> connected to the UniPhier SoC.  This driver parses the "ranges"
> property of the System Bus and set up the registers of the System Bus
> Controller for the correct bus routing.

Could you elaborate on why you need to do that?

What needs to be configured specifically?

[...]

> +The UniPhier System Bus is an external bus where on-board devices are connected
> +to the UniPhier SoC.  It it a simple parallel bus with address, data, and some
> +control signals.  It supports up to 8 banks (chip selects).
> +
> +Required properties for System Bus:
> +- compatible: should be "socionext,uniphier-system-bus", "simple-bus".

If the kernel has to perform setup of the bus, then it is not a
"simple-bus".

Configure the bus, then probe children. Don't use simple-bus jsut to get
probing.

> +- #address-cells: should be equal to or grater than 2.  The first cell is the

s/grater/greater/

> +  bank number (chip select).  The rest is the base address within the bank that
> +  should be mapped onto the parent bus (usually AMBA).
> +
> +Optional properties for System Bus:
> +- #size-cells: should be the same as that of the parent bus, if exists.  The
> +  value of the parent bus is assumed, if not specified.

This should be just as required as #address-cells, for consistency.

> +
> +
> +UniPhier System Bus Controller
> +------------------------------
> +
> +The UniPhier System Bus Controller is a hardware block with registers that
> +controls the System Bus accessing; how each bank is mapped onto the parent bus,
> +various timing parameters of the bus access, etc.
> +
> +Required properties for System Bus Controller:
> +- compatible: should be "socionext,uniphier-system-bus-controller".
> +- reg: offsets and lengths of the register sets for the device.  It should
> +  contain 2 regions: base & control register, misc register, in this order.

The example also has a system-bus phandle.

Is the "misc register" part of the bus controller, or is it a shared
system controller?

> +Example
> +-------
> +	system_bus: system-bus {
> +		compatible = "socionext,uniphier-system-bus", "simple-bus";
> +		#address-cells = <2>;
> +		#size-cells = <1>;
> +		ranges = <1 0x00000000 0x42000000 0x02000000
> +			  5 0x00000000 0x48000000 0x01000000>;
> +
> +		eth: ethernet@1,01f00000 {
> +			compatible = "smsc,lan9115";
> +			reg = <1 0x01f00000 0x1000>;
> +			phy-mode = "mii";
> +			reg-io-width = <4>;
> +		};
> +
> +		serial: uart@5,00200000 {
> +			compatible = "ns16550a";
> +			reg = <5 0x00200000 0x20>;
> +			clock-frequency = <12288000>;
> +			reg-shift = <1>;
> +		};
> +	};
> +
> +	system-bus-controller@58c00000 {
> +		compatible = "socionext,uniphier-system-bus-controller";
> +		reg = <0x58c00000 0x400>, <0x59800000 0x2000>;
> +		system-bus = <&system_bus>;
> +	};

Assuming that the controller and bus are 1-1 related, make this a single
node. e.g.

system-bus {
	compatible = "socionext,uniphier-system-bus";
	reg = <0x58c00000 0x400>, <0x59800000 0x2000>;
	#address-cells = <2>;
	#size-cells = <1>;
	ranges = <1 0x00000000 0x42000000 0x02000000>,
		 <5 0x00000000 0x48000000 0x01000000>;

	...
	child nodes here
	...

};

[...]

> +static int uniphier_sbc_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct uniphier_sbc_priv *priv;
> +	struct resource *regs;
> +	struct device_node *bus_np;
> +	int child_addrc, addrc, sizec, bank;
> +	u64 child_addr, addr, size;
> +	const __be32 *ranges;
> +	int rlen, rone, ret;
> +
> +	bus_np = of_find_compatible_node(NULL, NULL,
> +					 "socionext,uniphier-system-bus");

This is broken if you ever have multiple instances.

Either use a single node, or if there is a more complex relationship
between busses and their controllers, describe that explicitly with
phandles.

Thanks,
Mark.
Masahiro Yamada Nov. 24, 2015, 4:54 p.m. UTC | #2
Hi Mark,


2015-11-24 20:50 GMT+09:00 Mark Rutland <mark.rutland@arm.com>:
> On Tue, Nov 24, 2015 at 06:39:19PM +0900, Masahiro Yamada wrote:
>> The UniPhier System Bus is an external bus where on-board devices are
>> connected to the UniPhier SoC.  This driver parses the "ranges"
>> property of the System Bus and set up the registers of the System Bus
>> Controller for the correct bus routing.
>
> Could you elaborate on why you need to do that?

In order to have access to the System Bus (external bus),
the System Bus Controller must be set up.


> What needs to be configured specifically?

The base address and the size of each bank (each chip select)
must be set to the registers of the controller.



> [...]
>
>> +The UniPhier System Bus is an external bus where on-board devices are connected
>> +to the UniPhier SoC.  It it a simple parallel bus with address, data, and some
>> +control signals.  It supports up to 8 banks (chip selects).
>> +
>> +Required properties for System Bus:
>> +- compatible: should be "socionext,uniphier-system-bus", "simple-bus".
>
> If the kernel has to perform setup of the bus, then it is not a
> "simple-bus".
>
> Configure the bus, then probe children. Don't use simple-bus jsut to get
> probing.

OK, I will fix in v2.



>> +- #address-cells: should be equal to or grater than 2.  The first cell is the
>
> s/grater/greater/

OK, thanks.


>> +  bank number (chip select).  The rest is the base address within the bank that
>> +  should be mapped onto the parent bus (usually AMBA).
>> +
>> +Optional properties for System Bus:
>> +- #size-cells: should be the same as that of the parent bus, if exists.  The
>> +  value of the parent bus is assumed, if not specified.
>
> This should be just as required as #address-cells, for consistency.

Will fix.


>> +
>> +
>> +UniPhier System Bus Controller
>> +------------------------------
>> +
>> +The UniPhier System Bus Controller is a hardware block with registers that
>> +controls the System Bus accessing; how each bank is mapped onto the parent bus,
>> +various timing parameters of the bus access, etc.
>> +
>> +Required properties for System Bus Controller:
>> +- compatible: should be "socionext,uniphier-system-bus-controller".
>> +- reg: offsets and lengths of the register sets for the device.  It should
>> +  contain 2 regions: base & control register, misc register, in this order.
>
> The example also has a system-bus phandle.

Actually, I was wondering which is better to describe the relation between
the bus and the controller,  phandle or compatible string..



> Is the "misc register" part of the bus controller, or is it a shared
> system controller?

It is a part of the bus controller, but used for another purpose.
(i.e. partly this is syscon.  I know this is strange, but it is
what the hardware developers designed.)



>> +Example
>> +-------
>> +     system_bus: system-bus {
>> +             compatible = "socionext,uniphier-system-bus", "simple-bus";
>> +             #address-cells = <2>;
>> +             #size-cells = <1>;
>> +             ranges = <1 0x00000000 0x42000000 0x02000000
>> +                       5 0x00000000 0x48000000 0x01000000>;
>> +
>> +             eth: ethernet@1,01f00000 {
>> +                     compatible = "smsc,lan9115";
>> +                     reg = <1 0x01f00000 0x1000>;
>> +                     phy-mode = "mii";
>> +                     reg-io-width = <4>;
>> +             };
>> +
>> +             serial: uart@5,00200000 {
>> +                     compatible = "ns16550a";
>> +                     reg = <5 0x00200000 0x20>;
>> +                     clock-frequency = <12288000>;
>> +                     reg-shift = <1>;
>> +             };
>> +     };
>> +
>> +     system-bus-controller@58c00000 {
>> +             compatible = "socionext,uniphier-system-bus-controller";
>> +             reg = <0x58c00000 0x400>, <0x59800000 0x2000>;
>> +             system-bus = <&system_bus>;
>> +     };
>
> Assuming that the controller and bus are 1-1 related, make this a single
> node. e.g.
>
> system-bus {
>         compatible = "socionext,uniphier-system-bus";
>         reg = <0x58c00000 0x400>, <0x59800000 0x2000>;
>         #address-cells = <2>;
>         #size-cells = <1>;
>         ranges = <1 0x00000000 0x42000000 0x02000000>,
>                  <5 0x00000000 0x48000000 0x01000000>;
>
>         ...
>         child nodes here
>         ...
>
> };

Hmm, make sense.  But, I prefer to reflect the hardware structure.

The range of System Bus is <0x40000000 0x10000000>.

The register of the System Bus Controller is
<0x58c00000 0x400>  (and <0x59800000 0x2000>)


The bus and its controller is different.


> [...]
>
>> +static int uniphier_sbc_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct uniphier_sbc_priv *priv;
>> +     struct resource *regs;
>> +     struct device_node *bus_np;
>> +     int child_addrc, addrc, sizec, bank;
>> +     u64 child_addr, addr, size;
>> +     const __be32 *ranges;
>> +     int rlen, rone, ret;
>> +
>> +     bus_np = of_find_compatible_node(NULL, NULL,
>> +                                      "socionext,uniphier-system-bus");
>
> This is broken if you ever have multiple instances.
>
> Either use a single node, or if there is a more complex relationship
> between busses and their controllers, describe that explicitly with
> phandles.


Probably, I will stick to phandle in v2.


Thanks,
Mark Rutland Nov. 24, 2015, 5:38 p.m. UTC | #3
Hi,

> >> +UniPhier System Bus Controller
> >> +------------------------------
> >> +
> >> +The UniPhier System Bus Controller is a hardware block with registers that
> >> +controls the System Bus accessing; how each bank is mapped onto the parent bus,
> >> +various timing parameters of the bus access, etc.
> >> +
> >> +Required properties for System Bus Controller:
> >> +- compatible: should be "socionext,uniphier-system-bus-controller".
> >> +- reg: offsets and lengths of the register sets for the device.  It should
> >> +  contain 2 regions: base & control register, misc register, in this order.
> >
> > The example also has a system-bus phandle.
> 
> Actually, I was wondering which is better to describe the relation between
> the bus and the controller,  phandle or compatible string..

To describe relationships between nodes, use phandles.

Compatible strings alone cannot define relationships -- you cannot
encode how multiple instances are related.

> > Is the "misc register" part of the bus controller, or is it a shared
> > system controller?
> 
> It is a part of the bus controller, but used for another purpose.
> (i.e. partly this is syscon.  I know this is strange, but it is
> what the hardware developers designed.)

Ok. What else is going to need to use this in future?

> > Assuming that the controller and bus are 1-1 related, make this a single
> > node. e.g.
> >
> > system-bus {
> >         compatible = "socionext,uniphier-system-bus";
> >         reg = <0x58c00000 0x400>, <0x59800000 0x2000>;
> >         #address-cells = <2>;
> >         #size-cells = <1>;
> >         ranges = <1 0x00000000 0x42000000 0x02000000>,
> >                  <5 0x00000000 0x48000000 0x01000000>;
> >
> >         ...
> >         child nodes here
> >         ...
> >
> > };
> 
> Hmm, make sense.  But, I prefer to reflect the hardware structure.
> 
> The range of System Bus is <0x40000000 0x10000000>.
> 
> The register of the System Bus Controller is
> <0x58c00000 0x400>  (and <0x59800000 0x2000>)
> 
> 
> The bus and its controller is different.

So? We always describe the programming interface (i.e. the slave
interface of the device that responds to the CPU).

There's no need for separate nodes. It only makes the driver more
complicated.

> >> +static int uniphier_sbc_probe(struct platform_device *pdev)
> >> +{
> >> +     struct device *dev = &pdev->dev;
> >> +     struct uniphier_sbc_priv *priv;
> >> +     struct resource *regs;
> >> +     struct device_node *bus_np;
> >> +     int child_addrc, addrc, sizec, bank;
> >> +     u64 child_addr, addr, size;
> >> +     const __be32 *ranges;
> >> +     int rlen, rone, ret;
> >> +
> >> +     bus_np = of_find_compatible_node(NULL, NULL,
> >> +                                      "socionext,uniphier-system-bus");
> >
> > This is broken if you ever have multiple instances.
> >
> > Either use a single node, or if there is a more complex relationship
> > between busses and their controllers, describe that explicitly with
> > phandles.
> 
> 
> Probably, I will stick to phandle in v2.

I would prefer a single node unless there's some other complication
regarding the relationship of the controller and the bus itself.

Thanks,
Mark.
Masahiro Yamada Nov. 26, 2015, 2:27 a.m. UTC | #4
Hi Mark,

2015-11-25 2:38 GMT+09:00 Mark Rutland <mark.rutland@arm.com>:
> Hi,
>
>> >> +UniPhier System Bus Controller
>> >> +------------------------------
>> >> +
>> >> +The UniPhier System Bus Controller is a hardware block with registers that
>> >> +controls the System Bus accessing; how each bank is mapped onto the parent bus,
>> >> +various timing parameters of the bus access, etc.
>> >> +
>> >> +Required properties for System Bus Controller:
>> >> +- compatible: should be "socionext,uniphier-system-bus-controller".
>> >> +- reg: offsets and lengths of the register sets for the device.  It should
>> >> +  contain 2 regions: base & control register, misc register, in this order.
>> >
>> > The example also has a system-bus phandle.
>>
>> Actually, I was wondering which is better to describe the relation between
>> the bus and the controller,  phandle or compatible string..
>
> To describe relationships between nodes, use phandles.
>
> Compatible strings alone cannot define relationships -- you cannot
> encode how multiple instances are related.
>
>> > Is the "misc register" part of the bus controller, or is it a shared
>> > system controller?
>>
>> It is a part of the bus controller, but used for another purpose.
>> (i.e. partly this is syscon.  I know this is strange, but it is
>> what the hardware developers designed.)
>
> Ok. What else is going to need to use this in future?
>
>> > Assuming that the controller and bus are 1-1 related, make this a single
>> > node. e.g.
>> >
>> > system-bus {
>> >         compatible = "socionext,uniphier-system-bus";
>> >         reg = <0x58c00000 0x400>, <0x59800000 0x2000>;
>> >         #address-cells = <2>;
>> >         #size-cells = <1>;
>> >         ranges = <1 0x00000000 0x42000000 0x02000000>,
>> >                  <5 0x00000000 0x48000000 0x01000000>;
>> >
>> >         ...
>> >         child nodes here
>> >         ...
>> >
>> > };
>>
>> Hmm, make sense.  But, I prefer to reflect the hardware structure.
>>
>> The range of System Bus is <0x40000000 0x10000000>.
>>
>> The register of the System Bus Controller is
>> <0x58c00000 0x400>  (and <0x59800000 0x2000>)
>>
>>
>> The bus and its controller is different.
>
> So? We always describe the programming interface (i.e. the slave
> interface of the device that responds to the CPU).
>
> There's no need for separate nodes. It only makes the driver more
> complicated.
>
>> >> +static int uniphier_sbc_probe(struct platform_device *pdev)
>> >> +{
>> >> +     struct device *dev = &pdev->dev;
>> >> +     struct uniphier_sbc_priv *priv;
>> >> +     struct resource *regs;
>> >> +     struct device_node *bus_np;
>> >> +     int child_addrc, addrc, sizec, bank;
>> >> +     u64 child_addr, addr, size;
>> >> +     const __be32 *ranges;
>> >> +     int rlen, rone, ret;
>> >> +
>> >> +     bus_np = of_find_compatible_node(NULL, NULL,
>> >> +                                      "socionext,uniphier-system-bus");
>> >
>> > This is broken if you ever have multiple instances.
>> >
>> > Either use a single node, or if there is a more complex relationship
>> > between busses and their controllers, describe that explicitly with
>> > phandles.
>>
>>
>> Probably, I will stick to phandle in v2.
>
> I would prefer a single node unless there's some other complication
> regarding the relationship of the controller and the bus itself.


OK, i will try the single node way.

Patch
diff mbox

diff --git a/Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt b/Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt
new file mode 100644
index 0000000..a50db0a
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/uniphier/uniphier-system-bus.txt
@@ -0,0 +1,70 @@ 
+UniPhier System Bus
+-------------------
+
+The UniPhier System Bus is an external bus where on-board devices are connected
+to the UniPhier SoC.  It it a simple parallel bus with address, data, and some
+control signals.  It supports up to 8 banks (chip selects).
+
+Required properties for System Bus:
+- compatible: should be "socionext,uniphier-system-bus", "simple-bus".
+- #address-cells: should be equal to or grater than 2.  The first cell is the
+  bank number (chip select).  The rest is the base address within the bank that
+  should be mapped onto the parent bus (usually AMBA).
+
+Optional properties for System Bus:
+- #size-cells: should be the same as that of the parent bus, if exists.  The
+  value of the parent bus is assumed, if not specified.
+
+
+UniPhier System Bus Controller
+------------------------------
+
+The UniPhier System Bus Controller is a hardware block with registers that
+controls the System Bus accessing; how each bank is mapped onto the parent bus,
+various timing parameters of the bus access, etc.
+
+Required properties for System Bus Controller:
+- compatible: should be "socionext,uniphier-system-bus-controller".
+- reg: offsets and lengths of the register sets for the device.  It should
+  contain 2 regions: base & control register, misc register, in this order.
+
+
+Example
+-------
+	system_bus: system-bus {
+		compatible = "socionext,uniphier-system-bus", "simple-bus";
+		#address-cells = <2>;
+		#size-cells = <1>;
+		ranges = <1 0x00000000 0x42000000 0x02000000
+			  5 0x00000000 0x48000000 0x01000000>;
+
+		eth: ethernet@1,01f00000 {
+			compatible = "smsc,lan9115";
+			reg = <1 0x01f00000 0x1000>;
+			phy-mode = "mii";
+			reg-io-width = <4>;
+		};
+
+		serial: uart@5,00200000 {
+			compatible = "ns16550a";
+			reg = <5 0x00200000 0x20>;
+			clock-frequency = <12288000>;
+			reg-shift = <1>;
+		};
+	};
+
+	system-bus-controller@58c00000 {
+		compatible = "socionext,uniphier-system-bus-controller";
+		reg = <0x58c00000 0x400>, <0x59800000 0x2000>;
+		system-bus = <&system_bus>;
+	};
+
+In this example, the range 0x00000000-0x02000000 of bank 1 (CS1) is mapped to
+the range 0x42000000-0x44000000 of the parent bus.
+Likewise, the range 0x00000000-0x01000000 of bank 5 (CS5) is mapped to the
+range 0x48000000-0x49000000 of the parent bus.
+
+The Ethernet device is connected at the address 0x01f00000 of CS1, and visible
+at the address 0x43f00000 in the parent bus.  The UART device is connected at
+the address 0x00200000 of CS5, and visible at the address 0x48200000 in the
+parent bus.
diff --git a/MAINTAINERS b/MAINTAINERS
index 050d0e7..e849a38 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1642,6 +1642,7 @@  F:	arch/arm/boot/dts/uniphier*
 F:	arch/arm/include/asm/hardware/cache-uniphier.h
 F:	arch/arm/mach-uniphier/
 F:	arch/arm/mm/cache-uniphier.c
+F:	drivers/bus/uniphier-system-bus.c
 F:	drivers/i2c/busses/i2c-uniphier*
 F:	drivers/pinctrl/uniphier/
 F:	drivers/tty/serial/8250/8250_uniphier.c
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 116b363..5a772fc 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -131,6 +131,15 @@  config SUNXI_RSB
 	  with various RSB based devices, such as AXP223, AXP8XX PMICs,
 	  and AC100/AC200 ICs.
 
+config UNIPHIER_SYSTEM_BUS
+	tristate "UniPhier System Bus Controller"
+	depends on ARCH_UNIPHIER
+	default y
+	help
+	  Driver to set up the System Bus Controller on UniPhier SoCs.
+	  This is needed to use on-board devices connected to the System Bus
+	  (external bus) of UniPhier SoCs.
+
 config VEXPRESS_CONFIG
 	bool "Versatile Express configuration bus"
 	default y if ARCH_VEXPRESS
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index fcb9f97..ccff007 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -17,4 +17,5 @@  obj-$(CONFIG_OMAP_INTERCONNECT)	+= omap_l3_smx.o omap_l3_noc.o
 obj-$(CONFIG_OMAP_OCP2SCP)	+= omap-ocp2scp.o
 obj-$(CONFIG_SUNXI_RSB)		+= sunxi-rsb.o
 obj-$(CONFIG_SIMPLE_PM_BUS)	+= simple-pm-bus.o
+obj-$(CONFIG_UNIPHIER_SYSTEM_BUS)	+= uniphier-system-bus.o
 obj-$(CONFIG_VEXPRESS_CONFIG)	+= vexpress-config.o
diff --git a/drivers/bus/uniphier-system-bus.c b/drivers/bus/uniphier-system-bus.c
new file mode 100644
index 0000000..40cbf9e
--- /dev/null
+++ b/drivers/bus/uniphier-system-bus.c
@@ -0,0 +1,284 @@ 
+/*
+ * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/log2.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/sort.h>
+
+#define UNIPHIER_SBC_NR_BANKS	8	/* number of banks (chip select) */
+
+#define UNIPHIER_SBC_BASE	0x100	/* base address of bank0 space */
+#define    UNIPHIER_SBC_BASE_BE		BIT(0)	/* bank_enable */
+#define UNIPHIER_SBC_CTRL0	0x200	/* timing parameter 0 of bank0 */
+#define UNIPHIER_SBC_CTRL1	0x204	/* timing parameter 1 of bank0 */
+#define UNIPHIER_SBC_CTRL2	0x208	/* timing parameter 2 of bank0 */
+#define UNIPHIER_SBC_CTRL3	0x20c	/* timing parameter 3 of bank0 */
+#define UNIPHIER_SBC_CTRL4	0x300	/* timing parameter 4 of bank0 */
+
+#define UNIPHIER_SBC_STRIDE	0x10	/* register stride to next bank */
+
+struct uniphier_sbc_bank {
+	u32 base;
+	u32 end;
+};
+
+struct uniphier_sbc_priv {
+	struct device *dev;
+	void __iomem *membase;
+	struct uniphier_sbc_bank bank[UNIPHIER_SBC_NR_BANKS];
+};
+
+static void uniphier_sbc_set_reg(struct uniphier_sbc_priv *priv)
+{
+	void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE;
+	u32 base, end, mask, val;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(priv->bank); i++) {
+		base = priv->bank[i].base;
+		end = priv->bank[i].end;
+
+		if (base == end)
+			continue;
+
+		mask = base ^ (end - 1);
+
+		val = base & 0xfffe0000;
+		val |= (~mask >> 16) & 0xfffe;
+		val |= UNIPHIER_SBC_BASE_BE;
+
+		dev_dbg(priv->dev, "SBC_BASE[%d] = 0x%08x\n", i, val);
+
+		writel(val, base_reg + UNIPHIER_SBC_STRIDE * i);
+	}
+}
+
+static void uniphier_sbc_check_boot_swap(struct uniphier_sbc_priv *priv)
+{
+	void __iomem *base_reg = priv->membase + UNIPHIER_SBC_BASE;
+	int is_swapped;
+
+	is_swapped = !(readl(base_reg) & UNIPHIER_SBC_BASE_BE);
+
+	dev_dbg(priv->dev, "Boot Swap: %s\n", is_swapped ? "on" : "off");
+
+	if (is_swapped)
+		swap(priv->bank[0], priv->bank[1]);
+}
+
+static int uniphier_sbc_get_cells(struct device_node *np, int *child_addrc,
+				  int *addrc, int *sizec)
+{
+	u32 cells;
+	int ret;
+
+	*addrc = of_n_addr_cells(np);
+	*sizec = of_n_size_cells(np);
+
+	ret = of_property_read_u32(np, "#address-cells", &cells);
+	if (ret)
+		return ret;
+
+	*child_addrc = cells;
+	if (*child_addrc <= 1)
+		return -EINVAL;
+
+	ret = of_property_read_u32(np, "#size-cells", &cells);
+	if (!ret && cells != *sizec)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int uniphier_sbc_add_bank(struct uniphier_sbc_priv *priv, int bank,
+				 u64 child_addr, u64 addr, u64 size)
+{
+	u64 end, mask;
+
+	dev_dbg(priv->dev,
+		"range found: bank = %d, caddr = %08llx, addr = %08llx, size = %08llx\n",
+		bank, child_addr, addr, size);
+
+	if (bank >= ARRAY_SIZE(priv->bank)) {
+		dev_err(priv->dev, "unsupported bank number %d\n", bank);
+		return -EINVAL;
+	}
+
+	if (priv->bank[bank].base || priv->bank[bank].end) {
+		dev_err(priv->dev,
+			"range for bank %d has already been specified\n", bank);
+		return -EINVAL;
+	}
+
+	if (addr > U32_MAX) {
+		dev_err(priv->dev, "base address %llx is too high\n", addr);
+		return -EINVAL;
+	}
+
+	end = addr + size;
+
+	if (child_addr > addr) {
+		dev_err(priv->dev,
+			"base %llx cannot be mapped to %llx of parent\n",
+			child_addr, addr);
+		return -EINVAL;
+	}
+	addr -= child_addr;
+
+	addr = round_down(addr, 0x00020000);
+	end = round_up(end, 0x00020000);
+
+	if (end > U32_MAX) {
+		dev_err(priv->dev, "end address %llx is too high\n", end);
+		return -EINVAL;
+	}
+	mask = addr ^ (end - 1);
+	mask = roundup_pow_of_two(mask);
+
+	addr = round_down(addr, mask);
+	end = round_up(end, mask);
+
+	priv->bank[bank].base = addr;
+	priv->bank[bank].end = end;
+
+	dev_dbg(priv->dev, "range added: bank = %d, addr = %08x, end = %08x\n",
+		bank, priv->bank[bank].base, priv->bank[bank].end);
+
+	return 0;
+}
+
+static int uniphier_sbc_sort_cmp(const void *a, const void *b)
+{
+	return ((struct uniphier_sbc_bank *)a)->base
+		- ((struct uniphier_sbc_bank *)b)->base;
+}
+
+static int uniphier_sbc_check_overlap(struct uniphier_sbc_priv tmp)
+{
+	int i;
+
+	sort(&tmp.bank, ARRAY_SIZE(tmp.bank), sizeof(tmp.bank[0]),
+	     uniphier_sbc_sort_cmp, NULL);
+
+	for (i = 0; i < ARRAY_SIZE(tmp.bank) - 1; i++)
+		if (tmp.bank[i].end > tmp.bank[i + 1].base) {
+			dev_err(tmp.dev,
+				"region overlap between %08x-%08x and %08x-%08x\n",
+				tmp.bank[i].base, tmp.bank[i].end,
+				tmp.bank[i + 1].base, tmp.bank[i + 1].end);
+			return -EINVAL;
+		}
+
+	return 0;
+}
+
+static int uniphier_sbc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct uniphier_sbc_priv *priv;
+	struct resource *regs;
+	struct device_node *bus_np;
+	int child_addrc, addrc, sizec, bank;
+	u64 child_addr, addr, size;
+	const __be32 *ranges;
+	int rlen, rone, ret;
+
+	bus_np = of_find_compatible_node(NULL, NULL,
+					 "socionext,uniphier-system-bus");
+	if (!bus_np)
+		return 0;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->membase = devm_ioremap_resource(dev, regs);
+	if (IS_ERR(priv->membase)) {
+		ret = PTR_ERR(priv->membase);
+		goto out;
+	}
+
+	priv->dev = dev;
+
+	ret = uniphier_sbc_get_cells(bus_np, &child_addrc, &addrc, &sizec);
+	if (ret) {
+		dev_err(dev, "wrong #address-cells or #size-cells for bus\n");
+		goto out;
+	}
+
+	ranges = of_get_property(bus_np, "ranges", &rlen);
+	if (!ranges) {
+		ret = -ENOENT;
+		goto out;
+	}
+
+	rlen /= sizeof(*ranges);
+	rone = child_addrc + addrc + sizec;
+
+	for (; rlen >= rone; rlen -= rone, ranges += rone) {
+		bank = be32_to_cpup(ranges);
+		child_addr = of_read_number(ranges + 1, child_addrc - 1);
+		addr = of_translate_address(bus_np, ranges + child_addrc);
+		if (addr == OF_BAD_ADDR) {
+			ret = -EINVAL;
+			goto out;
+		}
+		size = of_read_number(ranges + child_addrc + addrc, sizec);
+
+		ret = uniphier_sbc_add_bank(priv, bank, child_addr, addr, size);
+		if (ret)
+			goto out;
+	}
+
+	ret = uniphier_sbc_check_overlap(*priv);
+	if (ret)
+		goto out;
+
+	uniphier_sbc_check_boot_swap(priv);
+
+	uniphier_sbc_set_reg(priv);
+
+out:
+	of_node_put(bus_np);
+
+	return ret;
+}
+
+
+
+static const struct of_device_id uniphier_sbc_match[] = {
+	{ .compatible = "socionext,uniphier-system-bus-controller" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, uniphier_sbc_match);
+
+static struct platform_driver uniphier_sbc_driver = {
+	.probe		= uniphier_sbc_probe,
+	.driver = {
+		.name	= "system-bus-controller",
+		.of_match_table = uniphier_sbc_match,
+	},
+};
+module_platform_driver(uniphier_sbc_driver);
+
+MODULE_AUTHOR("Masahiro Yamada <yamada.masahiro@socionext.com>");
+MODULE_DESCRIPTION("UniPhier System Bus Controller driver");
+MODULE_LICENSE("GPL");