diff mbox

[v3,4/4] ARM: msm: Describe MSM 8660 SURF FPGA registers in DT

Message ID 1313688345-17699-5-git-send-email-davidb@codeaurora.org (mailing list archive)
State New, archived
Headers show

Commit Message

David Brown Aug. 18, 2011, 5:25 p.m. UTC
The MSM 8660 SURF development board contains a register-accessible
FPGA.  By default, this FPGA configures the first UART in output-only
mode.  On this target, reconfigure this FPGA to enable the UART to be
bidirectional.

Signed-off-by: David Brown <davidb@codeaurora.org>
---
 arch/arm/boot/dts/msm8660-surf.dts |    5 +++
 arch/arm/mach-msm/board-msm8x60.c  |   49 +++++++++++++++++++++++++++++++++---
 2 files changed, 50 insertions(+), 4 deletions(-)

Comments

Arnd Bergmann Aug. 25, 2011, 11:27 a.m. UTC | #1
On Thursday 18 August 2011, David Brown wrote:
> +static void __init msm8660_surf_fpga_init(void __iomem *fpga_mem)
> +{
> +       /* Advanced mode */
> +       writew(0xFFFF, fpga_mem + 0x15C);
> +       /* FPGA_UART_SEL */
> +       writew(0, fpga_mem + 0x172);
> +       /* FPGA_GPIO_CONFIG_117 */
> +       writew(1, fpga_mem + 0xEA);
> +       /* FPGA_GPIO_CONFIG_118 */
> +       writew(1, fpga_mem + 0xEC);
> +       dmb();
> +}

Does the dmb() do the right thing here? It seems strange to combine a strictly
ordered I/O instruction with another ordering instruction, and I think it would
be better to use writew_relaxed for the first one, followed by a 'wmb()'.

> +#ifdef CONFIG_OF
> +static void __init msm8660_surf_fpga_init_dt(void)
> +{
> +       struct device_node *node;
> +       void __iomem *fpga_mem;
> +
> +       node = of_find_compatible_node(NULL, NULL, "qcom,msm8660-surf-fpga");
> +       if (!node)
> +               return;
> +
> +       fpga_mem = of_iomap(node, 0);
> +       of_node_put(node);
> +       if (!fpga_mem) {
> +               printk(KERN_ERR "%s: Can't map fpga registers\n", __func__);
> +               return;
> +       }
> +
> +       msm8660_surf_fpga_init(fpga_mem);
> +       iounmap(fpga_mem);
> +}
> +#endif

Is the serial port connected through the FPGA or just configured by it?

In the former case, I think it would be better to make this a proper device driver that
binds to the qcom,msm8660-surf-fpga device, configures it and then creates the
platform_devices for the child nodes (the serial port, possibly others) by calling
of_platform_bus_probe.

	Arnd
David Brown Aug. 25, 2011, 2:57 p.m. UTC | #2
On Thu, Aug 25, 2011 at 01:27:12PM +0200, Arnd Bergmann wrote:
> On Thursday 18 August 2011, David Brown wrote:
> > +static void __init msm8660_surf_fpga_init(void __iomem *fpga_mem)
> > +{
> > +       /* Advanced mode */
> > +       writew(0xFFFF, fpga_mem + 0x15C);
> > +       /* FPGA_UART_SEL */
> > +       writew(0, fpga_mem + 0x172);
> > +       /* FPGA_GPIO_CONFIG_117 */
> > +       writew(1, fpga_mem + 0xEA);
> > +       /* FPGA_GPIO_CONFIG_118 */
> > +       writew(1, fpga_mem + 0xEC);
> > +       dmb();
> > +}
> 
> Does the dmb() do the right thing here? It seems strange to combine a strictly
> ordered I/O instruction with another ordering instruction, and I think it would
> be better to use writew_relaxed for the first one, followed by a 'wmb()'.

I guess I didn't really think about that, I just kind of kept the
code.  I'll ask Stepan why he did it that way, and come up with a
cleaner solution.

> > +#ifdef CONFIG_OF
> > +static void __init msm8660_surf_fpga_init_dt(void)
> > +{
> > +       struct device_node *node;
> > +       void __iomem *fpga_mem;
> > +
> > +       node = of_find_compatible_node(NULL, NULL, "qcom,msm8660-surf-fpga");
> > +       if (!node)
> > +               return;
> > +
> > +       fpga_mem = of_iomap(node, 0);
> > +       of_node_put(node);
> > +       if (!fpga_mem) {
> > +               printk(KERN_ERR "%s: Can't map fpga registers\n", __func__);
> > +               return;
> > +       }
> > +
> > +       msm8660_surf_fpga_init(fpga_mem);
> > +       iounmap(fpga_mem);
> > +}
> > +#endif
> 
> Is the serial port connected through the FPGA or just configured by it?

The FPGA controls how the UART pins are connected on the development
board.  The serial port itself is in the MSM, not the FPGA, and on
other dev boards this isn't needed for the serial port to work.

> In the former case, I think it would be better to make this a proper
> device driver that binds to the qcom,msm8660-surf-fpga device,
> configures it and then creates the platform_devices for the child
> nodes (the serial port, possibly others) by calling
> of_platform_bus_probe.

It might make sense to have the FPGA as a driver.  I believe it was
done early to make sure that the pins were configured correctly before
the serial driver came up.  As far as I can tell, the output pin is
already configured correctly, so this can actually happen completely
independently, since early usage of the UART is really only for
console messages.

I don't think it makes sense for the serial to be a child node, this
FPGA configuration is more along the lines of pinmux.  Most
configurations of this SOC don't have or need this fpga.

So, if I made it a separate driver, where would it go?  Since this
board still has platform device support, I suspect the platform data
needed to describe this device would end up being larger than the
driver itself.

David
Arnd Bergmann Aug. 25, 2011, 3:26 p.m. UTC | #3
On Thursday 25 August 2011, David Brown wrote:
> On Thu, Aug 25, 2011 at 01:27:12PM +0200, Arnd Bergmann wrote:
> > On Thursday 18 August 2011, David Brown wrote:
> > > +static void __init msm8660_surf_fpga_init(void __iomem *fpga_mem)
> > > +{
> > > +       /* Advanced mode */
> > > +       writew(0xFFFF, fpga_mem + 0x15C);
> > > +       /* FPGA_UART_SEL */
> > > +       writew(0, fpga_mem + 0x172);
> > > +       /* FPGA_GPIO_CONFIG_117 */
> > > +       writew(1, fpga_mem + 0xEA);
> > > +       /* FPGA_GPIO_CONFIG_118 */
> > > +       writew(1, fpga_mem + 0xEC);
> > > +       dmb();
> > > +}
> > 
> > Does the dmb() do the right thing here? It seems strange to combine a strictly
> > ordered I/O instruction with another ordering instruction, and I think it would
> > be better to use writew_relaxed for the first one, followed by a 'wmb()'.
> 
> I guess I didn't really think about that, I just kind of kept the
> code.  I'll ask Stepan why he did it that way, and come up with a
> cleaner solution.

Yes, no worries. I saw later that the code already exists in similar form,
so it is not urgent to change.

> > > +#ifdef CONFIG_OF
> > > +static void __init msm8660_surf_fpga_init_dt(void)
> > > +{
> > > +       struct device_node *node;
> > > +       void __iomem *fpga_mem;
> > > +
> > > +       node = of_find_compatible_node(NULL, NULL, "qcom,msm8660-surf-fpga");
> > > +       if (!node)
> > > +               return;
> > > +
> > > +       fpga_mem = of_iomap(node, 0);
> > > +       of_node_put(node);
> > > +       if (!fpga_mem) {
> > > +               printk(KERN_ERR "%s: Can't map fpga registers\n", __func__);
> > > +               return;
> > > +       }
> > > +
> > > +       msm8660_surf_fpga_init(fpga_mem);
> > > +       iounmap(fpga_mem);
> > > +}
> > > +#endif
> > 
> > Is the serial port connected through the FPGA or just configured by it?
> 
> The FPGA controls how the UART pins are connected on the development
> board.  The serial port itself is in the MSM, not the FPGA, and on
> other dev boards this isn't needed for the serial port to work.

ok.

> > In the former case, I think it would be better to make this a proper
> > device driver that binds to the qcom,msm8660-surf-fpga device,
> > configures it and then creates the platform_devices for the child
> > nodes (the serial port, possibly others) by calling
> > of_platform_bus_probe.
> 
> It might make sense to have the FPGA as a driver.  I believe it was
> done early to make sure that the pins were configured correctly before
> the serial driver came up.  As far as I can tell, the output pin is
> already configured correctly, so this can actually happen completely
> independently, since early usage of the UART is really only for
> console messages.
> 
> I don't think it makes sense for the serial to be a child node, this
> FPGA configuration is more along the lines of pinmux.  Most
> configurations of this SOC don't have or need this fpga.

Agreed.

> So, if I made it a separate driver, where would it go?  Since this
> board still has platform device support, I suspect the platform data
> needed to describe this device would end up being larger than the
> driver itself.

Excellent question ;-)

When the driver is really small, I would just leave it in the board
file for now, although that might not be a good long-term strategy.
Do we have any similar cases that we can group together with the
fpga to make a subsystem? Maybe it could be a small driver in the
pinmux subsystem when that is established.

	Arnd
David Brown Aug. 26, 2011, 5:03 a.m. UTC | #4
> > So, if I made it a separate driver, where would it go?  Since this
> > board still has platform device support, I suspect the platform data
> > needed to describe this device would end up being larger than the
> > driver itself.
> 
> Excellent question ;-)
> 
> When the driver is really small, I would just leave it in the board
> file for now, although that might not be a good long-term strategy.
> Do we have any similar cases that we can group together with the
> fpga to make a subsystem? Maybe it could be a small driver in the
> pinmux subsystem when that is established.

At this point, I think I'm just going to leave this FPGA
initialization out.  It seems that newer versions of the bootloader
have made the FPGA entirely inaccessible.  This has the unfortunate
consequence of making the UART unusable on the MSM8660 SURF.  I'll see
if I can get this fixed at a bootloader level, or come up with another
solution.

So, meanwhile, I'll include the other patches, which work fine on the
MSM 8660 Fluid target, and I'll just make an additional dsb file for
this.  Patch soon...

The FLUID has the advantage of being a device that is technically
available to people (although a bit pricey)

  http://www.bsquare.com/snapdragon-mobile-development-platform.aspx

David
diff mbox

Patch

diff --git a/arch/arm/boot/dts/msm8660-surf.dts b/arch/arm/boot/dts/msm8660-surf.dts
index 15ded0d..3591c94 100644
--- a/arch/arm/boot/dts/msm8660-surf.dts
+++ b/arch/arm/boot/dts/msm8660-surf.dts
@@ -21,4 +21,9 @@ 
 		      <0x19c00000 0x1000>;
 		interrupts = <195>;
 	};
+
+	qcom,fpga@1d000000 {
+		compatible = "qcom,msm8660-surf-fpga";
+		reg = < 0x1d000000 0x1000 >;
+	};
 };
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index 10fa8f6..f2dacfe 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -58,8 +58,52 @@  static void __init msm8x60_init_irq(void)
 	}
 }
 
+static void __init msm8660_surf_fpga_init(void __iomem *fpga_mem)
+{
+	/* Advanced mode */
+	writew(0xFFFF, fpga_mem + 0x15C);
+	/* FPGA_UART_SEL */
+	writew(0, fpga_mem + 0x172);
+	/* FPGA_GPIO_CONFIG_117 */
+	writew(1, fpga_mem + 0xEA);
+	/* FPGA_GPIO_CONFIG_118 */
+	writew(1, fpga_mem + 0xEC);
+	dmb();
+}
+
+static void __init msm8660_surf_fpga_init_platform(void)
+{
+	/* 0x1D000000 now belongs to EBI2:CS3 i.e. USB ISP Controller */
+	void __iomem *fpga_mem = ioremap(0x1D000000, SZ_4K);
+	msm8660_surf_fpga_init(fpga_mem);
+	iounmap(fpga_mem);
+}
+
+#ifdef CONFIG_OF
+static void __init msm8660_surf_fpga_init_dt(void)
+{
+	struct device_node *node;
+	void __iomem *fpga_mem;
+
+	node = of_find_compatible_node(NULL, NULL, "qcom,msm8660-surf-fpga");
+	if (!node)
+		return;
+
+	fpga_mem = of_iomap(node, 0);
+	of_node_put(node);
+	if (!fpga_mem) {
+		printk(KERN_ERR "%s: Can't map fpga registers\n", __func__);
+		return;
+	}
+
+	msm8660_surf_fpga_init(fpga_mem);
+	iounmap(fpga_mem);
+}
+#endif
+
 static void __init msm8x60_init(void)
 {
+	msm8660_surf_fpga_init_platform();
 }
 
 #ifdef CONFIG_OF
@@ -81,10 +125,7 @@  static void __init msm8x60_dt_init(void)
 	if (node)
 		irq_domain_add_simple(node, GIC_SPI_START);
 
-	if (of_machine_is_compatible("qcom,msm8660-surf")) {
-		printk(KERN_INFO "Init surf UART registers\n");
-		msm8x60_init_uart12dm();
-	}
+	msm8660_surf_fpga_init_dt();
 
 	of_platform_populate(NULL, of_default_bus_match_table,
 			msm_auxdata_lookup, NULL);