LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* (no subject)
From: Tirumala Marri @ 2010-09-08 22:39 UTC (permalink / raw)
  To: linuxppc-dev

Is anyone working on Linus suggestion to combine the defconfigs under 44x
or 4xx ?
Thanks,
Marri

^ permalink raw reply

* Combining defconfigs for 44x based boards
From: Tirumala Marri @ 2010-09-08 22:33 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 84 bytes --]

Is anyone working on combining defconfigs for 44x or 4xx devices ?

Regards,

Marri

[-- Attachment #2: Type: text/html, Size: 1160 bytes --]

^ permalink raw reply

* Re: [PATCH 0/8] sdhci: Move real work out of an atomic context
From: Anton Vorontsov @ 2010-09-08 22:27 UTC (permalink / raw)
  To: Chris Ball
  Cc: Matt Fleming, Albert Herranz, linux-mmc, linux-kernel,
	linuxppc-dev, Ben Dooks, Andrew Morton, Pierre Ossman
In-Reply-To: <20100908220548.GA7967@void.printf.net>

On Wed, Sep 08, 2010 at 11:05:48PM +0100, Chris Ball wrote:
> Hi Anton,
> 
> On Thu, Sep 09, 2010 at 01:57:50AM +0400, Anton Vorontsov wrote:
> > Thanks!
> > 
> > Would be also great if you could point out which patch causes
> > most of the performance drop (if any)?
> > 
> > Albert, if you could find time, can you also "bisect" the
> > patchset? I wouldn't want to buy Nintendo WII just to debug the
> > perf regression. ;-) FWIW, I tried to disable multiblock
> > read/writes and test with SD cards, and still didn't notice
> > any performance drops.
> > 
> > Maybe it's SDIO IRQs that cause the performance drop for the
> > WII case, as we delay them a little bit? Or it could be the
> > patch that introduces threaded IRQ handler in whole causes
> > it. If so, I guess we'd need to move some of the processing to
> > the real IRQ context, keeping the handler lockless (if
> > possible) or introducing a very fine grained locking.
> 
> I didn't know anything about a reported performance drop, and I don't
> think Andrew did either -- Albert's test results don't seem to have
> made it to this list, or anywhere else that I can see.  Could you 
> link to/repost his comments?
> 
> (I'll be testing with libertas, so that will stress-test SDIO IRQs.)

Sure thing, here are Albert's results.

----- Forwarded message from Albert Herranz <albert_herranz@yahoo.es> -----

Date: Mon, 02 Aug 2010 21:23:51 +0200
From: Albert Herranz <albert_herranz@yahoo.es>
To: Anton Vorontsov <cbouatmailru@gmail.com>
CC: akpm@linux-foundation.org, mm-commits@vger.kernel.org,
	ben-linux@fluff.org, matt@console-pimps.org, pierre@ossman.eu,
	w.sang@pengutronix.de, mb@bu3sch.de
Subject: Re: + sdhci-use-work-structs-instead-of-tasklets.patch added to -mm
	tree

Hi,

Some initial numbers regarding performance. The patchset seems to cause a noticeable performance drop.
I've run two iperf client tests (see the two invocations of iperf -c) and two iperf server tests (see iperf -s invocation).

== 2.6.33 ==

$ iperf -c 192.168.1.130 
------------------------------------------------------------
Client connecting to 192.168.1.130, TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.1.127 port 40119 connected with 192.168.1.130 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.1 sec  1.05 MBytes    872 Kbits/sec

$ iperf -c 192.168.1.130 
------------------------------------------------------------
Client connecting to 192.168.1.130, TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.1.127 port 40120 connected with 192.168.1.130 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec  1.04 MBytes    870 Kbits/sec

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[  4] local 192.168.1.127 port 5001 connected with 192.168.1.130 port 36691
[ ID] Interval       Transfer     Bandwidth
[  4]  0.0-10.2 sec  3.61 MBytes  2.98 Mbits/sec
[  5] local 192.168.1.127 port 5001 connected with 192.168.1.130 port 36692
[  5]  0.0-10.1 sec  4.94 MBytes  4.09 Mbits/sec


== 2.6.33 + "sdhci: Move real work out of an atomic context" patchset ==

$ iperf -c 192.168.1.130 
------------------------------------------------------------
Client connecting to 192.168.1.130, TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.1.127 port 39210 connected with 192.168.1.130 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.0 sec    368 KBytes    301 Kbits/sec

$ iperf -c 192.168.1.130 
------------------------------------------------------------
Client connecting to 192.168.1.130, TCP port 5001
TCP window size: 16.0 KByte (default)
------------------------------------------------------------
[  3] local 192.168.1.127 port 39211 connected with 192.168.1.130 port 5001
[ ID] Interval       Transfer     Bandwidth
[  3]  0.0-10.2 sec    440 KBytes    354 Kbits/sec

$ iperf -s
------------------------------------------------------------
Server listening on TCP port 5001
TCP window size: 85.3 KByte (default)
------------------------------------------------------------
[  4] local 192.168.1.127 port 5001 connected with 192.168.1.130 port 57833
[ ID] Interval       Transfer     Bandwidth
[  4]  0.0-10.2 sec  2.37 MBytes  1.95 Mbits/sec
[  5] local 192.168.1.127 port 5001 connected with 192.168.1.130 port 57834
[  5]  0.0-10.2 sec  2.30 MBytes  1.90 Mbits/sec

The subjective feeling is too that the system is slower.

Cheers,
Albert

----- End forwarded message -----

^ permalink raw reply

* Re: [PATCH 0/8] sdhci: Move real work out of an atomic context
From: Chris Ball @ 2010-09-08 22:05 UTC (permalink / raw)
  To: Anton Vorontsov
  Cc: Matt Fleming, Albert Herranz, linux-mmc, linux-kernel,
	linuxppc-dev, Ben Dooks, Andrew Morton, Pierre Ossman
In-Reply-To: <20100908215750.GA17232@oksana.dev.rtsoft.ru>

Hi Anton,

On Thu, Sep 09, 2010 at 01:57:50AM +0400, Anton Vorontsov wrote:
> Thanks!
> 
> Would be also great if you could point out which patch causes
> most of the performance drop (if any)?
> 
> Albert, if you could find time, can you also "bisect" the
> patchset? I wouldn't want to buy Nintendo WII just to debug the
> perf regression. ;-) FWIW, I tried to disable multiblock
> read/writes and test with SD cards, and still didn't notice
> any performance drops.
> 
> Maybe it's SDIO IRQs that cause the performance drop for the
> WII case, as we delay them a little bit? Or it could be the
> patch that introduces threaded IRQ handler in whole causes
> it. If so, I guess we'd need to move some of the processing to
> the real IRQ context, keeping the handler lockless (if
> possible) or introducing a very fine grained locking.

I didn't know anything about a reported performance drop, and I don't
think Andrew did either -- Albert's test results don't seem to have
made it to this list, or anywhere else that I can see.  Could you 
link to/repost his comments?

(I'll be testing with libertas, so that will stress-test SDIO IRQs.)

Thanks,

-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply

* Re: [PATCH][v2] mpc8308_p1m: support for MPC8308 P1M board
From: Scott Wood @ 2010-09-08 22:02 UTC (permalink / raw)
  To: Ilya Yanok; +Cc: vlad, linuxppc-dev, wd, dzu
In-Reply-To: <1283982886-24523-1-git-send-email-yanok@emcraft.com>

On Wed, 8 Sep 2010 23:54:46 +0200
Ilya Yanok <yanok@emcraft.com> wrote:

> +		cpld@2,0 {
> +			compatible = "cpld";
> +			reg = <0x2 0x0 0x8>;
> +			interrupts = <48 0x8>;
> +			interrups-parent = <&ipic>;
> +		};

Sorry, missed this before -- "cpld" is way too generic.  The compatible
should uniquely identify this programming interface (possibly something
like "denx,mpc8308_p1m-cpld").

-Scott

^ permalink raw reply

* Re: [PATCH 0/8] sdhci: Move real work out of an atomic context
From: Chris Ball @ 2010-09-08 21:37 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Matt Fleming, Albert Herranz, Anton Vorontsov, linux-mmc,
	linux-kernel, linuxppc-dev, Ben Dooks, Pierre Ossman
In-Reply-To: <20100907153813.936db0c6.akpm@linux-foundation.org>

Hi Andrew,

On Tue, Sep 07, 2010 at 03:38:13PM -0700, Andrew Morton wrote:
> > I noticed no throughput drop neither with PIO transfers nor
> > with DMA (tested on MPC8569E CPU), while latencies should be
> > greatly improved.
> 
> This patchset isn't causing any problems yet, but may do so in the
> future and will impact the validity of any testing.  It seems to be
> kind of stuck.  Should I drop it all?

I suggest keeping it -- I'll find time to test it out here soon, and
will keep it in mind as a possible regression cause.

Thanks,

-- 
Chris Ball   <cjb@laptop.org>   <http://printf.net/>
One Laptop Per Child

^ permalink raw reply

* Re: [PATCH 0/8] sdhci: Move real work out of an atomic context
From: Anton Vorontsov @ 2010-09-08 21:57 UTC (permalink / raw)
  To: Chris Ball
  Cc: Matt Fleming, Albert Herranz, linux-mmc, linux-kernel,
	linuxppc-dev, Ben Dooks, Andrew Morton, Pierre Ossman
In-Reply-To: <20100908213740.GA7550@void.printf.net>

On Wed, Sep 08, 2010 at 10:37:41PM +0100, Chris Ball wrote:
> Hi Andrew,
> 
> On Tue, Sep 07, 2010 at 03:38:13PM -0700, Andrew Morton wrote:
> > > I noticed no throughput drop neither with PIO transfers nor
> > > with DMA (tested on MPC8569E CPU), while latencies should be
> > > greatly improved.
> > 
> > This patchset isn't causing any problems yet, but may do so in the
> > future and will impact the validity of any testing.  It seems to be
> > kind of stuck.  Should I drop it all?
> 
> I suggest keeping it -- I'll find time to test it out here soon, and
> will keep it in mind as a possible regression cause.

Thanks!

Would be also great if you could point out which patch causes
most of the performance drop (if any)?

Albert, if you could find time, can you also "bisect" the
patchset? I wouldn't want to buy Nintendo WII just to debug the
perf regression. ;-) FWIW, I tried to disable multiblock
read/writes and test with SD cards, and still didn't notice
any performance drops.

Maybe it's SDIO IRQs that cause the performance drop for the
WII case, as we delay them a little bit? Or it could be the
patch that introduces threaded IRQ handler in whole causes
it. If so, I guess we'd need to move some of the processing to
the real IRQ context, keeping the handler lockless (if
possible) or introducing a very fine grained locking.

Thanks,

-- 
Anton Vorontsov
email: cbouatmailru@gmail.com
irc://irc.freenode.net/bd2

^ permalink raw reply

* [PATCH][v2] mpc8308_p1m: support for MPC8308 P1M board
From: Ilya Yanok @ 2010-09-08 21:54 UTC (permalink / raw)
  To: linuxppc-dev, scottwood, wd, dzu, vlad; +Cc: Ilya Yanok
In-Reply-To: <20100907152834.1f038f49@schlenkerla.am.freescale.net>

This patch adds support for MPC8308 P1M board.
Supported devices:
 DUART
 Dual Ethernet
 NOR flash
 Both I2C controllers
 USB in peripheral mode
 PCI Express

Signed-off-by: Ilya Yanok <yanok@emcraft.com>
---

Scott's comments addressed.

 arch/powerpc/boot/dts/mpc8308_p1m.dts     |  332 +++++++++++++++++++++++++++++
 arch/powerpc/platforms/83xx/Kconfig       |    4 +-
 arch/powerpc/platforms/83xx/mpc830x_rdb.c |    3 +-
 3 files changed, 336 insertions(+), 3 deletions(-)
 create mode 100644 arch/powerpc/boot/dts/mpc8308_p1m.dts

diff --git a/arch/powerpc/boot/dts/mpc8308_p1m.dts b/arch/powerpc/boot/dts/mpc8308_p1m.dts
new file mode 100644
index 0000000..97cb691
--- /dev/null
+++ b/arch/powerpc/boot/dts/mpc8308_p1m.dts
@@ -0,0 +1,332 @@
+/*
+ * mpc8308_p1m Device Tree Source
+ *
+ * Copyright 2010 Ilya Yanok, Emcraft Systems, yanok@emcraft.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.
+ */
+
+/dts-v1/;
+
+/ {
+	compatible = "denx,mpc8308_p1m";
+	#address-cells = <1>;
+	#size-cells = <1>;
+
+	aliases {
+		ethernet0 = &enet0;
+		ethernet1 = &enet1;
+		serial0 = &serial0;
+		serial1 = &serial1;
+		pci0 = &pci0;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		PowerPC,8308@0 {
+			device_type = "cpu";
+			reg = <0x0>;
+			d-cache-line-size = <32>;
+			i-cache-line-size = <32>;
+			d-cache-size = <16384>;
+			i-cache-size = <16384>;
+			timebase-frequency = <0>;	// from bootloader
+			bus-frequency = <0>;		// from bootloader
+			clock-frequency = <0>;		// from bootloader
+		};
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x00000000 0x08000000>;	// 128MB at 0
+	};
+
+	localbus@e0005000 {
+		#address-cells = <2>;
+		#size-cells = <1>;
+		compatible = "fsl,mpc8315-elbc", "fsl,elbc", "simple-bus";
+		reg = <0xe0005000 0x1000>;
+		interrupts = <77 0x8>;
+		interrupt-parent = <&ipic>;
+
+		ranges = <0x0 0x0 0xfc000000 0x04000000
+		          0x1 0x0 0xfbff0000 0x00008000
+		          0x2 0x0 0xfbff8000 0x00008000>;
+
+		flash@0,0 {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "cfi-flash";
+			reg = <0x0 0x0 0x4000000>;
+			bank-width = <2>;
+			device-width = <1>;
+
+			u-boot@0 {
+				reg = <0x0 0x60000>;
+				read-only;
+			};
+			env@60000 {
+				reg = <0x60000 0x20000>;
+			};
+			env1@80000 {
+				reg = <0x80000 0x20000>;
+			};
+			kernel@a0000 {
+				reg = <0xa0000 0x200000>;
+			};
+			dtb@2a0000 {
+				reg = <0x2a0000 0x20000>;
+			};
+			ramdisk@2c0000 {
+				reg = <0x2c0000 0x640000>;
+			};
+			user@700000 {
+				reg = <0x700000 0x3900000>;
+			};
+		};
+
+		can@1,0 {
+			compatible = "nxp,sja1000";
+			reg = <0x1 0x0 0x80>;
+			interrupts = <18 0x8>;
+			interrups-parent = <&ipic>;
+		};
+
+		cpld@2,0 {
+			compatible = "cpld";
+			reg = <0x2 0x0 0x8>;
+			interrupts = <48 0x8>;
+			interrups-parent = <&ipic>;
+		};
+	};
+
+	immr@e0000000 {
+		#address-cells = <1>;
+		#size-cells = <1>;
+		device_type = "soc";
+		compatible = "fsl,mpc8308-immr", "simple-bus";
+		ranges = <0 0xe0000000 0x00100000>;
+		reg = <0xe0000000 0x00000200>;
+		bus-frequency = <0>;
+
+		i2c@3000 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "fsl-i2c";
+			reg = <0x3000 0x100>;
+			interrupts = <14 0x8>;
+			interrupt-parent = <&ipic>;
+			dfsrr;
+			fram@50 {
+				compatible = "ramtron,24c64";
+				reg = <0x50>;
+			};
+		};
+
+		i2c@3100 {
+			#address-cells = <1>;
+			#size-cells = <0>;
+			compatible = "fsl-i2c";
+			reg = <0x3100 0x100>;
+			interrupts = <15 0x8>;
+			interrupt-parent = <&ipic>;
+			dfsrr;
+			pwm@28 {
+				compatible = "maxim,ds1050";
+				reg = <0x28>;
+			};
+			sensor@48 {
+				compatible = "maxim,max6625";
+				reg = <0x48>;
+			};
+			sensor@49 {
+				compatible = "maxim,max6625";
+				reg = <0x49>;
+			};
+			sensor@4b {
+				compatible = "maxim,max6625";
+				reg = <0x4b>;
+			};
+		};
+
+		usb@23000 {
+			compatible = "fsl-usb2-dr";
+			reg = <0x23000 0x1000>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupt-parent = <&ipic>;
+			interrupts = <38 0x8>;
+			dr_mode = "peripheral";
+			phy_type = "ulpi";
+		};
+
+		enet0: ethernet@24000 {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			ranges = <0x0 0x24000 0x1000>;
+
+			cell-index = <0>;
+			device_type = "network";
+			model = "eTSEC";
+			compatible = "gianfar";
+			reg = <0x24000 0x1000>;
+			local-mac-address = [ 00 00 00 00 00 00 ];
+			interrupts = <32 0x8 33 0x8 34 0x8>;
+			interrupt-parent = <&ipic>;
+			phy-handle = < &phy1 >;
+
+			mdio@520 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "fsl,gianfar-mdio";
+				reg = <0x520 0x20>;
+				phy1: ethernet-phy@1 {
+					interrupt-parent = <&ipic>;
+					interrupts = <17 0x8>;
+					reg = <0x1>;
+					device_type = "ethernet-phy";
+				};
+				phy2: ethernet-phy@2 {
+					interrupt-parent = <&ipic>;
+					interrupts = <19 0x8>;
+					reg = <0x2>;
+					device_type = "ethernet-phy";
+				};
+				tbi0: tbi-phy@11 {
+					reg = <0x11>;
+					device_type = "tbi-phy";
+				};
+			};
+		};
+
+		enet1: ethernet@25000 {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			cell-index = <1>;
+			device_type = "network";
+			model = "eTSEC";
+			compatible = "gianfar";
+			reg = <0x25000 0x1000>;
+			ranges = <0x0 0x25000 0x1000>;
+			local-mac-address = [ 00 00 00 00 00 00 ];
+			interrupts = <35 0x8 36 0x8 37 0x8>;
+			interrupt-parent = <&ipic>;
+			phy-handle = < &phy2 >;
+
+			mdio@520 {
+				#address-cells = <1>;
+				#size-cells = <0>;
+				compatible = "fsl,gianfar-tbi";
+				reg = <0x520 0x20>;
+				tbi1: tbi-phy@11 {
+					reg = <0x11>;
+					device_type = "tbi-phy";
+				};
+			};
+		};
+
+		serial0: serial@4500 {
+			cell-index = <0>;
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <0x4500 0x100>;
+			clock-frequency = <133333333>;
+			interrupts = <9 0x8>;
+			interrupt-parent = <&ipic>;
+		};
+
+		serial1: serial@4600 {
+			cell-index = <1>;
+			device_type = "serial";
+			compatible = "ns16550";
+			reg = <0x4600 0x100>;
+			clock-frequency = <133333333>;
+			interrupts = <10 0x8>;
+			interrupt-parent = <&ipic>;
+		};
+
+		gpio@c00 {
+			#gpio-cells = <2>;
+			compatible = "fsl,mpc8308-gpio", "fsl,mpc8349-gpio";
+			reg = <0xc00 0x18>;
+			interrupts = <74 0x8>;
+			interrupt-parent = <&ipic>;
+			gpio-controller;
+		};
+
+		timer@500 {
+			compatible = "fsl,mpc8308-gtm", "fsl,gtm";
+			reg = <0x500 0x100>;
+			interrupts = <90 8 78 8 84 8 72 8>;
+			interrupt-parent = <&ipic>;
+			clock-frequency = <133333333>;
+		};
+
+		/* IPIC
+		 * interrupts cell = <intr #, sense>
+		 * sense values match linux IORESOURCE_IRQ_* defines:
+		 * sense == 8: Level, low assertion
+		 * sense == 2: Edge, high-to-low change
+		 */
+		ipic: interrupt-controller@700 {
+			compatible = "fsl,ipic";
+			interrupt-controller;
+			#address-cells = <0>;
+			#interrupt-cells = <2>;
+			reg = <0x700 0x100>;
+			device_type = "ipic";
+		};
+
+		ipic-msi@7c0 {
+			compatible = "fsl,ipic-msi";
+			reg = <0x7c0 0x40>;
+			msi-available-ranges = <0x0 0x100>;
+			interrupts = < 0x43 0x8
+					0x4  0x8
+					0x51 0x8
+					0x52 0x8
+					0x56 0x8
+					0x57 0x8
+					0x58 0x8
+					0x59 0x8 >;
+			interrupt-parent = < &ipic >;
+		};
+
+	};
+
+	pci0: pcie@e0009000 {
+		#address-cells = <3>;
+		#size-cells = <2>;
+		#interrupt-cells = <1>;
+		device_type = "pci";
+		compatible = "fsl,mpc8308-pcie", "fsl,mpc8314-pcie";
+		reg = <0xe0009000 0x00001000
+			0xb0000000 0x01000000>;
+		ranges = <0x02000000 0 0xa0000000 0xa0000000 0 0x10000000
+		          0x01000000 0 0x00000000 0xb1000000 0 0x00800000>;
+		bus-range = <0 0>;
+		interrupt-map-mask = <0 0 0 0>;
+		interrupt-map = <0 0 0 0 &ipic 1 8>;
+		interrupts = <0x1 0x8>;
+		interrupt-parent = <&ipic>;
+		clock-frequency = <0>;
+
+		pcie@0 {
+			#address-cells = <3>;
+			#size-cells = <2>;
+			device_type = "pci";
+			reg = <0 0 0 0 0>;
+			ranges = <0x02000000 0 0xa0000000
+				  0x02000000 0 0xa0000000
+				  0 0x10000000
+				  0x01000000 0 0x00000000
+				  0x01000000 0 0x00000000
+				  0 0x00800000>;
+		};
+	};
+};
diff --git a/arch/powerpc/platforms/83xx/Kconfig b/arch/powerpc/platforms/83xx/Kconfig
index 021763a..73f4135 100644
--- a/arch/powerpc/platforms/83xx/Kconfig
+++ b/arch/powerpc/platforms/83xx/Kconfig
@@ -10,12 +10,12 @@ menuconfig PPC_83xx
 if PPC_83xx
 
 config MPC830x_RDB
-	bool "Freescale MPC830x RDB"
+	bool "Freescale MPC830x RDB and derivatives"
 	select DEFAULT_UIMAGE
 	select PPC_MPC831x
 	select FSL_GTM
 	help
-	  This option enables support for the MPC8308 RDB board.
+	  This option enables support for the MPC8308 RDB and MPC8308 P1M boards.
 
 config MPC831x_RDB
 	bool "Freescale MPC831x RDB"
diff --git a/arch/powerpc/platforms/83xx/mpc830x_rdb.c b/arch/powerpc/platforms/83xx/mpc830x_rdb.c
index ac102ee..846831d 100644
--- a/arch/powerpc/platforms/83xx/mpc830x_rdb.c
+++ b/arch/powerpc/platforms/83xx/mpc830x_rdb.c
@@ -65,7 +65,8 @@ static int __init mpc830x_rdb_probe(void)
 	unsigned long root = of_get_flat_dt_root();
 
 	return of_flat_dt_is_compatible(root, "MPC8308RDB") ||
-	       of_flat_dt_is_compatible(root, "fsl,mpc8308rdb");
+	       of_flat_dt_is_compatible(root, "fsl,mpc8308rdb") ||
+	       of_flat_dt_is_compatible(root, "denx,mpc8308_p1m");
 }
 
 static struct of_device_id __initdata of_bus_ids[] = {
-- 
1.6.2.5

^ permalink raw reply related

* Re: [PATCH 4/4] arch/powerpc/platforms/chrp/nvram.c: Add of_node_put to avoid memory leak
From: Grant Likely @ 2010-09-08 20:00 UTC (permalink / raw)
  To: Julia Lawall
  Cc: devicetree-discuss, kernel-janitors, linux-kernel, Paul Mackerras,
	linuxppc-dev
In-Reply-To: <1283269738-14612-5-git-send-email-julia@diku.dk>

On Tue, Aug 31, 2010 at 05:48:58PM +0200, Julia Lawall wrote:
> Add a call to of_node_put in the error handling code following a call to
> of_find_node_by_type.
> 
> The semantic match that finds this problem is as follows:
> (http://coccinelle.lip6.fr/)
> 
> // <smpl>
> @r exists@
> local idexpression x;
> expression E,E1,E2;
> statement S;
> @@
> 
> *x = 
> (of_find_node_by_path
> |of_find_node_by_name
> |of_find_node_by_phandle
> |of_get_parent
> |of_get_next_parent
> |of_get_next_child
> |of_find_compatible_node
> |of_match_node
> |of_find_node_by_type
> |of_find_node_with_property
> |of_find_matching_node
> |of_parse_phandle
> )(...);
> ...
> if (x == NULL) S
> <... when != x = E
> *if (...) {
>   ... when != of_node_put(x)
>       when != if (...) { ... of_node_put(x); ... }
> (
>   return <+...x...+>;
> |
> *  return ...;
> )
> }
> ...>
> (
> E2 = x;
> |
> of_node_put(x);
> )
> // </smpl>
> 
> Signed-off-by: Julia Lawall <julia@diku.dk>

Acked-by: Grant Likely <grant.likely@secretlab.ca>

> 
> ---
>  arch/powerpc/platforms/chrp/nvram.c |    4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/platforms/chrp/nvram.c b/arch/powerpc/platforms/chrp/nvram.c
> index ba3588f..d3ceff0 100644
> --- a/arch/powerpc/platforms/chrp/nvram.c
> +++ b/arch/powerpc/platforms/chrp/nvram.c
> @@ -74,8 +74,10 @@ void __init chrp_nvram_init(void)
>  		return;
>  
>  	nbytes_p = of_get_property(nvram, "#bytes", &proplen);
> -	if (nbytes_p == NULL || proplen != sizeof(unsigned int))
> +	if (nbytes_p == NULL || proplen != sizeof(unsigned int)) {
> +		of_node_put(nvram);
>  		return;
> +	}
>  
>  	nvram_size = *nbytes_p;
>  
> 

^ permalink raw reply

* Re: [PATCH 1/4] drivers/serial/ucc_uart.c: Add of_node_put to avoid memory leak
From: Grant Likely @ 2010-09-08 19:59 UTC (permalink / raw)
  To: Julia Lawall
  Cc: linuxppc-dev, devicetree-discuss, kernel-janitors, Timur Tabi,
	linux-kernel
In-Reply-To: <1283269738-14612-2-git-send-email-julia@diku.dk>

On Tue, Aug 31, 2010 at 05:48:55PM +0200, Julia Lawall wrote:
> Add a call to of_node_put in the error handling code following a call to
> of_find_compatible_node or of_find_node_by_type.
> 
> This patch also substantially reorganizes the error handling code in the
> function, to that it is possible first to jump to code that frees qe_port
> and then to jump to code that also puts np.
> 
> The semantic match that finds this problem is as follows:
> (http://coccinelle.lip6.fr/)
> 
> // <smpl>
> @r exists@
> local idexpression x;
> expression E,E1,E2;
> statement S;
> @@
> 
> *x = 
> (of_find_node_by_path
> |of_find_node_by_name
> |of_find_node_by_phandle
> |of_get_parent
> |of_get_next_parent
> |of_get_next_child
> |of_find_compatible_node
> |of_match_node
> |of_find_node_by_type
> |of_find_node_with_property
> |of_find_matching_node
> |of_parse_phandle
> )(...);
> ...
> if (x == NULL) S
> <... when != x = E
> *if (...) {
>   ... when != of_node_put(x)
>       when != if (...) { ... of_node_put(x); ... }
> (
>   return <+...x...+>;
> |
> *  return ...;
> )
> }
> ...>
> (
> E2 = x;
> |
> of_node_put(x);
> )
> // </smpl>
> 
> Signed-off-by: Julia Lawall <julia@diku.dk>

Looks good to me.

Acked-by: Grant Likely <grant.likely@secretlab.ca>

> 
> ---
>  drivers/serial/ucc_uart.c |   67 ++++++++++++++++++++++++----------------------
>  1 file changed, 35 insertions(+), 32 deletions(-)
> 
> diff --git a/drivers/serial/ucc_uart.c b/drivers/serial/ucc_uart.c
> index 3f4848e..38a5ef0 100644
> --- a/drivers/serial/ucc_uart.c
> +++ b/drivers/serial/ucc_uart.c
> @@ -1270,13 +1270,12 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  	ret = of_address_to_resource(np, 0, &res);
>  	if (ret) {
>  		dev_err(&ofdev->dev, "missing 'reg' property in device tree\n");
> -		kfree(qe_port);
> -		return ret;
> +		goto out_free;
>  	}
>  	if (!res.start) {
>  		dev_err(&ofdev->dev, "invalid 'reg' property in device tree\n");
> -		kfree(qe_port);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out_free;
>  	}
>  	qe_port->port.mapbase = res.start;
>  
> @@ -1286,17 +1285,17 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  	if (!iprop) {
>  		iprop = of_get_property(np, "device-id", NULL);
>  		if (!iprop) {
> -			kfree(qe_port);
>  			dev_err(&ofdev->dev, "UCC is unspecified in "
>  				"device tree\n");
> -			return -EINVAL;
> +			ret = -EINVAL;
> +			goto out_free;
>  		}
>  	}
>  
>  	if ((*iprop < 1) || (*iprop > UCC_MAX_NUM)) {
>  		dev_err(&ofdev->dev, "no support for UCC%u\n", *iprop);
> -		kfree(qe_port);
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto out_free;
>  	}
>  	qe_port->ucc_num = *iprop - 1;
>  
> @@ -1310,16 +1309,16 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  	sprop = of_get_property(np, "rx-clock-name", NULL);
>  	if (!sprop) {
>  		dev_err(&ofdev->dev, "missing rx-clock-name in device tree\n");
> -		kfree(qe_port);
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto out_free;
>  	}
>  
>  	qe_port->us_info.rx_clock = qe_clock_source(sprop);
>  	if ((qe_port->us_info.rx_clock < QE_BRG1) ||
>  	    (qe_port->us_info.rx_clock > QE_BRG16)) {
>  		dev_err(&ofdev->dev, "rx-clock-name must be a BRG for UART\n");
> -		kfree(qe_port);
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto out_free;
>  	}
>  
>  #ifdef LOOPBACK
> @@ -1329,39 +1328,39 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  	sprop = of_get_property(np, "tx-clock-name", NULL);
>  	if (!sprop) {
>  		dev_err(&ofdev->dev, "missing tx-clock-name in device tree\n");
> -		kfree(qe_port);
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto out_free;
>  	}
>  	qe_port->us_info.tx_clock = qe_clock_source(sprop);
>  #endif
>  	if ((qe_port->us_info.tx_clock < QE_BRG1) ||
>  	    (qe_port->us_info.tx_clock > QE_BRG16)) {
>  		dev_err(&ofdev->dev, "tx-clock-name must be a BRG for UART\n");
> -		kfree(qe_port);
> -		return -ENODEV;
> +		ret = -ENODEV;
> +		goto out_free;
>  	}
>  
>  	/* Get the port number, numbered 0-3 */
>  	iprop = of_get_property(np, "port-number", NULL);
>  	if (!iprop) {
>  		dev_err(&ofdev->dev, "missing port-number in device tree\n");
> -		kfree(qe_port);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out_free;
>  	}
>  	qe_port->port.line = *iprop;
>  	if (qe_port->port.line >= UCC_MAX_UART) {
>  		dev_err(&ofdev->dev, "port-number must be 0-%u\n",
>  			UCC_MAX_UART - 1);
> -		kfree(qe_port);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out_free;
>  	}
>  
>  	qe_port->port.irq = irq_of_parse_and_map(np, 0);
>  	if (qe_port->port.irq == NO_IRQ) {
>  		dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n",
>  		       qe_port->ucc_num + 1);
> -		kfree(qe_port);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out_free;
>  	}
>  
>  	/*
> @@ -1373,8 +1372,8 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  		np = of_find_node_by_type(NULL, "qe");
>  		if (!np) {
>  			dev_err(&ofdev->dev, "could not find 'qe' node\n");
> -			kfree(qe_port);
> -			return -EINVAL;
> +			ret = -EINVAL;
> +			goto out_free;
>  		}
>  	}
>  
> @@ -1382,8 +1381,8 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  	if (!iprop) {
>  		dev_err(&ofdev->dev,
>  		       "missing brg-frequency in device tree\n");
> -		kfree(qe_port);
> -		return -EINVAL;
> +		ret = -EINVAL;
> +		goto out_np;
>  	}
>  
>  	if (*iprop)
> @@ -1398,16 +1397,16 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  		if (!iprop) {
>  			dev_err(&ofdev->dev,
>  				"missing QE bus-frequency in device tree\n");
> -			kfree(qe_port);
> -			return -EINVAL;
> +			ret = -EINVAL;
> +			goto out_np;
>  		}
>  		if (*iprop)
>  			qe_port->port.uartclk = *iprop / 2;
>  		else {
>  			dev_err(&ofdev->dev,
>  				"invalid QE bus-frequency in device tree\n");
> -			kfree(qe_port);
> -			return -EINVAL;
> +			ret = -EINVAL;
> +			goto out_np;
>  		}
>  	}
>  
> @@ -1445,8 +1444,7 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  	if (ret) {
>  		dev_err(&ofdev->dev, "could not add /dev/ttyQE%u\n",
>  		       qe_port->port.line);
> -		kfree(qe_port);
> -		return ret;
> +		goto out_np;
>  	}
>  
>  	dev_set_drvdata(&ofdev->dev, qe_port);
> @@ -1460,6 +1458,11 @@ static int ucc_uart_probe(struct platform_device *ofdev,
>  	       SERIAL_QE_MINOR + qe_port->port.line);
>  
>  	return 0;
> +out_np:
> +	of_node_put(np);
> +out_free:
> +	kfree(qe_port);
> +	return ret;
>  }
>  
>  static int ucc_uart_remove(struct platform_device *ofdev)
> 

^ permalink raw reply

* Re: [PATCH 4/7] arch/powerpc/platforms/powermac/pfunc_core.c: Add of_node_put to avoid memory leak
From: Grant Likely @ 2010-09-08 19:54 UTC (permalink / raw)
  To: Julia Lawall; +Cc: kernel-janitors, linuxppc-dev, Paul Mackerras, linux-kernel
In-Reply-To: <1283075566-27441-5-git-send-email-julia@diku.dk>

[cc'ing linuxppc-dev]

On Sun, Aug 29, 2010 at 11:52:43AM +0200, Julia Lawall wrote:
> Add a call to of_node_put in the error handling code following a call to
> of_find_node_by_phandle.
> 
> The semantic match that finds this problem is as follows:
> (http://coccinelle.lip6.fr/)
> 
> // <smpl>
> @r exists@
> local idexpression x;
> expression E,E1;
> statement S;
> @@
> 
> *x = 
> (of_find_node_by_path
> |of_find_node_by_name
> |of_find_node_by_phandle
> |of_get_parent
> |of_get_next_parent
> |of_get_next_child
> |of_find_compatible_node
> |of_match_node
> )(...);
> ...
> if (x == NULL) S
> <... when != x = E
> *if (...) {
>   ... when != of_node_put(x)
>       when != if (...) { ... of_node_put(x); ... }
> (
>   return <+...x...+>;
> |
> *  return ...;
> )
> }
> ...>
> of_node_put(x);
> // </smpl>
> 
> Signed-off-by: Julia Lawall <julia@diku.dk>

Acked-by: Grant Likely <grant.likely@secretlab.ca>

> 
> ---
>  arch/powerpc/platforms/powermac/pfunc_core.c |    9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/powermac/pfunc_core.c b/arch/powerpc/platforms/powermac/pfunc_core.c
> index cec6359..b0c3777 100644
> --- a/arch/powerpc/platforms/powermac/pfunc_core.c
> +++ b/arch/powerpc/platforms/powermac/pfunc_core.c
> @@ -837,8 +837,10 @@ struct pmf_function *__pmf_find_function(struct device_node *target,
>  		return NULL;
>   find_it:
>  	dev = pmf_find_device(actor);
> -	if (dev == NULL)
> -		return NULL;
> +	if (dev == NULL) {
> +		result = NULL;
> +		goto out;
> +	}
>  
>  	list_for_each_entry(func, &dev->functions, link) {
>  		if (name && strcmp(name, func->name))
> @@ -850,8 +852,9 @@ struct pmf_function *__pmf_find_function(struct device_node *target,
>  		result = func;
>  		break;
>  	}
> -	of_node_put(actor);
>  	pmf_put_device(dev);
> +out:
> +	of_node_put(actor);
>  	return result;
>  }
>  
> 

^ permalink raw reply

* Re: [PATCH 6/7] arch/powerpc/platforms/maple/setup.c: Add of_node_put to avoid memory leak
From: Grant Likely @ 2010-09-08 19:51 UTC (permalink / raw)
  To: Julia Lawall
  Cc: devicetree-discuss, kernel-janitors, linux-kernel, Paul Mackerras,
	linuxppc-dev
In-Reply-To: <1283075566-27441-7-git-send-email-julia@diku.dk>

On Sun, Aug 29, 2010 at 11:52:45AM +0200, Julia Lawall wrote:
> Add a call to of_node_put in the error handling code following a call to
> of_find_node_by_path.
> 
> The semantic match that finds this problem is as follows:
> (http://coccinelle.lip6.fr/)
> 
> // <smpl>
> @r exists@
> local idexpression x;
> expression E,E1;
> statement S;
> @@
> 
> *x = 
> (of_find_node_by_path
> |of_find_node_by_name
> |of_find_node_by_phandle
> |of_get_parent
> |of_get_next_parent
> |of_get_next_child
> |of_find_compatible_node
> |of_match_node
> )(...);
> ...
> if (x == NULL) S
> <... when != x = E
> *if (...) {
>   ... when != of_node_put(x)
>       when != if (...) { ... of_node_put(x); ... }
> (
>   return <+...x...+>;
> |
> *  return ...;
> )
> }
> ...>
> of_node_put(x);
> // </smpl>
> 
> Signed-off-by: Julia Lawall <julia@diku.dk>
> 
> ---
>  arch/powerpc/platforms/maple/setup.c |    1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c
> index 3fff8d9..fe34c3d 100644
> --- a/arch/powerpc/platforms/maple/setup.c
> +++ b/arch/powerpc/platforms/maple/setup.c
> @@ -358,6 +358,7 @@ static int __init maple_cpc925_edac_setup(void)
>  	model = (const unsigned char *)of_get_property(np, "model", NULL);
>  	if (!model) {
>  		printk(KERN_ERR "%s: Unabel to get model info\n", __func__);
> +		of_node_put(np);

Acked-by: Grant Likely <grant.likely@secretlab.ca>

This patch should also fix the exact same problem after a call to
of_find_node_by_type() in the same function (line 370).

>  		return -ENODEV;
>  	}
>  
> 

^ permalink raw reply

* Re: [PATCH 7/7] arch/powerpc/platforms/cell: Add of_node_put to avoid memory leak
From: Grant Likely @ 2010-09-08 19:46 UTC (permalink / raw)
  To: Julia Lawall
  Cc: cbe-oss-dev, Arnd Bergmann, devicetree-discuss, kernel-janitors,
	linux-kernel, Paul Mackerras, linuxppc-dev
In-Reply-To: <1283075566-27441-8-git-send-email-julia@diku.dk>

On Sun, Aug 29, 2010 at 11:52:46AM +0200, Julia Lawall wrote:
> Add calls to of_node_put in the error handling code following calls to
> of_find_node_by_path and of_find_node_by_phandle.
> 
> The semantic match that finds this problem is as follows:
> (http://coccinelle.lip6.fr/)
> 
> // <smpl>
> @r exists@
> local idexpression x;
> expression E,E1;
> statement S;
> @@
> 
> *x = 
> (of_find_node_by_path
> |of_find_node_by_name
> |of_find_node_by_phandle
> |of_get_parent
> |of_get_next_parent
> |of_get_next_child
> |of_find_compatible_node
> |of_match_node
> )(...);
> ...
> if (x == NULL) S
> <... when != x = E
> *if (...) {
>   ... when != of_node_put(x)
>       when != if (...) { ... of_node_put(x); ... }
> (
>   return <+...x...+>;
> |
> *  return ...;
> )
> }
> ...>
> of_node_put(x);
> // </smpl>
> 
> Signed-off-by: Julia Lawall <julia@diku.dk>

Acked-by: Grant Likely <grant.likely@secretlab.ca>

> 
> ---
>  arch/powerpc/platforms/cell/ras.c        |    4 +++-
>  arch/powerpc/platforms/cell/spider-pic.c |    4 +++-
>  2 files changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/cell/ras.c b/arch/powerpc/platforms/cell/ras.c
> index 1d3c4ef..5ec1e47 100644
> --- a/arch/powerpc/platforms/cell/ras.c
> +++ b/arch/powerpc/platforms/cell/ras.c
> @@ -173,8 +173,10 @@ static int __init cbe_ptcal_enable(void)
>  		return -ENODEV;
>  
>  	size = of_get_property(np, "ibm,cbe-ptcal-size", NULL);
> -	if (!size)
> +	if (!size) {
> +		of_node_put(np);
>  		return -ENODEV;
> +	}
>  
>  	pr_debug("%s: enabling PTCAL, size = 0x%x\n", __func__, *size);
>  	order = get_order(*size);
> diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c
> index 5876e88..3f2e557 100644
> --- a/arch/powerpc/platforms/cell/spider-pic.c
> +++ b/arch/powerpc/platforms/cell/spider-pic.c
> @@ -258,8 +258,10 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
>  		return NO_IRQ;
>  	imap += intsize + 1;
>  	tmp = of_get_property(iic, "#interrupt-cells", NULL);
> -	if (tmp == NULL)
> +	if (tmp == NULL) {
> +		of_node_put(iic);
>  		return NO_IRQ;
> +	}
>  	intsize = *tmp;
>  	/* Assume unit is last entry of interrupt specifier */
>  	unit = imap[intsize - 1];
> 

^ permalink raw reply

* Re: [PATCH v2] powerpc/5200: tighten up ac97 reset timing
From: Grant Likely @ 2010-09-08 17:55 UTC (permalink / raw)
  To: Eric Millbrandt; +Cc: linuxppc-dev
In-Reply-To: <1283534858-17951-1-git-send-email-emillbrandt@dekaresearch.com>

On Fri, Sep 03, 2010 at 01:27:38PM -0400, Eric Millbrandt wrote:
> Tighten up time timing around the gpio reset functionality.  Add a 200ns
> delay before remuxing the pins back to ac97 to comply with the ac97 spec.
> 
> Signed-off-by: Eric Millbrandt <emillbrandt@dekaresearch.com>
> ---
> 
> Scope shots availible upon request
> 
> changes since v1
> - amended with comments from Wolfram Sang
> 
>  arch/powerpc/platforms/52xx/mpc52xx_common.c |    8 ++++++--
>  1 files changed, 6 insertions(+), 2 deletions(-)

Applied, but the patch was mangled (tabs->spaces) and I had to fix it
up by hand.  You'll need to find a way to get out from under your
corporate email manging server.

g.

> 
> diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
> index 6e90531..41f3a7e 100644
> --- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
> +++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
> @@ -325,12 +325,16 @@ int mpc5200_psc_ac97_gpio_reset(int psc_number)
>         clrbits32(&simple_gpio->simple_dvo, sync | out);
>         clrbits8(&wkup_gpio->wkup_dvo, reset);
> 
> -       /* wait at lease 1 us */
> -       udelay(2);
> +       /* wait for 1 us */
> +       udelay(1);
> 
>         /* Deassert reset */
>         setbits8(&wkup_gpio->wkup_dvo, reset);
> 
> +       /* wait at least 200ns */
> +       /* 7 ~= (200ns * timebase) / ns2sec */
> +       __delay(7);
> +
>         /* Restore pin-muxing */
>         out_be32(&simple_gpio->port_config, mux);
> 
> --
> 1.6.3.1
> 
> -DISCLAIMER: an automatically appended disclaimer may follow. By posting-
> -to a public e-mail mailing list I hereby grant permission to distribute-
> -and copy this message.-
> 
> This e-mail and the information, including any attachments, it contains are intended to be a confidential communication only to the person or entity to whom it is addressed and may contain information that is privileged. If the reader of this message is not the intended recipient, you are hereby notified that any dissemination, distribution or copying of this communication is strictly prohibited. If you have received this communication in error, please immediately notify the sender and destroy the original message.
> 
> Thank you.
> 
> Please consider the environment before printing this email.

^ permalink raw reply

* Re: [PATCH] arch/powerpc/platforms/52xx/efika.c: Add of_node_put to avoid memory leak
From: Grant Likely @ 2010-09-08 17:45 UTC (permalink / raw)
  To: Julia Lawall
  Cc: devicetree-discuss, kernel-janitors, linux-kernel, Paul Mackerras,
	linuxppc-dev
In-Reply-To: <1283584397-7697-1-git-send-email-julia@diku.dk>

On Sat, Sep 04, 2010 at 09:13:17AM +0200, Julia Lawall wrote:
> This function is implemented as though the function of_get_next_child does
> not increment the reference count of its result, but actually it does.
> Thus the patch adds of_node_put in error handling code and drops a call to
> of_node_get.

applied, thanks.

g.

> 
> The semantic match that finds this problem is as follows:
> (http://coccinelle.lip6.fr/)
> 
> // <smpl>
> @r exists@
> local idexpression x;
> expression E1;
> position p1,p2;
> @@
> 
> x@p1 = of_get_next_child(...);
> ... when != x = E1
> of_node_get@p2(x)
> 
> @script:python@
> p1 << r.p1;
> p2 << r.p2;
> @@
> 
> cocci.print_main("call",p1)
> cocci.print_secs("get",p2)
> // </smpl>
> 
> Signed-off-by: Julia Lawall <julia@diku.dk>
> 
> ---
>  arch/powerpc/platforms/52xx/efika.c |    9 ++++++---
>  1 file changed, 6 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/powerpc/platforms/52xx/efika.c b/arch/powerpc/platforms/52xx/efika.c
> index 45c0cb9..18c1048 100644
> --- a/arch/powerpc/platforms/52xx/efika.c
> +++ b/arch/powerpc/platforms/52xx/efika.c
> @@ -99,7 +99,7 @@ static void __init efika_pcisetup(void)
>  	if (bus_range == NULL || len < 2 * sizeof(int)) {
>  		printk(KERN_WARNING EFIKA_PLATFORM_NAME
>  		       ": Can't get bus-range for %s\n", pcictrl->full_name);
> -		return;
> +		goto out_put;
>  	}
>  
>  	if (bus_range[1] == bus_range[0])
> @@ -111,12 +111,12 @@ static void __init efika_pcisetup(void)
>  	printk(" controlled by %s\n", pcictrl->full_name);
>  	printk("\n");
>  
> -	hose = pcibios_alloc_controller(of_node_get(pcictrl));
> +	hose = pcibios_alloc_controller(pcictrl);
>  	if (!hose) {
>  		printk(KERN_WARNING EFIKA_PLATFORM_NAME
>  		       ": Can't allocate PCI controller structure for %s\n",
>  		       pcictrl->full_name);
> -		return;
> +		goto out_put;
>  	}
>  
>  	hose->first_busno = bus_range[0];
> @@ -124,6 +124,9 @@ static void __init efika_pcisetup(void)
>  	hose->ops = &rtas_pci_ops;
>  
>  	pci_process_bridge_OF_ranges(hose, pcictrl, 0);
> +	return;
> +out_put:
> +	of_node_put(pcictrl);
>  }
>  
>  #else
> 

^ permalink raw reply

* RE: [PATCH] APM821xx: Add support for new SoC APM821xx
From: Tirumala Marri @ 2010-09-08 17:27 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev
In-Reply-To: <20100906154526.GA515@lixom.net>

> > CPU portion uses SoC name.
>
> Hm, you're right. Confusing.
>
> Still, the cpu setup functions would make more sense to have the core
> name in, not the SoC name. Especially since multiple SoC families might
> use the same core, etc.
[Marri] I agree. Probably we need another node which identifies SoC as
well.


> > Different features from other.
>
> Actually, it doesn't. Linus has had a strong pushback to the ARM
> community
> because of this mentality. arch/powerpc already has 100 defconfigs.
>
> The use of devicetrees means that only the actual devices on your
> board,
> will be configured, so it doesn't do any damage to compile in more
> drivers
> than you happen to have. Thus generating a defconfig that is a superset
> of some of your more common boards, or for example one per family of
> boards.
>
> One of the arguments for having custom defconfigs per board is that
> customers that
> base designs off of your eval board needs them. But they will make
> other changes
> to the config to add drivers for whatever additional devices they have
> anyway.
[Marri] I see. I am working on the modifications.
Thanks
-marri

^ permalink raw reply

* [PATCH 5/5] fpga: add CARMA DATA-FPGA Programmer support
From: Ira W. Snyder @ 2010-09-08 16:41 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder
In-Reply-To: <1283964082-30133-1-git-send-email-iws@ovro.caltech.edu>

This adds support for programming the data processing FPGAs on the OVRO
CARMA board. These FPGAs have a special programming sequence that
requires that we program the Freescale DMA engine, which is only
available inside the kernel.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 drivers/fpga/carma/Kconfig              |    8 +
 drivers/fpga/carma/Makefile             |    1 +
 drivers/fpga/carma/carma-fpga-program.c | 1023 +++++++++++++++++++++++++++++++
 3 files changed, 1032 insertions(+), 0 deletions(-)
 create mode 100644 drivers/fpga/carma/carma-fpga-program.c

diff --git a/drivers/fpga/carma/Kconfig b/drivers/fpga/carma/Kconfig
index 13c4662..a0e56b5 100644
--- a/drivers/fpga/carma/Kconfig
+++ b/drivers/fpga/carma/Kconfig
@@ -27,4 +27,12 @@ config CARMA_FPGA
 	  Say Y here to include support for communicating with the data
 	  processing FPGAs on the CARMA board.
 
+config CARMA_FPGA_PROGRAM
+	tristate "CARMA DATA-FPGA Programmer"
+	depends on CARMA && MEDIA_SUPPORT && HAS_DMA && FSL_DMA
+	default n
+	help
+	  Say Y here to include support for programming the data processing
+	  FPGAs on the CARMA board.
+
 endif # FPGA_DRIVERS
diff --git a/drivers/fpga/carma/Makefile b/drivers/fpga/carma/Makefile
index c175d34..4948bbb 100644
--- a/drivers/fpga/carma/Makefile
+++ b/drivers/fpga/carma/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_CARMA)			+= carma.o
 obj-$(CONFIG_CARMA_FPGA)		+= carma-fpga.o
+obj-$(CONFIG_CARMA_FPGA_PROGRAM)	+= carma-fpga-program.o
diff --git a/drivers/fpga/carma/carma-fpga-program.c b/drivers/fpga/carma/carma-fpga-program.c
new file mode 100644
index 0000000..3d6556f
--- /dev/null
+++ b/drivers/fpga/carma/carma-fpga-program.c
@@ -0,0 +1,1023 @@
+/*
+ * CARMA Board DATA-FPGA Programmer
+ *
+ * Copyright (c) 2009-2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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.
+ */
+
+#include <linux/cdev.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/dmaengine.h>
+#include <media/videobuf-dma-sg.h>
+
+/* Freescale DMA Controller DMA_SLAVE interface */
+#include <asm/fsldma.h>
+
+/* MPC8349EMDS specific get_immrbase() */
+#include <sysdev/fsl_soc.h>
+
+/* CARMA device class */
+#include "carma.h"
+
+static const char drv_name[] = "carma-fpga-program";
+
+/*
+ * Maximum firmware size
+ *
+ * 12849552 bytes for a CARMA Digitizer Board
+ * 18662880 bytes for a CARMA Correlator Board
+ */
+#define FW_SIZE_EP2S90		12849552
+#define FW_SIZE_EP2S130		18662880
+
+struct fpga_dev {
+
+	/* Character Device */
+	struct cdev cdev;
+	dev_t devno;
+
+	/* Device Registers */
+	struct device *dev;
+	void __iomem *regs;
+	void __iomem *immr;
+
+	/* Freescale DMA Device */
+	struct device *dmadev;
+	struct dma_chan *chan;
+
+	/* Interrupts */
+	int irq, status;
+	struct completion completion;
+
+	/* FPGA Bitfile */
+	struct mutex lock;
+	size_t bytes;
+
+	struct videobuf_dmabuf vb;
+
+	size_t fw_size;
+};
+
+#define to_fpga_dev(X) container_of(X, struct fpga_dev, cdev)
+
+/*
+ * FPGA Bitfile Helpers
+ */
+
+/**
+ * fpga_drop_firmware_data() - drop the bitfile image from memory
+ * @priv: the driver's private data structure
+ *
+ * LOCKING: must hold priv->lock
+ */
+static void fpga_drop_firmware_data(struct fpga_dev *priv)
+{
+	videobuf_dma_free(&priv->vb);
+	priv->bytes = 0;
+}
+
+/*
+ * LED Trigger (could be a seperate module)
+ */
+
+/*
+ * NOTE: this whole thing does have the problem that whenever the led's are
+ * NOTE: first set to use the fpga trigger, they could be in the wrong state
+ */
+
+DEFINE_LED_TRIGGER(ledtrig_fpga);
+
+static void ledtrig_fpga_programmed(bool enabled)
+{
+	if (enabled)
+		led_trigger_event(ledtrig_fpga, LED_FULL);
+	else
+		led_trigger_event(ledtrig_fpga, LED_OFF);
+}
+
+/*
+ * FPGA Register Helpers
+ */
+
+/* Register Definitions */
+#define FPGA_CONFIG_CONTROL		0x40
+#define FPGA_CONFIG_STATUS		0x44
+#define FPGA_CONFIG_FIFO_SIZE		0x48
+#define FPGA_CONFIG_FIFO_USED		0x4C
+#define FPGA_CONFIG_TOTAL_BYTE_COUNT	0x50
+#define FPGA_CONFIG_CUR_BYTE_COUNT	0x54
+
+#define FPGA_FIFO_ADDRESS		0x3000
+
+static int fpga_fifo_size(void __iomem *regs)
+{
+	return ioread32be(regs + FPGA_CONFIG_FIFO_SIZE);
+}
+
+static int fpga_config_error(void __iomem *regs)
+{
+	return ioread32be(regs + FPGA_CONFIG_STATUS) & 0xFFFE;
+}
+
+static int fpga_fifo_empty(void __iomem *regs)
+{
+	return ioread32be(regs + FPGA_CONFIG_FIFO_USED) == 0;
+}
+
+static void fpga_fifo_write(void __iomem *regs, u32 val)
+{
+	iowrite32be(val, regs + FPGA_FIFO_ADDRESS);
+}
+
+static void fpga_set_byte_count(void __iomem *regs, u32 count)
+{
+	iowrite32be(count, regs + FPGA_CONFIG_TOTAL_BYTE_COUNT);
+}
+
+static void fpga_programmer_enable(struct fpga_dev *priv, bool dma)
+{
+	if (dma)
+		iowrite32be(0x5, priv->regs + FPGA_CONFIG_CONTROL);
+	else
+		iowrite32be(0x1, priv->regs + FPGA_CONFIG_CONTROL);
+}
+
+static void fpga_programmer_disable(struct fpga_dev *priv)
+{
+	iowrite32be(0x0, priv->regs + FPGA_CONFIG_CONTROL);
+}
+
+static void fpga_dump_registers(struct fpga_dev *priv)
+{
+	/* good status: do nothing */
+	if (priv->status == 0)
+		return;
+
+	/* Dump all status registers */
+	dev_err(priv->dev, "Configuration failed, dumping status registers\n");
+	dev_err(priv->dev, "Control:    0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_CONTROL));
+	dev_err(priv->dev, "Status:     0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_STATUS));
+	dev_err(priv->dev, "FIFO Size:  0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_FIFO_SIZE));
+	dev_err(priv->dev, "FIFO Used:  0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_FIFO_USED));
+	dev_err(priv->dev, "FIFO Total: 0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_TOTAL_BYTE_COUNT));
+	dev_err(priv->dev, "FIFO Curr:  0x%.8x\n", ioread32be(priv->regs + FPGA_CONFIG_CUR_BYTE_COUNT));
+}
+
+/*
+ * FPGA Power Supply Code
+ */
+
+#define CTL_PWR_CONTROL	0x2006
+#define CTL_PWR_STATUS	0x200A
+#define CTL_PWR_FAIL	0x200B
+
+/*
+ * Determine if the FPGA power is good for all supplies
+ */
+static bool fpga_power_good(struct fpga_dev *priv)
+{
+	u8 val;
+
+	val = ioread8(priv->regs + CTL_PWR_STATUS);
+	if (val & 0x10)
+		return false;
+
+	return val == 0x0F;
+}
+
+/*
+ * Disable the FPGA power supplies
+ */
+static void fpga_disable_power_supplies(struct fpga_dev *priv)
+{
+	unsigned long start;
+	u8 val;
+
+	iowrite8(0x00, priv->regs + CTL_PWR_CONTROL);
+
+	/*
+	 * Wait 500ms for the power rails to discharge
+	 *
+	 * Without this delay, the CTL-CPLD state machine can get into a
+	 * state where it is waiting for the power-goods to assert, but they
+	 * never do. This only happens when enabling and disabling the
+	 * power sequencer very rapidly.
+	 *
+	 * The loop below will also wait for the power goods to de-assert,
+	 * but testing has shown that they are always disabled by the time
+	 * the sleep completes. However, omitting the sleep and only waiting
+	 * for the power-goods to de-assert was not sufficient to ensure
+	 * that the power sequencer would not wedge itself.
+	 */
+	msleep(500);
+
+	start = jiffies;
+	while (time_before(jiffies, start + HZ)) {
+		val = ioread8(priv->regs + CTL_PWR_STATUS);
+		if (!(val & 0x0f))
+			break;
+
+		msleep(10);
+	}
+
+	val = ioread8(priv->regs + CTL_PWR_STATUS);
+	if (val & 0x0f) {
+		dev_err(priv->dev, "power disable failed: "
+				   "power goods: status 0x%.2x\n", val);
+	}
+
+	if (val & 0x10) {
+		dev_err(priv->dev, "power disable failed: "
+				   "alarm bit set: status 0x%.2x\n", val);
+	}
+}
+
+/**
+ * fpga_enable_power_supplies() - enable the DATA-FPGA power supplies
+ * @priv: the driver's private data structure
+ *
+ * Enable the DATA-FPGA power supplies, waiting up to 1 second for
+ * them to enable successfully.
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int fpga_enable_power_supplies(struct fpga_dev *priv)
+{
+	unsigned long start = jiffies;
+
+	if (fpga_power_good(priv)) {
+		dev_dbg(priv->dev, "power was already good\n");
+		return 0;
+	}
+
+	iowrite8(0x01, priv->regs + CTL_PWR_CONTROL);
+	while (time_before(jiffies, start + HZ)) {
+		if (fpga_power_good(priv))
+			return 0;
+
+		msleep(10);
+	}
+
+	return fpga_power_good(priv) ? 0 : -EBUSY;
+}
+
+/*
+ * Determine if the FPGA power supplies are all enabled
+ */
+static bool fpga_power_enabled(struct fpga_dev *priv)
+{
+	u8 val;
+
+	val = ioread8(priv->regs + CTL_PWR_CONTROL);
+	if (val & 0x01)
+		return true;
+
+	return false;
+}
+
+/*
+ * Determine if the FPGA's are programmed and running correctly
+ */
+static bool fpga_running(struct fpga_dev *priv)
+{
+	if (!fpga_power_good(priv))
+		return false;
+
+	/* Check the config done bit */
+	return ioread32be(priv->regs + FPGA_CONFIG_STATUS) & (1 << 18);
+}
+
+/*
+ * FPGA Programming Code
+ */
+
+/**
+ * fpga_program_block() - put a block of data into the programmer's FIFO
+ * @priv: the driver's private data structure
+ * @buf: the data to program
+ * @count: the length of data to program (must be a multiple of 4 bytes)
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int fpga_program_block(struct fpga_dev *priv, void *buf, size_t count)
+{
+	u32 *data = buf;
+	int size = fpga_fifo_size(priv->regs);
+	int i, len;
+	unsigned long timeout;
+
+	/* FIXME: BUG_ON instead */
+	WARN_ON_ONCE(count % 4 != 0);
+
+	while (count > 0) {
+
+		/* Get the size of the block to write (maximum is FIFO_SIZE) */
+		len = min_t(size_t, count, size);
+		timeout = jiffies + HZ / 4;
+
+		/* Write the block */
+		for (i = 0; i < len / 4; i++)
+			fpga_fifo_write(priv->regs, data[i]);
+
+		/* Update the amounts left */
+		count -= len;
+		data += len / 4;
+
+		/* Wait for the fifo to empty */
+		while (true) {
+
+			if (fpga_fifo_empty(priv->regs)) {
+				break;
+			} else {
+				dev_dbg(priv->dev, "Fifo not empty\n");
+				cpu_relax();
+			}
+
+			if (fpga_config_error(priv->regs)) {
+				dev_err(priv->dev, "Error detected\n");
+				return -EIO;
+			}
+
+			if (time_after(jiffies, timeout)) {
+				dev_err(priv->dev, "Fifo timed out\n");
+				return -ETIMEDOUT;
+			}
+
+			msleep(10);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * fpga_program_cpu() - program the DATA-FPGA's using the CPU
+ * @priv: the driver's private data structure
+ *
+ * This is useful when the DMA programming method fails. It is possible to
+ * wedge the Freescale DMA controller such that the DMA programming method
+ * always fails. This method has always succeeded.
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static noinline int fpga_program_cpu(struct fpga_dev *priv)
+{
+	int ret;
+
+	/* Disable the programmer */
+	fpga_programmer_disable(priv);
+
+	/* Set the total byte count */
+	fpga_set_byte_count(priv->regs, priv->bytes);
+	dev_dbg(priv->dev, "total byte count %u bytes\n", priv->bytes);
+
+	/* Enable the controller for programming */
+	fpga_programmer_enable(priv, false);
+	dev_dbg(priv->dev, "enabled the controller\n");
+
+	/* Write each chunk of the FPGA bitfile to FPGA programmer */
+	ret = fpga_program_block(priv, priv->vb.vaddr, priv->bytes);
+	if (ret)
+		goto out_disable_controller;
+
+	/* Wait for the interrupt handler to notify us that programming finished */
+	ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+	if (!ret) {
+		dev_err(priv->dev, "Timed out waiting for completion\n");
+		ret = -ETIMEDOUT;
+		goto out_disable_controller;
+	}
+
+	/* Retrieve the status from the interrupt handler */
+	ret = priv->status;
+
+out_disable_controller:
+	fpga_programmer_disable(priv);
+	return ret;
+}
+
+/**
+ * fpga_program_dma() - program the DATA-FPGA's using the DMA engine
+ * @priv: the driver's private data structure
+ *
+ * Program the DATA-FPGA's using the Freescale DMA engine. This requires that
+ * the engine is programmed such that the hardware DMA request lines can
+ * control the entire DMA transaction. The system controller FPGA then
+ * completely offloads the programming from the CPU.
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static noinline int fpga_program_dma(struct fpga_dev *priv)
+{
+	struct dma_chan *chan = priv->chan;
+	struct dma_async_tx_descriptor *tx;
+	struct fsl_dma_slave *slave;
+	dma_cookie_t cookie;
+	size_t len, avail = 0;
+	int ret;
+
+	struct videobuf_dmabuf *vb = &priv->vb;
+
+	/* Disable the programmer */
+	fpga_programmer_disable(priv);
+	//chan->device->device_terminate_all(chan);
+
+	/* Allocate the DMA_SLAVE structure */
+	slave = fsl_dma_slave_alloc(GFP_KERNEL);
+	if (!slave) {
+		dev_err(priv->dev, "Unable to allocate DMA_SLAVE structure\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	/* Set the DMA controller in external start mode */
+	slave->external_start = true;
+	slave->request_count = 8 * 32;
+
+	/* Map the buffer for DMA */
+	ret = videobuf_dma_map(priv->dmadev, &priv->vb);
+	if (ret) {
+		dev_err(priv->dev, "Unable to map buffer for DMA\n");
+		goto out_free_slave;
+	}
+
+	/* Append the addresses to the DMA_SLAVE structure */
+	avail = priv->bytes;
+	while (avail > 0) {
+		len = min_t(size_t, avail, PAGE_SIZE);
+		ret = fsl_dma_slave_append(slave, 0xf0003000, len, GFP_KERNEL);
+		if (ret) {
+			dev_err(priv->dev, "Unable to append FIFO address\n");
+			goto out_dma_unmap;
+		}
+
+		avail -= len;
+	}
+
+	/* Submit the DMA slave */
+	chan->private = slave;
+	tx = chan->device->device_prep_slave_sg(chan, vb->sglist, vb->sglen,
+						DMA_TO_DEVICE, 0);
+	if (!tx) {
+		dev_err(priv->dev, "Unable to prep DMA_SLAVE transaction\n");
+		ret = -ENOMEM;
+		goto out_dma_unmap;
+	}
+
+	/*
+	 * Submit the transaction to the DMA controller
+	 *
+	 * We would leak memory if the submission failed, but that doesn't
+	 * happen in the fsldma driver, so it isn't an issue
+	 */
+	cookie = tx->tx_submit(tx);
+	if (dma_submit_error(cookie)) {
+		dev_err(priv->dev, "Unable to submit DMA_SLAVE transaction\n");
+		ret = -ENOMEM;
+		goto out_dma_unmap;
+	}
+
+	dma_async_memcpy_issue_pending(chan);
+
+	/* Set the total byte count */
+	fpga_set_byte_count(priv->regs, priv->bytes);
+	dev_dbg(priv->dev, "total byte count %u bytes\n", priv->bytes);
+
+	/* Enable the controller for DMA programming */
+	fpga_programmer_enable(priv, true);
+	dev_dbg(priv->dev, "enabled the controller\n");
+
+	/* Wait for the interrupt handler to notify us that programming finished */
+	ret = wait_for_completion_timeout(&priv->completion, 2 * HZ);
+	if (!ret) {
+		dev_err(priv->dev, "Timed out waiting for completion\n");
+		ret = -ETIMEDOUT;
+		goto out_disable_controller;
+	}
+
+	/* Retrieve the status from the interrupt handler */
+	ret = priv->status;
+
+out_disable_controller:
+	fpga_programmer_disable(priv);
+	//chan->device->device_terminate_all(chan);
+out_dma_unmap:
+	videobuf_dma_unmap(priv->dmadev, vb);
+out_free_slave:
+	fsl_dma_slave_free(slave);
+out_return:
+	return ret;
+}
+
+/*
+ * Interrupt Handling
+ */
+
+static irqreturn_t fpga_interrupt(int irq, void *dev_id)
+{
+	struct fpga_dev *priv = dev_id;
+
+	/* Save the status */
+	priv->status = fpga_config_error(priv->regs) ? -EIO : 0;
+	dev_dbg(priv->dev, "INTERRUPT status %d\n", priv->status);
+	fpga_dump_registers(priv);
+
+	/* Disabling the programmer clears the interrupt */
+	fpga_programmer_disable(priv);
+
+	/* Notify any waiters */
+	complete(&priv->completion);
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * SYSFS Helpers
+ */
+
+/**
+ * fpga_do_stop() - deconfigure (reset) the DATA-FPGA's
+ * @priv: the driver's private data structure
+ *
+ * LOCKING: must hold priv->lock
+ */
+static int fpga_do_stop(struct fpga_dev *priv)
+{
+	/* Set the led to unprogrammed */
+	ledtrig_fpga_programmed(false);
+
+	/* Pulse the config line to reset the FPGA's */
+	iowrite32be(0x3, priv->regs + FPGA_CONFIG_CONTROL);
+	iowrite32be(0x0, priv->regs + FPGA_CONFIG_CONTROL);
+
+	return 0;
+}
+
+static noinline int fpga_do_program(struct fpga_dev *priv)
+{
+	int ret;
+
+	if (priv->bytes != priv->fw_size) {
+		dev_err(priv->dev, "Incorrect bitfile size: got %zu bytes, "
+				   "should be %zu bytes\n",
+				   priv->bytes, priv->fw_size);
+		return -EINVAL;
+	}
+
+	if (!fpga_power_enabled(priv)) {
+		dev_err(priv->dev, "Power not enabled\n");
+		return -EINVAL;
+	}
+
+	if (!fpga_power_good(priv)) {
+		dev_err(priv->dev, "Power not good\n");
+		return -EINVAL;
+	}
+
+	/* Set the LED to unprogrammed */
+	ledtrig_fpga_programmed(false);
+
+	/* Try to program the FPGA's using DMA */
+	ret = fpga_program_dma(priv);
+
+	/* If DMA failed or doesn't exist, try with CPU */
+	if (ret) {
+		dev_warn(priv->dev, "Falling back to CPU programming\n");
+		ret = fpga_program_cpu(priv);
+	}
+
+	if (ret) {
+		dev_err(priv->dev, "Unable to program FPGA's\n");
+		return ret;
+	}
+
+	/* Drop the firmware bitfile from memory */
+	fpga_drop_firmware_data(priv);
+
+	dev_dbg(priv->dev, "FPGA programming successful\n");
+	ledtrig_fpga_programmed(true);
+
+	return 0;
+}
+
+/*
+ * File Operations
+ */
+
+static int fpga_open(struct inode *inode, struct file *filp)
+{
+	struct fpga_dev *priv = to_fpga_dev(inode->i_cdev);
+	unsigned int nr_pages;
+	int ret;
+
+	/* We only allow one process at a time */
+	if (mutex_lock_interruptible(&priv->lock))
+		return -ERESTARTSYS;
+
+	filp->private_data = priv;
+
+	/* Free any data left over from a previous open (if any) */
+	fpga_drop_firmware_data(priv);
+
+	/* Allocate a buffer to hold enough data for the bitfile */
+	nr_pages = DIV_ROUND_UP(priv->fw_size, PAGE_SIZE);
+	ret = videobuf_dma_init_kernel(&priv->vb, DMA_TO_DEVICE, nr_pages);
+	if (ret) {
+		dev_err(priv->dev, "unable to allocate data buffer\n");
+		mutex_unlock(&priv->lock);
+		return ret;
+	}
+
+	return nonseekable_open(inode, filp);
+}
+
+static int fpga_release(struct inode *inode, struct file *filp)
+{
+	struct fpga_dev *priv = filp->private_data;
+
+	mutex_unlock(&priv->lock);
+	return 0;
+}
+
+static ssize_t fpga_write(struct file *filp, const char __user *buf,
+			  size_t count, loff_t *f_pos)
+{
+	struct fpga_dev *priv = filp->private_data;
+
+	/* FPGA bitfiles have an exact size: disallow anything else */
+	if (priv->bytes >= priv->fw_size)
+		return -ENOSPC;
+
+	count = min_t(size_t, priv->fw_size - priv->bytes, count);
+	if (copy_from_user(priv->vb.vaddr + priv->bytes, buf, count))
+		return -EFAULT;
+
+	priv->bytes += count;
+	return count;
+}
+
+static const struct file_operations fpga_fops = {
+	.open		= fpga_open,
+	.release	= fpga_release,
+	.write		= fpga_write,
+	.llseek		= no_llseek,
+};
+
+/*
+ * Device Attributes
+ */
+
+static ssize_t pfail_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct fpga_dev *priv = dev_get_drvdata(dev);
+	u8 val;
+
+	val = ioread8(priv->regs + CTL_PWR_FAIL);
+	return snprintf(buf, PAGE_SIZE, "0x%.2x\n", val);
+}
+
+static ssize_t pgood_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct fpga_dev *priv = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", fpga_power_good(priv));
+}
+
+static ssize_t penable_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct fpga_dev *priv = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", fpga_power_enabled(priv));
+}
+
+static ssize_t penable_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct fpga_dev *priv = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+
+	if (strict_strtoul(buf, 0, &val))
+		return -EINVAL;
+
+	if (val) {
+		ret = fpga_enable_power_supplies(priv);
+		if (ret)
+			return ret;
+	} else {
+		fpga_do_stop(priv);
+		fpga_disable_power_supplies(priv);
+	}
+
+	return count;
+}
+
+static ssize_t program_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct fpga_dev *priv = dev_get_drvdata(dev);
+	return snprintf(buf, PAGE_SIZE, "%d\n", fpga_running(priv));
+}
+
+static ssize_t program_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t count)
+{
+	struct fpga_dev *priv = dev_get_drvdata(dev);
+	unsigned long val;
+	int ret;
+
+	if (strict_strtoul(buf, 0, &val))
+		return -EINVAL;
+
+	/* We can't have an image writer and be programming simultaneously */
+	if (mutex_lock_interruptible(&priv->lock))
+		return -ERESTARTSYS;
+
+	/* Program or Reset the FPGA's */
+	ret = val ? fpga_do_program(priv) : fpga_do_stop(priv);
+	if (ret)
+		goto out_unlock;
+
+	/* Success */
+	ret = count;
+
+out_unlock:
+	mutex_unlock(&priv->lock);
+	return ret;
+}
+
+static DEVICE_ATTR(power_fail, S_IRUGO, pfail_show, NULL);
+static DEVICE_ATTR(power_good, S_IRUGO, pgood_show, NULL);
+static DEVICE_ATTR(power_enable, S_IRUGO | S_IWUGO, penable_show, penable_store);
+static DEVICE_ATTR(program, S_IRUGO | S_IWUGO, program_show, program_store);
+
+static struct attribute *fpga_attributes[] = {
+	&dev_attr_power_fail.attr,
+	&dev_attr_power_good.attr,
+	&dev_attr_power_enable.attr,
+	&dev_attr_program.attr,
+	NULL,
+};
+
+static const struct attribute_group fpga_attr_group = {
+	.attrs = fpga_attributes,
+};
+
+/*
+ * OpenFirmware Device Subsystem
+ */
+
+#define SYS_REG_VERSION		0x00
+#define SYS_REG_GEOGRAPHIC	0x10
+
+static bool dma_filter(struct dma_chan *chan, void *data)
+{
+	/*
+	 * DMA Channel #0 is the only acceptable device
+	 *
+	 * This probably won't survive an unload/load cycle of the Freescale
+	 * DMAEngine driver, but that won't be a problem
+	 */
+	return chan->chan_id == 0 && chan->device->dev_id == 0;
+}
+
+static int fpga_of_remove(struct platform_device *op)
+{
+	struct fpga_dev *priv = dev_get_drvdata(&op->dev);
+
+	sysfs_remove_group(&priv->dev->kobj, &fpga_attr_group);
+
+	cdev_del(&priv->cdev);
+	free_irq(priv->irq, priv);
+
+	iounmap(priv->immr);
+
+	fpga_disable_power_supplies(priv);
+	iounmap(priv->regs);
+
+	dma_release_channel(priv->chan);
+	carma_device_destroy(priv->devno);
+	unregister_chrdev_region(priv->devno, 1);
+
+	/* Free any firmware image that has not been programmed */
+	fpga_drop_firmware_data(priv);
+
+	mutex_destroy(&priv->lock);
+	kfree(priv);
+
+	return 0;
+}
+
+static int fpga_of_probe(struct platform_device *op, const struct of_device_id *match)
+{
+	struct device_node *of_node = op->dev.of_node;
+	struct fpga_dev *priv;
+	dma_cap_mask_t mask;
+	u32 ver;
+	int ret;
+
+	/* Allocate private data */
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&op->dev, "Unable to allocate private data\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	dev_set_drvdata(&op->dev, priv);
+	mutex_init(&priv->lock);
+	init_completion(&priv->completion);
+	cdev_init(&priv->cdev, &fpga_fops);
+	priv->dmadev = &op->dev;
+	videobuf_dma_init(&priv->vb);
+
+	/* Allocate the character device */
+	ret = alloc_chrdev_region(&priv->devno, 0, 1, drv_name);
+	if (ret) {
+		dev_err(&op->dev, "Unable to allocate chardev region\n");
+		goto out_free_priv;
+	}
+
+	/* Allocate the CARMA device */
+	priv->dev = carma_device_create(&op->dev, priv->devno, drv_name);
+	if (IS_ERR(priv->dev)) {
+		dev_err(&op->dev, "Unable to create CARMA device\n");
+		ret = PTR_ERR(priv->dev);
+		goto out_unregister_chrdev_region;
+	}
+
+	dev_set_drvdata(priv->dev, priv);
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+	dma_cap_set(DMA_INTERRUPT, mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	/* Get control of DMA channel #0 */
+	priv->chan = dma_request_channel(mask, dma_filter, NULL);
+	if (!priv->chan) {
+		dev_err(&op->dev, "Unable to acquire DMA channel #0\n");
+		ret = -ENODEV;
+		goto out_carma_device_destroy;
+	}
+
+	/* Remap the registers for use */
+	priv->regs = of_iomap(of_node, 0);
+	if (!priv->regs) {
+		dev_err(&op->dev, "Unable to ioremap registers\n");
+		ret = -ENOMEM;
+		goto out_dma_release_channel;
+	}
+
+	/* Remap the IMMR for use */
+	priv->immr = ioremap(get_immrbase(), 0x100000);
+	if (!priv->immr) {
+		dev_err(&op->dev, "Unable to ioremap IMMR\n");
+		ret = -ENOMEM;
+		goto out_unmap_regs;
+	}
+
+	/*
+	 * Check that external DMA is configured
+	 *
+	 * U-Boot does this for us, but we should check it and bail out if
+	 * there is a problem. Failing to have this register setup correctly
+	 * will cause the DMA controller to transfer a single cacheline
+	 * worth of data, then wedge itself.
+	 */
+	if ((ioread32be(priv->immr + 0x114) & 0xE00) != 0xE00) {
+		dev_err(&op->dev, "External DMA control not configured\n");
+		ret = -ENODEV;
+		goto out_unmap_immr;
+	}
+
+	/*
+	 * Check the CTL-CPLD version
+	 *
+	 * This driver uses the CTL-CPLD DATA-FPGA power sequencer, and we
+	 * don't want to run on any version of the CTL-CPLD that does not use
+	 * a compatible register layout.
+	 *
+	 * v2: changed register layout, added power sequencer
+	 * v3: added glitch filter on the i2c overcurrent/overtemp outputs
+	 */
+	ver = ioread8(priv->regs + 0x2000);
+	if (ver != 0x02 && ver != 0x03) {
+		dev_err(&op->dev, "CTL-CPLD is not version 0x02 or 0x03!\n");
+		ret = -ENODEV;
+		goto out_unmap_immr;
+	}
+
+	/*
+	 * Set the exact size that the firmware image should be
+	 */
+	ver = ioread32be(priv->regs + SYS_REG_VERSION);
+	priv->fw_size = (ver & (1 << 18)) ? FW_SIZE_EP2S130 : FW_SIZE_EP2S90;
+
+	/* Find the correct IRQ number */
+	priv->irq = irq_of_parse_and_map(of_node, 0);
+	ret = request_irq(priv->irq, fpga_interrupt, IRQF_SHARED, drv_name, priv);
+	if (ret) {
+		dev_err(&op->dev, "Unable to request IRQ %d\n", priv->irq);
+		ret = -ENODEV;
+		goto out_unmap_immr;
+	}
+
+	/* Reset and stop the FPGA's, just in case */
+	fpga_do_stop(priv);
+
+	/* Register the character device */
+	ret = cdev_add(&priv->cdev, priv->devno, 1);
+	if (ret) {
+		dev_err(&op->dev, "Unable to add character device\n");
+		goto out_free_irq;
+	}
+
+	/* Create the sysfs files */
+	ret = sysfs_create_group(&priv->dev->kobj, &fpga_attr_group);
+	if (ret) {
+		dev_err(&op->dev, "Unable to create sysfs files\n");
+		goto out_cdev_del;
+	}
+
+	dev_info(priv->dev, "CARMA FPGA Programmer: %s rev%s with %s FPGAs\n",
+			(ver & (1 << 17)) ? "Correlator" : "Digitizer",
+			(ver & (1 << 16)) ? "B" : "A",
+			(ver & (1 << 18)) ? "EP2S130" : "EP2S90");
+
+	return 0;
+
+out_cdev_del:
+	cdev_del(&priv->cdev);
+out_free_irq:
+	free_irq(priv->irq, priv);
+out_unmap_immr:
+	iounmap(priv->immr);
+out_unmap_regs:
+	iounmap(priv->regs);
+out_dma_release_channel:
+	dma_release_channel(priv->chan);
+out_carma_device_destroy:
+	carma_device_destroy(priv->devno);
+out_unregister_chrdev_region:
+	unregister_chrdev_region(priv->devno, 1);
+out_free_priv:
+	mutex_destroy(&priv->lock);
+	kfree(priv);
+out_return:
+	return ret;
+}
+
+static struct of_device_id fpga_of_match[] = {
+	{ .compatible = "carma,fpga-programmer", },
+	{},
+};
+
+static struct of_platform_driver fpga_of_driver = {
+	.probe		= fpga_of_probe,
+	.remove		= fpga_of_remove,
+	.driver		= {
+		.name		= drv_name,
+		.of_match_table	= fpga_of_match,
+		.owner		= THIS_MODULE,
+	},
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init fpga_init(void)
+{
+	led_trigger_register_simple("fpga", &ledtrig_fpga);
+	return of_register_platform_driver(&fpga_of_driver);
+}
+
+static void __exit fpga_exit(void)
+{
+	of_unregister_platform_driver(&fpga_of_driver);
+	led_trigger_unregister_simple(ledtrig_fpga);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("CARMA Board DATA-FPGA Programmer");
+MODULE_LICENSE("GPL");
+
+module_init(fpga_init);
+module_exit(fpga_exit);
-- 
1.7.1

^ permalink raw reply related

* [PATCH 4/5] fpga: add CARMA DATA-FPGA Access Driver
From: Ira W. Snyder @ 2010-09-08 16:41 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder
In-Reply-To: <1283964082-30133-1-git-send-email-iws@ovro.caltech.edu>

This driver allows userspace to access the data processing FPGAs on the
OVRO CARMA board. It has two modes of operation:

1) random access

This allows users to poke any DATA-FPGA registers by using mmap to map
the address region directly into their memory map.

2) correlation dumping

When correlating, the DATA-FPGA's have special requirements for getting
the data out of their memory before the next correlation. This nominally
happens at 64Hz (every 15.625ms). If the data is not dumped before the
next correlation, data is lost.

The data dumping driver handles buffering up to 1 second worth of
correlation data from the FPGAs. This lowers the realtime scheduling
requirements for the userspace process reading the device.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 drivers/fpga/carma/Kconfig      |    9 +
 drivers/fpga/carma/Makefile     |    1 +
 drivers/fpga/carma/carma-fpga.c | 1471 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 1481 insertions(+), 0 deletions(-)
 create mode 100644 drivers/fpga/carma/carma-fpga.c

diff --git a/drivers/fpga/carma/Kconfig b/drivers/fpga/carma/Kconfig
index 448885e..13c4662 100644
--- a/drivers/fpga/carma/Kconfig
+++ b/drivers/fpga/carma/Kconfig
@@ -18,4 +18,13 @@ config CARMA
 	  Say Y here to include basic support for the CARMA System Controller
 	  FPGA. This option allows the other more advanced drivers to be built.
 
+config CARMA_FPGA
+	tristate "CARMA DATA-FPGA Access Driver"
+	depends on CARMA && MEDIA_SUPPORT && HAS_DMA && FSL_DMA
+	select VIDEOBUF_DMA_SG
+	default n
+	help
+	  Say Y here to include support for communicating with the data
+	  processing FPGAs on the CARMA board.
+
 endif # FPGA_DRIVERS
diff --git a/drivers/fpga/carma/Makefile b/drivers/fpga/carma/Makefile
index 90d0594..c175d34 100644
--- a/drivers/fpga/carma/Makefile
+++ b/drivers/fpga/carma/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_CARMA)			+= carma.o
+obj-$(CONFIG_CARMA_FPGA)		+= carma-fpga.o
diff --git a/drivers/fpga/carma/carma-fpga.c b/drivers/fpga/carma/carma-fpga.c
new file mode 100644
index 0000000..9534162
--- /dev/null
+++ b/drivers/fpga/carma/carma-fpga.c
@@ -0,0 +1,1471 @@
+/*
+ * CARMA DATA-FPGA Access Driver
+ *
+ * Copyright (c) 2009-2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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.
+ */
+
+/*
+ * FPGA Memory Dump Format
+ *
+ * FPGA #0 control registers (32 x 32-bit words)
+ * FPGA #1 control registers (32 x 32-bit words)
+ * FPGA #2 control registers (32 x 32-bit words)
+ * FPGA #3 control registers (32 x 32-bit words)
+ * SYSFPGA control registers (32 x 32-bit words)
+ * FPGA #0 correlation array (NUM_CORL0 correlation blocks)
+ * FPGA #1 correlation array (NUM_CORL1 correlation blocks)
+ * FPGA #2 correlation array (NUM_CORL2 correlation blocks)
+ * FPGA #3 correlation array (NUM_CORL3 correlation blocks)
+ *
+ * Each correlation array consists of:
+ *
+ * Correlation Data      (2 x NUM_LAGSn x 32-bit words)
+ * Pipeline Metadata     (2 x NUM_METAn x 32-bit words)
+ * Quantization Counters (2 x NUM_QCNTn x 32-bit words)
+ *
+ * The NUM_CORLn, NUM_LAGSn, NUM_METAn, and NUM_QCNTn values come from
+ * the FPGA configuration registers. They do not change once the FPGA's
+ * have been programmed, they only change on re-programming.
+ */
+
+/*
+ * Basic Description:
+ *
+ * This driver is used to capture correlation spectra off of the four data
+ * processing FPGAs. The FPGAs are often reprogrammed at runtime, therefore
+ * this driver supports dynamic enable/disable of capture while the device
+ * remains open.
+ *
+ * The nominal capture rate is 64Hz (every 15.625ms). To facilitate this fast
+ * capture rate, all buffers are pre-allocated to avoid any potentially long
+ * running memory allocations while capturing.
+ *
+ * There are three lists which are used to keep track of the different states
+ * of data buffers.
+ *
+ * 1) free list
+ * This list holds all empty data buffers which are ready to receive data.
+ *
+ * 2) inflight list
+ * This list holds data buffers which are currently waiting for a DMA operation
+ * to complete.
+ *
+ * 3) used list
+ * This list holds data buffers which have been filled, and are waiting to be
+ * read by userspace.
+ *
+ * All buffers start life on the free list, then move successively to the
+ * inflight list, and then to the used list. After they have been read by
+ * userspace, they are moved back to the free list. The cycle repeats as long
+ * as necessary.
+ */
+
+/*
+ * Notes on the IRQ masking scheme:
+ *
+ * The IRQ masking scheme here is different than most other hardware. The only
+ * way for the DATA-FPGAs to detect if the kernel has taken too long to copy
+ * the data is if the status registers are not cleared before the next
+ * correlation data dump is ready.
+ *
+ * The interrupt line is connected to the status registers, such that when they
+ * are cleared, the interrupt is de-asserted. Therein lies our problem. We need
+ * to schedule a long-running DMA operation and return from the interrupt
+ * handler quickly, but we cannot clear the status registers.
+ *
+ * To handle this, the system controller FPGA has the capability to connect the
+ * interrupt line to a user-controlled GPIO pin. This pin is driven high
+ * (unasserted) and left that way. To mask the interrupt, we change the
+ * interrupt source to the GPIO pin. Tada, we hid the interrupt. :)
+ */
+
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/dmaengine.h>
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <media/videobuf-dma-sg.h>
+#include <asm/fsldma.h>
+
+#include "carma.h"
+
+/* system controller registers */
+#define SYS_IRQ_SOURCE_CTL	0x24
+#define SYS_IRQ_OUTPUT_EN	0x28
+#define SYS_IRQ_OUTPUT_DATA	0x2C
+#define SYS_IRQ_INPUT_DATA	0x30
+
+/* GPIO IRQ line assignment */
+#define IRQ_CORL_DONE		0x10
+
+/* FPGA registers */
+#define MMAP_REG_CORL_CONF1	0x08
+#define MMAP_REG_CORL_CONF2	0x0C
+#define MMAP_REG_STATUS		0x48
+
+#define SYS_FPGA_BLOCK		0xF0000000
+
+static const char drv_name[] = "carma-fpga";
+
+#define NUM_FPGA	4
+
+#define MIN_DATA_BUFS	8
+#define MAX_DATA_BUFS	64
+
+struct fpga_info {
+	unsigned int num_corl;
+	unsigned int blk_size;
+};
+
+struct data_buf {
+	struct list_head entry;
+	struct videobuf_dmabuf vb;
+	bool mapped;
+	size_t size;
+};
+
+struct fpga_device {
+	struct cdev cdev;
+	dev_t devno;
+
+	struct device *dev;
+	struct mutex mutex;
+
+	/* FPGA registers and information */
+	struct fpga_info info[NUM_FPGA];
+	void __iomem *regs;
+	int irq;
+
+	/* FPGA Physical Address/Size Information */
+	resource_size_t phys_addr;
+	size_t phys_size;
+
+	/* DMA structures */
+	struct fsl_dma_slave *slave;
+	struct dma_chan *chan;
+	struct device *dmadev;
+
+	/* Protection for all members below */
+	spinlock_t lock;
+
+	/* Device enable/disable flag */
+	bool enabled;
+
+	/* Correlation data buffers */
+	wait_queue_head_t wait;
+	struct list_head free;
+	struct list_head used;
+	struct list_head inflight;
+
+	/* Information about data buffers */
+	unsigned int num_dropped;
+	unsigned int num_buffers;
+	size_t bufsize;
+};
+
+struct fpga_reader {
+	struct fpga_device *priv;
+	struct data_buf *buf;
+	off_t buf_start;
+};
+
+#define inode_to_dev(inode) container_of(inode->i_cdev, struct fpga_device, cdev)
+
+/*
+ * Data Buffer Allocation Helpers
+ */
+
+static int data_map_buffer(struct device *dev, struct data_buf *buf)
+{
+	int ret;
+
+	/* if the buffer is already mapped, we're done */
+	if (buf->mapped)
+		return 0;
+
+	ret = videobuf_dma_map(dev, &buf->vb);
+	if (ret)
+		return ret;
+
+	buf->mapped = true;
+	return 0;
+}
+
+static void data_unmap_buffer(struct device *dev, struct data_buf *buf)
+{
+	/* the buffer is already unmapped, we're done */
+	if (!buf->mapped)
+		return;
+
+	videobuf_dma_unmap(dev, &buf->vb);
+	buf->mapped = false;
+}
+
+/**
+ * data_free_buffer() - free a single data buffer and all allocated memory
+ * @dev: the DMA device to map for
+ * @buf: the buffer to free
+ *
+ * This will free all of the pages allocated to the given data buffer, and
+ * then free the structure itself
+ */
+static void data_free_buffer(struct device *dev, struct data_buf *buf)
+{
+	/* It is ok to free a NULL buffer */
+	if (!buf)
+		return;
+
+	/* Make sure the buffer is not on any list */
+	list_del_init(&buf->entry);
+
+	/* unmap it for DMA */
+	data_unmap_buffer(dev, buf);
+
+	/* free all memory */
+	videobuf_dma_free(&buf->vb);
+	kfree(buf);
+}
+
+/**
+ * data_alloc_buffer() - allocate and fill a data buffer with pages
+ * @dev: the DMA device to map for
+ * @bytes: the number of bytes required
+ *
+ * This allocates all space needed for a data buffer, and gets it ready to be
+ * used in a DMA transaction. It only needs to be used, never mapped before
+ * use. This avoids calling vmalloc in hardirq context.
+ *
+ * Returns NULL on failure
+ */
+static struct data_buf *data_alloc_buffer(struct device *dev, const size_t bytes)
+{
+	unsigned int nr_pages;
+	struct data_buf *buf;
+	int ret;
+
+	/* calculate the number of pages necessary */
+	nr_pages = DIV_ROUND_UP(bytes, PAGE_SIZE);
+
+	/* allocate the buffer structure */
+	buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		goto out_return;
+
+	/* initialize internal fields */
+	INIT_LIST_HEAD(&buf->entry);
+	buf->size = bytes;
+
+	/* allocate the videobuf */
+	videobuf_dma_init(&buf->vb);
+	ret = videobuf_dma_init_kernel(&buf->vb, DMA_FROM_DEVICE, nr_pages);
+	if (ret)
+		goto out_free_buf;
+
+	/* map it for DMA */
+	ret = data_map_buffer(dev, buf);
+	if (ret)
+		goto out_free_videobuf;
+
+	return buf;
+
+out_free_videobuf:
+	videobuf_dma_free(&buf->vb);
+out_free_buf:
+	kfree(buf);
+out_return:
+	return NULL;
+}
+
+/**
+ * data_free_buffers() - free all allocated buffers
+ * @priv: the driver's private data structure
+ *
+ * Free all buffers allocated by the driver (except those currently in the
+ * process of being read by userspace).
+ *
+ * LOCKING: must hold dev->mutex
+ * CONTEXT: user
+ */
+static void data_free_buffers(struct fpga_device *priv)
+{
+	struct device *dev = priv->dmadev;
+	struct data_buf *buf, *tmp;
+
+	spin_lock_irq(&priv->lock);
+	BUG_ON(!list_empty(&priv->inflight));
+
+	list_for_each_entry_safe(buf, tmp, &priv->free, entry) {
+		list_del_init(&buf->entry);
+		spin_unlock_irq(&priv->lock);
+		data_free_buffer(dev, buf);
+		spin_lock_irq(&priv->lock);
+	}
+
+	list_for_each_entry_safe(buf, tmp, &priv->used, entry) {
+		list_del_init(&buf->entry);
+		spin_unlock_irq(&priv->lock);
+		data_free_buffer(dev, buf);
+		spin_lock_irq(&priv->lock);
+	}
+
+	priv->num_buffers = 0;
+	priv->bufsize = 0;
+
+	spin_unlock_irq(&priv->lock);
+}
+
+/**
+ * data_alloc_buffers() - allocate 1 seconds worth of data buffers
+ * @priv: the driver's private data structure
+ *
+ * Allocate enough buffers for a whole second worth of data
+ *
+ * This routine will attempt to degrade nicely by succeeding even if a full
+ * second worth of data buffers could not be allocated, as long as a minimum
+ * number were allocated. In this case, it will print a message to the kernel
+ * log.
+ *
+ * CONTEXT: user
+ * LOCKING: must hold dev->mutex
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_alloc_buffers(struct fpga_device *priv)
+{
+	struct device *dev = priv->dmadev;
+	struct data_buf *buf;
+	int i;
+
+	for (i = 0; i < MAX_DATA_BUFS; i++) {
+		buf = data_alloc_buffer(dev, priv->bufsize);
+		if (!buf)
+			break;
+
+		spin_lock_irq(&priv->lock);
+		list_add_tail(&buf->entry, &priv->free);
+		spin_unlock_irq(&priv->lock);
+	}
+
+	/* Make sure we allocated the minimum required number of buffers */
+	if (i < MIN_DATA_BUFS) {
+		dev_err(priv->dev, "Unable to allocate enough data buffers\n");
+		data_free_buffers(priv);
+		return -ENOMEM;
+	}
+
+	/* Warn if we are running in a degraded state, but do not fail */
+	if (i < MAX_DATA_BUFS) {
+		dev_warn(priv->dev, "Unable to allocate one second worth of "
+				   "buffers, using %d buffers instead\n", i);
+	}
+
+	priv->num_buffers = i;
+	return 0;
+}
+
+/*
+ * DMA Operations Helpers
+ */
+
+/**
+ * fpga_start_addr() - get the physical address a DATA-FPGA
+ * @priv: the driver's private data structure
+ * @fpga: the DATA-FPGA number (zero based)
+ */
+static dma_addr_t fpga_start_addr(struct fpga_device *priv, unsigned int fpga)
+{
+	return priv->phys_addr + 0x400000 + (0x80000 * fpga);
+}
+
+/**
+ * fpga_block_addr() - get the physical address of a correlation data block
+ * @priv: the driver's private data structure
+ * @fpga: the DATA-FPGA number (zero based)
+ * @blknum: the correlation block number (zero based)
+ */
+static dma_addr_t fpga_block_addr(struct fpga_device *priv, unsigned int fpga,
+				  unsigned int blknum)
+{
+	return fpga_start_addr(priv, fpga) + (0x10000 * (1 + blknum));
+}
+
+/**
+ * fpga_append_correlation_data() - append correlation data for DMA
+ * @priv: the driver's private data structure
+ * @slave: the Freescale DMA_SLAVE structure
+ * @fpga: the DATA-FPGA number (zero based)
+ *
+ * Add the correlation data for a single DATA FPGA to the DMA_SLAVE structure
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int fpga_append_correlation_data(struct fpga_device *priv,
+					struct fsl_dma_slave *slave,
+					unsigned int fpga)
+{
+	struct fpga_info *info = &priv->info[fpga];
+	dma_addr_t addr;
+	size_t len;
+	int i, ret;
+
+	for (i = 0; i < info->num_corl; i++) {
+		addr = fpga_block_addr(priv, fpga, i);
+		len = info->blk_size;
+		ret = fsl_dma_slave_append(slave, addr, len, GFP_KERNEL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * data_setup_dma_slave() - create the DMA_SLAVE structure
+ * @priv: the driver's private data structure
+ *
+ * Create the DMA_SLAVE structure for transferring data from the DATA FPGA's
+ *
+ * This structure will be reused for each buffer that needs to be filled
+ * with correlation data from the DATA FPGA's
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_setup_dma_slave(struct fpga_device *priv)
+{
+	static const char prefix[] = "DMA_SLAVE: unable to";
+	struct fsl_dma_slave *slave;
+	dma_addr_t addr;
+	size_t len;
+	int i, ret;
+
+	/* Create the Freescale DMA_SLAVE structure */
+	slave = fsl_dma_slave_alloc(GFP_KERNEL);
+	if (!slave) {
+		dev_err(priv->dev, "%s allocate structure\n", prefix);
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	/* Add the FPGA registers to the slave list */
+	for (i = 0; i < NUM_FPGA; i++) {
+		addr = fpga_start_addr(priv, i);
+		len = 32 * 4;
+		ret = fsl_dma_slave_append(slave, addr, len, GFP_KERNEL);
+		if (ret) {
+			dev_err(priv->dev, "%s add FPGA registers\n", prefix);
+			goto out_free_slave;
+		}
+	}
+
+	/* Add the SYS-FPGA registers to the slave list */
+	addr = SYS_FPGA_BLOCK;
+	len = 32 * 4;
+	ret = fsl_dma_slave_append(slave, addr, len, GFP_KERNEL);
+	if (ret) {
+		dev_err(priv->dev, "%s add SYS-FPGA registers\n", prefix);
+		goto out_free_slave;
+	}
+
+	/* Add the FPGA correlation data blocks to the slave list */
+	for (i = 0; i < NUM_FPGA; i++) {
+		ret = fpga_append_correlation_data(priv, slave, i);
+		if (ret) {
+			dev_err(priv->dev, "%s add correlation data\n", prefix);
+			goto out_free_slave;
+		}
+	}
+
+	/*
+	 * That's everything, this slave structure can be re-used for
+	 * every FPGA DATA interrupt
+	 */
+	priv->slave = slave;
+	return 0;
+
+out_free_slave:
+	fsl_dma_slave_free(slave);
+out_return:
+	return ret;
+}
+
+/*
+ * FPGA Register Access Helpers
+ */
+
+static void fpga_write_reg(struct fpga_device *priv, unsigned int fpga,
+			   unsigned int reg, u32 val)
+{
+	iowrite32be(val, priv->regs + 0x400000 + (fpga * 0x80000) + reg);
+}
+
+static u32 fpga_read_reg(struct fpga_device *priv, unsigned int fpga,
+			 unsigned int reg)
+{
+	return ioread32be(priv->regs + 0x400000 + (fpga * 0x80000) + reg);
+}
+
+/**
+ * data_calculate_bufsize() - calculate the data buffer size required
+ * @priv: the driver's private data structure
+ *
+ * Calculate the total buffer size needed to hold a single block
+ * of correlation data
+ *
+ * CONTEXT: user
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_calculate_bufsize(struct fpga_device *priv)
+{
+	u32 num_corl, num_lags, num_meta, num_qcnt, blk_size;
+	u32 conf1, conf2;
+	int i;
+
+	/* Zero the total buffer size */
+	priv->bufsize = 0;
+
+	/* Read and store the configuration data for each FPGA */
+	for (i = 0; i < NUM_FPGA; i++) {
+		conf1 = fpga_read_reg(priv, i, MMAP_REG_CORL_CONF1);
+		conf2 = fpga_read_reg(priv, i, MMAP_REG_CORL_CONF2);
+
+		num_corl = (conf1 & 0x000000F0) >> 4;
+		num_lags = (conf1 & 0x000FFF00) >> 8;
+		num_meta = (conf1 & 0x7FF00000) >> 20;
+		num_qcnt = (conf2 & 0x00000FFF) >> 0;
+		blk_size = (num_lags + num_meta + num_qcnt) * 8;
+
+		priv->info[i].num_corl = num_corl;
+		priv->info[i].blk_size = blk_size;
+		priv->bufsize += num_corl * blk_size;
+
+		dev_dbg(priv->dev, "FPGA %d NUM_CORL: %d\n", i, num_corl);
+		dev_dbg(priv->dev, "FPGA %d NUM_LAGS: %d\n", i, num_lags);
+		dev_dbg(priv->dev, "FPGA %d NUM_META: %d\n", i, num_meta);
+		dev_dbg(priv->dev, "FPGA %d NUM_QCNT: %d\n", i, num_qcnt);
+		dev_dbg(priv->dev, "FPGA %d BLK_SIZE: %d\n", i, blk_size);
+	}
+
+	/* Add in the 5 FPGA register areas */
+	priv->bufsize += 5 * (32 * 4);
+	dev_dbg(priv->dev, "TOTAL BUFFER SIZE: %zu bytes\n", priv->bufsize);
+
+	return 0;
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/**
+ * data_disable_interrupts() - stop the device from generating interrupts
+ * @priv: the driver's private data structure
+ *
+ * Hide interrupts by switching to GPIO interrupt source
+ *
+ * LOCKING: must hold dev->lock
+ */
+static void data_disable_interrupts(struct fpga_device *priv)
+{
+	/* hide the interrupt by switching the IRQ driver to GPIO */
+	iowrite32be(0x2F, priv->regs + SYS_IRQ_SOURCE_CTL);
+}
+
+/**
+ * data_enable_interrupts() - allow the device to generate interrupts
+ * @priv: the driver's private data structure
+ *
+ * Unhide interrupts by switching to the FPGA interrupt source. At the
+ * same time, clear the DATA-FPGA status registers.
+ *
+ * LOCKING: must hold dev->lock
+ */
+static void data_enable_interrupts(struct fpga_device *priv)
+{
+	/* clear the actual FPGA corl_done interrupt */
+	fpga_write_reg(priv, 0, MMAP_REG_STATUS, 0x0);
+	fpga_write_reg(priv, 1, MMAP_REG_STATUS, 0x0);
+	fpga_write_reg(priv, 2, MMAP_REG_STATUS, 0x0);
+	fpga_write_reg(priv, 3, MMAP_REG_STATUS, 0x0);
+
+	/* flush the writes */
+	fpga_read_reg(priv, 0, MMAP_REG_STATUS);
+
+	/* switch back to the external interrupt source */
+	iowrite32be(0x3F, priv->regs + SYS_IRQ_SOURCE_CTL);
+}
+
+/**
+ * data_dma_cb() - DMAEngine callback for DMA completion
+ * @data: the driver's private data structure
+ *
+ * Complete a DMA transfer from the DATA-FPGA's
+ *
+ * This is called via the DMA callback mechanism, and will handle moving the
+ * completed DMA transaction to the used list, and then wake any processes
+ * waiting for new data
+ *
+ * CONTEXT: any, softirq expected
+ */
+static void data_dma_cb(void *data)
+{
+	struct fpga_device *priv = data;
+	struct data_buf *buf;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->lock, flags);
+
+	/* clear the FPGA status and re-enable interrupts */
+	data_enable_interrupts(priv);
+
+	/* If the inflight list is empty, we've got a bug */
+	BUG_ON(list_empty(&priv->inflight));
+
+	/* Grab the first buffer from the inflight list */
+	buf = list_first_entry(&priv->inflight, struct data_buf, entry);
+	list_del_init(&buf->entry);
+
+	/* Add it to the used list */
+	list_add_tail(&buf->entry, &priv->used);
+
+	spin_unlock_irqrestore(&priv->lock, flags);
+
+	/* We've changed both the inflight and used lists, so we need
+	 * to wake up any processes that are blocking for those events */
+	wake_up(&priv->wait);
+}
+
+/**
+ * data_submit_dma() - prepare and submit the required DMA to fill a buffer
+ * @priv: the driver's private data structure
+ * @buf: the data buffer
+ *
+ * Prepare and submit a DMA_SLAVE transaction for a correlation data buffer
+ *
+ * LOCKING: must hold dev->lock
+ * CONTEXT: hardirq only
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
+{
+	struct scatterlist *sg = buf->vb.sglist;
+	unsigned int nents = buf->vb.sglen;
+	struct dma_chan *chan = priv->chan;
+	struct dma_async_tx_descriptor *tx;
+	dma_cookie_t cookie;
+	dma_addr_t dst, src;
+
+	/*
+	 * All buffers passed to this function should be ready and mapped
+	 * for DMA already. Therefore, we don't need to do anything except
+	 * submit it to the Freescale DMA Engine for processing
+	 */
+
+	/* setup the DMA_SLAVE transaction */
+	chan->private = priv->slave;
+	tx = chan->device->device_prep_slave_sg(chan, sg, nents,
+						DMA_FROM_DEVICE, 0);
+	if (!tx) {
+		dev_err(priv->dev, "unable to prep slave DMA 1\n");
+		return -ENOMEM;
+	}
+
+	/* submit the transaction to the DMA controller */
+	cookie = tx->tx_submit(tx);
+	if (dma_submit_error(cookie)) {
+		dev_err(priv->dev, "unable to submit slave DMA 1\n");
+		return -ENOMEM;
+	}
+
+	/* Prepare the re-read of the SYS-FPGA block */
+	dst = sg_dma_address(sg) + (NUM_FPGA * 32 * 4);
+	src = SYS_FPGA_BLOCK;
+	tx = chan->device->device_prep_dma_memcpy(chan, dst, src, 32 * 4,
+						  DMA_PREP_INTERRUPT);
+	if (!tx) {
+		dev_err(priv->dev, "unable to prep slave DMA 2\n");
+		return -ENOMEM;
+	}
+
+	/* Setup the callback */
+	tx->callback = data_dma_cb;
+	tx->callback_param = priv;
+
+	/* submit the transaction to the DMA controller */
+	cookie = tx->tx_submit(tx);
+	if (dma_submit_error(cookie)) {
+		dev_err(priv->dev, "unable to submit slave DMA 2\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+#define CORL_DONE	0x1
+#define CORL_ERR	0x2
+
+static irqreturn_t data_irq(int irq, void *dev_id)
+{
+	struct fpga_device *priv = dev_id;
+	struct data_buf *buf;
+	u32 status;
+	int i;
+
+	/* detect spurious interrupts via FPGA status */
+	for (i = 0; i < 4; i++) {
+		status = fpga_read_reg(priv, i, MMAP_REG_STATUS);
+		if (!(status & (CORL_DONE | CORL_ERR))) {
+			dev_err(priv->dev, "spurious irq detected (FPGA)\n");
+			return IRQ_NONE;
+		}
+	}
+
+	/* detect spurious interrupts via raw IRQ pin readback */
+	status = ioread32be(priv->regs + SYS_IRQ_INPUT_DATA);
+	if (status & IRQ_CORL_DONE) {
+		dev_err(priv->dev, "spurious irq detected (IRQ)\n");
+		return IRQ_NONE;
+	}
+
+	spin_lock(&priv->lock);
+
+	/* hide the interrupt by switching the IRQ driver to GPIO */
+	data_disable_interrupts(priv);
+
+	/* Check that we actually have a free buffer */
+	if (list_empty(&priv->free)) {
+		priv->num_dropped++;
+		data_enable_interrupts(priv);
+		goto out_unlock;
+	}
+
+	buf = list_first_entry(&priv->free, struct data_buf, entry);
+	list_del_init(&buf->entry);
+
+	/* Check the buffer size */
+	BUG_ON(buf->size != priv->bufsize);
+
+	/* Submit a DMA transfer to get the correlation data */
+	if (data_submit_dma(priv, buf)) {
+		dev_err(priv->dev, "Unable to setup DMA transfer\n");
+		list_add_tail(&buf->entry, &priv->free);
+		data_enable_interrupts(priv);
+		goto out_unlock;
+	}
+
+	/* DMA setup succeeded, GO!!! */
+	list_add_tail(&buf->entry, &priv->inflight);
+	dma_async_memcpy_issue_pending(priv->chan);
+
+out_unlock:
+	spin_unlock(&priv->lock);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Realtime Device Enable Helpers
+ */
+
+/**
+ * data_device_enable() - enable the device for buffered dumping
+ * @priv: the driver's private data structure
+ *
+ * Enable the device for buffered dumping. Allocates buffers and hooks up
+ * the interrupt handler. When this finishes, data will come pouring in.
+ *
+ * LOCKING: must hold dev->mutex
+ * CONTEXT: user context only
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_device_enable(struct fpga_device *priv)
+{
+	u32 val;
+	int ret;
+
+	/* multiple enables are safe: they do nothing */
+	if (priv->enabled)
+		return 0;
+
+	/* check that the FPGAs are programmed */
+	val = ioread32be(priv->regs + 0x44);
+	if (!(val & (1 << 18))) {
+		dev_err(priv->dev, "DATA-FPGAs are not enabled\n");
+		return -ENODATA;
+	}
+
+	/* read the FPGAs to calculate the buffer size */
+	ret = data_calculate_bufsize(priv);
+	if (ret) {
+		dev_err(priv->dev, "unable to calculate buffer size\n");
+		goto out_error;
+	}
+
+	/* allocate the correlation data buffers */
+	ret = data_alloc_buffers(priv);
+	if (ret) {
+		dev_err(priv->dev, "unable to allocate buffers\n");
+		goto out_error;
+	}
+
+	/* allocate the DMA_SLAVE structure for correlation data */
+	ret = data_setup_dma_slave(priv);
+	if (ret) {
+		dev_err(priv->dev, "unable to setup DMA list\n");
+		goto out_error;
+	}
+
+	/* switch to the external FPGA IRQ line */
+	data_enable_interrupts(priv);
+
+	/* hookup the irq handler */
+	ret = request_irq(priv->irq, data_irq, IRQF_SHARED, drv_name, priv);
+	if (ret) {
+		dev_err(priv->dev, "unable to request IRQ handler\n");
+		goto out_free_slave;
+	}
+
+	/* success, we're enabled */
+	priv->enabled = true;
+	return 0;
+
+out_free_slave:
+	fsl_dma_slave_free(priv->slave);
+	priv->slave = NULL;
+out_error:
+	data_free_buffers(priv);
+	return ret;
+}
+
+/**
+ * data_device_disable() - disable the device for buffered dumping
+ * @priv: the driver's private data structure
+ *
+ * Disable the device for buffered dumping. Stops new DMA transactions from
+ * being generated, waits for all outstanding DMA to complete, and then frees
+ * all buffers.
+ *
+ * LOCKING: must hold dev->mutex
+ * CONTEXT: user only
+ *
+ * Returns 0 on success, -ERRNO otherwise
+ */
+static int data_device_disable(struct fpga_device *priv)
+{
+	struct list_head *list;
+	int ret;
+
+	/* allow multiple disable */
+	if (!priv->enabled)
+		return 0;
+
+	/* switch to the internal GPIO IRQ line */
+	data_disable_interrupts(priv);
+
+	/* unhook the irq handler */
+	free_irq(priv->irq, priv);
+
+	/* wait for all outstanding DMA to complete */
+	list = &priv->inflight;
+
+	spin_lock_irq(&priv->lock);
+	while (!list_empty(list)) {
+		spin_unlock_irq(&priv->lock);
+
+		ret = wait_event_interruptible(priv->wait, list_empty(list));
+		if (ret)
+			return -ERESTARTSYS;
+
+		spin_lock_irq(&priv->lock);
+	}
+	spin_unlock_irq(&priv->lock);
+
+	/* free the DMA_SLAVE structure */
+	fsl_dma_slave_free(priv->slave);
+	priv->slave = NULL;
+
+	/* free all of the buffers */
+	data_free_buffers(priv);
+	priv->enabled = false;
+	return 0;
+}
+
+/*
+ * SYSFS Attributes
+ */
+
+/*
+ * Count the number of entries in the given list
+ */
+static unsigned int list_num_entries(struct list_head *list)
+{
+	struct list_head *entry;
+	unsigned int ret = 0;
+
+	list_for_each(entry, list)
+		ret++;
+
+	return ret;
+}
+
+static ssize_t data_num_buffers_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	unsigned int num;
+
+	spin_lock_irq(&priv->lock);
+	num = priv->num_buffers;
+	spin_unlock_irq(&priv->lock);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_bufsize_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	size_t num;
+
+	spin_lock_irq(&priv->lock);
+	num = priv->bufsize;
+	spin_unlock_irq(&priv->lock);
+
+	return snprintf(buf, PAGE_SIZE, "%zu\n", num);
+}
+
+static ssize_t data_inflight_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	unsigned int num;
+
+	spin_lock_irq(&priv->lock);
+	num = list_num_entries(&priv->inflight);
+	spin_unlock_irq(&priv->lock);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_free_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	unsigned int num;
+
+	spin_lock_irq(&priv->lock);
+	num = list_num_entries(&priv->free);
+	spin_unlock_irq(&priv->lock);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_used_show(struct device *dev,
+			      struct device_attribute *attr, char *buf)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	unsigned int num;
+
+	spin_lock_irq(&priv->lock);
+	num = list_num_entries(&priv->used);
+	spin_unlock_irq(&priv->lock);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_num_dropped_show(struct device *dev,
+				     struct device_attribute *attr, char *buf)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	unsigned int num;
+
+	spin_lock_irq(&priv->lock);
+	num = priv->num_dropped;
+	spin_unlock_irq(&priv->lock);
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", num);
+}
+
+static ssize_t data_en_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	ssize_t count;
+
+	if (mutex_lock_interruptible(&priv->mutex))
+		return -ERESTARTSYS;
+
+	count = snprintf(buf, PAGE_SIZE, "%u\n", priv->enabled);
+	mutex_unlock(&priv->mutex);
+	return count;
+}
+
+static ssize_t data_en_set(struct device *dev, struct device_attribute *attr,
+			   const char *buf, size_t count)
+{
+	struct fpga_device *priv = dev_get_drvdata(dev);
+	unsigned long enable;
+	int ret;
+
+	ret = strict_strtoul(buf, 0, &enable);
+	if (ret) {
+		dev_err(priv->dev, "unable to parse enable input\n");
+		return -EINVAL;
+	}
+
+	if (mutex_lock_interruptible(&priv->mutex))
+		return -ERESTARTSYS;
+
+	if (enable)
+		ret = data_device_enable(priv);
+	else
+		ret = data_device_disable(priv);
+
+	if (ret) {
+		dev_err(priv->dev, "device %s failed\n",
+			enable ? "enable" : "disable");
+		count = ret;
+		goto out_unlock;
+	}
+
+out_unlock:
+	mutex_unlock(&priv->mutex);
+	return count;
+}
+
+static DEVICE_ATTR(num_buffers, S_IRUGO, data_num_buffers_show, NULL);
+static DEVICE_ATTR(buffer_size, S_IRUGO, data_bufsize_show, NULL);
+static DEVICE_ATTR(num_inflight, S_IRUGO, data_inflight_show, NULL);
+static DEVICE_ATTR(num_free, S_IRUGO, data_free_show, NULL);
+static DEVICE_ATTR(num_used, S_IRUGO, data_used_show, NULL);
+static DEVICE_ATTR(num_dropped, S_IRUGO, data_num_dropped_show, NULL);
+static DEVICE_ATTR(enable, S_IWUGO | S_IRUGO, data_en_show, data_en_set);
+
+static struct attribute *data_sysfs_attrs[] = {
+	&dev_attr_num_buffers.attr,
+	&dev_attr_buffer_size.attr,
+	&dev_attr_num_inflight.attr,
+	&dev_attr_num_free.attr,
+	&dev_attr_num_used.attr,
+	&dev_attr_num_dropped.attr,
+	&dev_attr_enable.attr,
+	NULL,
+};
+
+static const struct attribute_group rt_sysfs_attr_group = {
+	.attrs = data_sysfs_attrs,
+};
+
+/*
+ * FPGA Realtime Data Character Device
+ */
+
+static int data_open(struct inode *inode, struct file *filp)
+{
+	struct fpga_device *priv = inode_to_dev(inode);
+	struct fpga_reader *reader;
+	int ret;
+
+	/* allocate private data */
+	reader = kzalloc(sizeof(*reader), GFP_KERNEL);
+	if (!reader)
+		return -ENOMEM;
+
+	reader->priv = priv;
+	reader->buf = NULL;
+
+	filp->private_data = reader;
+	ret = nonseekable_open(inode, filp);
+	if (ret) {
+		dev_err(priv->dev, "nonseekable-open failed\n");
+		kfree(reader);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int data_release(struct inode *inode, struct file *filp)
+{
+	struct fpga_reader *reader = filp->private_data;
+	struct fpga_device *priv = reader->priv;
+
+	/* free the per-reader structure */
+	data_free_buffer(priv->dmadev, reader->buf);
+	kfree(reader);
+	return 0;
+}
+
+static ssize_t data_read(struct file *filp, char __user *ubuf, size_t count,
+			 loff_t *f_pos)
+{
+	struct fpga_reader *reader = filp->private_data;
+	struct fpga_device *priv = reader->priv;
+	struct list_head *used = &priv->used;
+	struct data_buf *dbuf;
+	size_t avail;
+	void *data;
+	int ret;
+
+	/* check if we already have a partial buffer */
+	if (reader->buf) {
+		dbuf = reader->buf;
+		goto have_buffer;
+	}
+
+	spin_lock_irq(&priv->lock);
+
+	/* Block until there is at least one buffer on the used list */
+	while (list_empty(used)) {
+		spin_unlock_irq(&priv->lock);
+
+		if (filp->f_flags & O_NONBLOCK)
+			return -EAGAIN;
+
+		if (wait_event_interruptible(priv->wait, !list_empty(used)))
+			return -ERESTARTSYS;
+
+		spin_lock_irq(&priv->lock);
+	}
+
+	/* Grab the first buffer off of the used list */
+	dbuf = list_first_entry(used, struct data_buf, entry);
+	list_del_init(&dbuf->entry);
+
+	spin_unlock_irq(&priv->lock);
+
+	/* Buffers are always mapped: unmap it */
+	data_unmap_buffer(priv->dmadev, dbuf);
+
+	/* save the buffer for later */
+	reader->buf = dbuf;
+	reader->buf_start = 0;
+
+	/* we removed a buffer from the used list: wake any waiters */
+	wake_up(&priv->wait);
+
+have_buffer:
+	/* Get the number of bytes available */
+	avail = dbuf->size - reader->buf_start;
+	data = dbuf->vb.vaddr + reader->buf_start;
+
+	/* Get the number of bytes we can transfer */
+	count = min(count, avail);
+
+	/* Copy the data to the userspace buffer */
+	if (copy_to_user(ubuf, data, count))
+		return -EFAULT;
+
+	/* Update the amount of available space */
+	avail -= count;
+
+	/* Lock against concurrent enable/disable */
+	if (mutex_lock_interruptible(&priv->mutex))
+		return -ERESTARTSYS;
+
+	/* Still some space available: save the buffer for later */
+	if (avail != 0) {
+		reader->buf_start += count;
+		reader->buf = dbuf;
+		goto out_unlock;
+	}
+
+	/*
+	 * No space is available in this buffer
+	 *
+	 * This is a complicated decision:
+	 * - if the device is not enabled: free the buffer
+	 * - if the buffer is too small: free the buffer
+	 */
+	if (!priv->enabled || dbuf->size != priv->bufsize) {
+		data_free_buffer(priv->dmadev, dbuf);
+		reader->buf = NULL;
+		goto out_unlock;
+	}
+
+	/*
+	 * The buffer is safe to recycle: remap it and finish
+	 *
+	 * If this fails, we pretend that the read never happened, and return
+	 * -EFAULT to userspace. They'll retry the read again.
+	 */
+	ret = data_map_buffer(priv->dmadev, dbuf);
+	if (ret) {
+		dev_err(priv->dev, "unable to remap buffer for DMA\n");
+		count = -EFAULT;
+		goto out_unlock;
+	}
+
+	/* Add the buffer back to the free list */
+	reader->buf = NULL;
+	spin_lock_irq(&priv->lock);
+	list_add_tail(&dbuf->entry, &priv->free);
+	spin_unlock_irq(&priv->lock);
+
+out_unlock:
+	mutex_unlock(&priv->mutex);
+	return count;
+}
+
+static unsigned int data_poll(struct file *filp, struct poll_table_struct *tbl)
+{
+	struct fpga_reader *reader = filp->private_data;
+	struct fpga_device *priv = reader->priv;
+	unsigned int mask = 0;
+
+	poll_wait(filp, &priv->wait, tbl);
+
+	spin_lock_irq(&priv->lock);
+
+	if (!list_empty(&priv->used))
+		mask |= POLLIN | POLLRDNORM;
+
+	spin_unlock_irq(&priv->lock);
+	return mask;
+}
+
+static int data_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct fpga_reader *reader = filp->private_data;
+	struct fpga_device *priv = reader->priv;
+	unsigned long offset, vsize, psize, addr;
+
+	/* VMA properties */
+	offset = vma->vm_pgoff << PAGE_SHIFT;
+	vsize = vma->vm_end - vma->vm_start;
+	psize = priv->phys_size - offset;
+	addr = (priv->phys_addr + offset) >> PAGE_SHIFT;
+
+	/* Check against the FPGA region's physical memory size */
+	if (vsize > psize) {
+		dev_err(priv->dev, "requested mmap mapping too large\n");
+		return -EINVAL;
+	}
+
+	/* IO memory (stop cacheing) */
+	vma->vm_flags |= VM_IO | VM_RESERVED;
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+	return io_remap_pfn_range(vma, vma->vm_start, addr, vsize,
+				  vma->vm_page_prot);
+}
+
+static const struct file_operations data_fops = {
+	.owner		= THIS_MODULE,
+	.open		= data_open,
+	.release	= data_release,
+	.read		= data_read,
+	.poll		= data_poll,
+	.mmap		= data_mmap,
+	.llseek		= no_llseek,
+};
+
+/*
+ * OpenFirmware Device Subsystem
+ */
+
+static bool dma_filter(struct dma_chan *chan, void *data)
+{
+	/*
+	 * DMA Channel #0 is used for the FPGA Programmer, so ignore it
+	 *
+	 * This probably won't survive an unload/load cycle of the Freescale
+	 * DMAEngine driver, but that won't be a problem
+	 */
+	if (chan->chan_id == 0 && chan->device->dev_id == 0)
+		return false;
+
+	return true;
+}
+
+static int data_of_probe(struct platform_device *op,
+			 const struct of_device_id *match)
+{
+	struct device_node *of_node = op->dev.of_node;
+	struct fpga_device *priv;
+	struct resource res;
+	dma_cap_mask_t mask;
+	int ret;
+
+	/* Allocate private data */
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&op->dev, "Unable to allocate device private data\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	dev_set_drvdata(&op->dev, priv);
+	priv->dmadev = &op->dev;
+
+	/* Allocate the character device */
+	ret = alloc_chrdev_region(&priv->devno, 0, 1, drv_name);
+	if (ret) {
+		dev_err(&op->dev, "Unable to allocate chardev region\n");
+		goto out_free_priv;
+	}
+
+	/* Get the physical address of the FPGA registers */
+	ret = of_address_to_resource(of_node, 0, &res);
+	if (ret) {
+		dev_err(&op->dev, "Unable to find FPGA physical address\n");
+		ret = -ENODEV;
+		goto out_unregister_chrdev_region;
+	}
+
+	priv->phys_addr = res.start;
+	priv->phys_size = resource_size(&res);
+
+	/* ioremap the registers for use */
+	priv->regs = of_iomap(of_node, 0);
+	if (!priv->regs) {
+		dev_err(&op->dev, "Unable to ioremap registers\n");
+		ret = -ENOMEM;
+		goto out_unregister_chrdev_region;
+	}
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_MEMCPY, mask);
+	dma_cap_set(DMA_INTERRUPT, mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	/* Request a DMA channel */
+	priv->chan = dma_request_channel(mask, dma_filter, NULL);
+	if (!priv->chan) {
+		dev_err(&op->dev, "Unable to request DMA channel\n");
+		ret = -ENODEV;
+		goto out_unmap_regs;
+	}
+
+	/* Find the correct IRQ number */
+	priv->irq = irq_of_parse_and_map(of_node, 0);
+	if (priv->irq == NO_IRQ) {
+		dev_err(&op->dev, "Unable to find IRQ line\n");
+		ret = -ENODEV;
+		goto out_release_dma;
+	}
+
+	priv->dev = carma_device_create(&op->dev, priv->devno, drv_name);
+	if (IS_ERR(priv->dev)) {
+		dev_err(&op->dev, "Unable to create CARMA device\n");
+		ret = PTR_ERR(priv->dev);
+		goto out_irq_dispose_mapping;
+	}
+
+	dev_set_drvdata(priv->dev, priv);
+	cdev_init(&priv->cdev, &data_fops);
+	mutex_init(&priv->mutex);
+	spin_lock_init(&priv->lock);
+	INIT_LIST_HEAD(&priv->free);
+	INIT_LIST_HEAD(&priv->used);
+	INIT_LIST_HEAD(&priv->inflight);
+	init_waitqueue_head(&priv->wait);
+
+	/* Drive the GPIO for FPGA IRQ high (no interrupt) */
+	iowrite32be(IRQ_CORL_DONE, priv->regs + SYS_IRQ_OUTPUT_DATA);
+
+	/* Register the character device */
+	ret = cdev_add(&priv->cdev, priv->devno, 1);
+	if (ret) {
+		dev_err(&op->dev, "Unable to add character device\n");
+		goto out_destroy_carma_device;
+	}
+
+	/* Create the sysfs files */
+	ret = sysfs_create_group(&priv->dev->kobj, &rt_sysfs_attr_group);
+	if (ret) {
+		dev_err(&op->dev, "Unable to create sysfs files\n");
+		goto out_cdev_del;
+	}
+
+	dev_info(&op->dev, "CARMA FPGA Realtime Data Driver Loaded\n");
+	return 0;
+
+out_cdev_del:
+	cdev_del(&priv->cdev);
+out_destroy_carma_device:
+	carma_device_destroy(priv->devno);
+out_irq_dispose_mapping:
+	irq_dispose_mapping(priv->irq);
+out_release_dma:
+	dma_release_channel(priv->chan);
+out_unmap_regs:
+	iounmap(priv->regs);
+out_unregister_chrdev_region:
+	unregister_chrdev_region(priv->devno, 1);
+out_free_priv:
+	kfree(priv);
+out_return:
+	return ret;
+}
+
+static int data_of_remove(struct platform_device *op)
+{
+	struct fpga_device *priv = dev_get_drvdata(&op->dev);
+
+	/* make sure the IRQ line is disabled */
+	mutex_lock(&priv->mutex);
+	data_device_disable(priv);
+	mutex_unlock(&priv->mutex);
+
+	sysfs_remove_group(&priv->dev->kobj, &rt_sysfs_attr_group);
+	cdev_del(&priv->cdev);
+	carma_device_destroy(priv->devno);
+
+	irq_dispose_mapping(priv->irq);
+	dma_release_channel(priv->chan);
+	iounmap(priv->regs);
+	unregister_chrdev_region(priv->devno, 1);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct of_device_id data_of_match[] = {
+	{ .compatible = "carma,carma-fpga", },
+	{},
+};
+
+static struct of_platform_driver data_of_driver = {
+	.probe		= data_of_probe,
+	.remove		= data_of_remove,
+	.driver		= {
+		.name		= drv_name,
+		.of_match_table	= data_of_match,
+		.owner		= THIS_MODULE,
+	},
+};
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init data_init(void)
+{
+	return of_register_platform_driver(&data_of_driver);
+}
+
+static void __exit data_exit(void)
+{
+	of_unregister_platform_driver(&data_of_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("CARMA DATA-FPGA Access Driver");
+MODULE_LICENSE("GPL");
+
+module_init(data_init);
+module_exit(data_exit);
-- 
1.7.1

^ permalink raw reply related

* [PATCH 3/5] fpga: add basic CARMA board support
From: Ira W. Snyder @ 2010-09-08 16:41 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder
In-Reply-To: <1283964082-30133-1-git-send-email-iws@ovro.caltech.edu>

This adds basic support for the system controller FPGA on the OVRO CARMA
board. This patch only adds infrastructure that will be used by later
drivers.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 drivers/Kconfig             |    2 +
 drivers/Makefile            |    1 +
 drivers/fpga/Kconfig        |    1 +
 drivers/fpga/Makefile       |    1 +
 drivers/fpga/carma/Kconfig  |   21 ++++++++++++
 drivers/fpga/carma/Makefile |    1 +
 drivers/fpga/carma/carma.c  |   73 +++++++++++++++++++++++++++++++++++++++++++
 drivers/fpga/carma/carma.h  |   22 +++++++++++++
 8 files changed, 122 insertions(+), 0 deletions(-)
 create mode 100644 drivers/fpga/Kconfig
 create mode 100644 drivers/fpga/Makefile
 create mode 100644 drivers/fpga/carma/Kconfig
 create mode 100644 drivers/fpga/carma/Makefile
 create mode 100644 drivers/fpga/carma/carma.c
 create mode 100644 drivers/fpga/carma/carma.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index a2b902f..8945ae6 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -111,4 +111,6 @@ source "drivers/xen/Kconfig"
 source "drivers/staging/Kconfig"
 
 source "drivers/platform/Kconfig"
+
+source "drivers/fpga/Kconfig"
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index ae47344..c0b05de 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -115,3 +115,4 @@ obj-$(CONFIG_VLYNQ)		+= vlynq/
 obj-$(CONFIG_STAGING)		+= staging/
 obj-y				+= platform/
 obj-y				+= ieee802154/
+obj-$(CONFIG_FPGA_DRIVERS)	+= fpga/
diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig
new file mode 100644
index 0000000..c85c2cc
--- /dev/null
+++ b/drivers/fpga/Kconfig
@@ -0,0 +1 @@
+source "drivers/fpga/carma/Kconfig"
diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile
new file mode 100644
index 0000000..409a5f9
--- /dev/null
+++ b/drivers/fpga/Makefile
@@ -0,0 +1 @@
+obj-y		+= carma/
diff --git a/drivers/fpga/carma/Kconfig b/drivers/fpga/carma/Kconfig
new file mode 100644
index 0000000..448885e
--- /dev/null
+++ b/drivers/fpga/carma/Kconfig
@@ -0,0 +1,21 @@
+
+menuconfig FPGA_DRIVERS
+	bool "FPGA Drivers"
+	default n
+	help
+	  Say Y here to see options for devices used with custom FPGAs.
+	  This option alone does not add any kernel code.
+
+	  If you say N, all options in this submenu will be skipped and disabled.
+
+if FPGA_DRIVERS
+
+config CARMA
+	tristate "CARMA System Controller FPGA support"
+	depends on FSL_SOC && PPC_83xx
+	default n
+	help
+	  Say Y here to include basic support for the CARMA System Controller
+	  FPGA. This option allows the other more advanced drivers to be built.
+
+endif # FPGA_DRIVERS
diff --git a/drivers/fpga/carma/Makefile b/drivers/fpga/carma/Makefile
new file mode 100644
index 0000000..90d0594
--- /dev/null
+++ b/drivers/fpga/carma/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_CARMA)			+= carma.o
diff --git a/drivers/fpga/carma/carma.c b/drivers/fpga/carma/carma.c
new file mode 100644
index 0000000..97549d2
--- /dev/null
+++ b/drivers/fpga/carma/carma.c
@@ -0,0 +1,73 @@
+/*
+ * CARMA Board Utility Driver
+ *
+ * Copyright (c) 2009-2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include "carma.h"
+
+static struct class *carma_class;
+static const char drv_name[] = "carma";
+
+/*
+ * CARMA Device Class Functions
+ */
+
+struct device *carma_device_create(struct device *parent, dev_t devno,
+				   const char *fmt, ...)
+{
+	struct device *dev;
+	va_list vargs;
+
+	va_start(vargs, fmt);
+	dev = device_create_vargs(carma_class, parent, devno, NULL, fmt, vargs);
+	va_end(vargs);
+
+	return dev;
+}
+EXPORT_SYMBOL_GPL(carma_device_create);
+
+void carma_device_destroy(dev_t devno)
+{
+	device_destroy(carma_class, devno);
+}
+EXPORT_SYMBOL_GPL(carma_device_destroy);
+
+/*
+ * Module Init / Exit
+ */
+
+static int __init carma_init(void)
+{
+	/* Register the CARMA device class */
+	carma_class = class_create(THIS_MODULE, "carma");
+	if (IS_ERR(carma_class)) {
+		pr_err("%s: unable to create CARMA class\n", drv_name);
+		return PTR_ERR(carma_class);
+	}
+
+	return 0;
+}
+
+static void __exit carma_exit(void)
+{
+	class_destroy(carma_class);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("CARMA Device Class Driver");
+MODULE_LICENSE("GPL");
+
+module_init(carma_init);
+module_exit(carma_exit);
diff --git a/drivers/fpga/carma/carma.h b/drivers/fpga/carma/carma.h
new file mode 100644
index 0000000..f556dc8
--- /dev/null
+++ b/drivers/fpga/carma/carma.h
@@ -0,0 +1,22 @@
+/*
+ * CARMA Board Utilities
+ *
+ * Copyright (c) 2009-2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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.
+ */
+
+#ifndef CARMA_DEVICE_H
+#define CARMA_DEVICE_H
+
+#include <linux/device.h>
+
+struct device *carma_device_create(struct device *parent, dev_t devno,
+				   const char *fmt, ...)
+				   __attribute__((format(printf, 3, 4)));
+void carma_device_destroy(dev_t devno);
+
+#endif /* CARMA_DEVICE_H */
-- 
1.7.1

^ permalink raw reply related

* [PATCH 2/5] fsldma: move DMA_SLAVE support functions to the driver
From: Ira W. Snyder @ 2010-09-08 16:41 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder
In-Reply-To: <1283964082-30133-1-git-send-email-iws@ovro.caltech.edu>

The DMA_SLAVE support functions all existed as static inlines in the
driver specific header arch/powerpc/include/asm/fsldma.h. Move the body
of the functions to the driver itself, and EXPORT_SYMBOL_GPL() them.

At the same time, add the missing linux/list.h header.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 arch/powerpc/include/asm/fsldma.h |   70 +++------------------------------
 drivers/dma/fsldma.c              |   77 +++++++++++++++++++++++++++++++++++++
 2 files changed, 83 insertions(+), 64 deletions(-)

diff --git a/arch/powerpc/include/asm/fsldma.h b/arch/powerpc/include/asm/fsldma.h
index debc5ed..34ec00b 100644
--- a/arch/powerpc/include/asm/fsldma.h
+++ b/arch/powerpc/include/asm/fsldma.h
@@ -1,7 +1,7 @@
 /*
  * Freescale MPC83XX / MPC85XX DMA Controller
  *
- * Copyright (c) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
+ * Copyright (c) 2009-2010 Ira W. Snyder <iws@ovro.caltech.edu>
  *
  * This file is licensed under the terms of the GNU General Public License
  * version 2. This program is licensed "as is" without any warranty of any
@@ -11,6 +11,7 @@
 #ifndef __ARCH_POWERPC_ASM_FSLDMA_H__
 #define __ARCH_POWERPC_ASM_FSLDMA_H__
 
+#include <linux/list.h>
 #include <linux/slab.h>
 #include <linux/dmaengine.h>
 
@@ -69,69 +70,10 @@ struct fsl_dma_slave {
 	bool external_pause;
 };
 
-/**
- * fsl_dma_slave_append - add an address/length pair to a struct fsl_dma_slave
- * @slave: the &struct fsl_dma_slave to add to
- * @address: the hardware address to add
- * @length: the length of bytes to transfer from @address
- *
- * Add a hardware address/length pair to a struct fsl_dma_slave. Returns 0 on
- * success, -ERRNO otherwise.
- */
-static inline int fsl_dma_slave_append(struct fsl_dma_slave *slave,
-				       dma_addr_t address, size_t length)
-{
-	struct fsl_dma_hw_addr *addr;
-
-	addr = kzalloc(sizeof(*addr), GFP_ATOMIC);
-	if (!addr)
-		return -ENOMEM;
-
-	INIT_LIST_HEAD(&addr->entry);
-	addr->address = address;
-	addr->length = length;
-
-	list_add_tail(&addr->entry, &slave->addresses);
-	return 0;
-}
-
-/**
- * fsl_dma_slave_free - free a struct fsl_dma_slave
- * @slave: the struct fsl_dma_slave to free
- *
- * Free a struct fsl_dma_slave and all associated address/length pairs
- */
-static inline void fsl_dma_slave_free(struct fsl_dma_slave *slave)
-{
-	struct fsl_dma_hw_addr *addr, *tmp;
-
-	if (slave) {
-		list_for_each_entry_safe(addr, tmp, &slave->addresses, entry) {
-			list_del(&addr->entry);
-			kfree(addr);
-		}
-
-		kfree(slave);
-	}
-}
-
-/**
- * fsl_dma_slave_alloc - allocate a struct fsl_dma_slave
- * @gfp: the flags to pass to kmalloc when allocating this structure
- *
- * Allocate a struct fsl_dma_slave for use by the DMA_SLAVE API. Returns a new
- * struct fsl_dma_slave on success, or NULL on failure.
- */
-static inline struct fsl_dma_slave *fsl_dma_slave_alloc(gfp_t gfp)
-{
-	struct fsl_dma_slave *slave;
-
-	slave = kzalloc(sizeof(*slave), gfp);
-	if (!slave)
-		return NULL;
+struct fsl_dma_slave *fsl_dma_slave_alloc(gfp_t gfp);
+void fsl_dma_slave_free(struct fsl_dma_slave *slave);
 
-	INIT_LIST_HEAD(&slave->addresses);
-	return slave;
-}
+int fsl_dma_slave_append(struct fsl_dma_slave *slave, dma_addr_t address,
+			 size_t length, gfp_t gfp);
 
 #endif /* __ARCH_POWERPC_ASM_FSLDMA_H__ */
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index cea08be..f436ca4 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -38,6 +38,83 @@
 #include <asm/fsldma.h>
 #include "fsldma.h"
 
+/*
+ * External API
+ */
+
+/**
+ * fsl_dma_slave_append - add an address/length pair to a struct fsl_dma_slave
+ * @slave: the &struct fsl_dma_slave to add to
+ * @address: the hardware address to add
+ * @length: the length of bytes to transfer from @address
+ * @gfp: the flags to pass to kmalloc when allocating memory
+ *
+ * Add a hardware address/length pair to a struct fsl_dma_slave. Returns 0 on
+ * success, -ERRNO otherwise.
+ */
+int fsl_dma_slave_append(struct fsl_dma_slave *slave, dma_addr_t address,
+			 size_t length, gfp_t gfp)
+{
+	struct fsl_dma_hw_addr *addr;
+
+	addr = kzalloc(sizeof(*addr), gfp);
+	if (!addr)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&addr->entry);
+	addr->address = address;
+	addr->length = length;
+
+	list_add_tail(&addr->entry, &slave->addresses);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fsl_dma_slave_append);
+
+/**
+ * fsl_dma_slave_free - free a struct fsl_dma_slave
+ * @slave: the struct fsl_dma_slave to free
+ *
+ * Free a struct fsl_dma_slave and all associated address/length pairs
+ */
+void fsl_dma_slave_free(struct fsl_dma_slave *slave)
+{
+	struct fsl_dma_hw_addr *addr, *tmp;
+
+	if (slave) {
+		list_for_each_entry_safe(addr, tmp, &slave->addresses, entry) {
+			list_del(&addr->entry);
+			kfree(addr);
+		}
+
+		kfree(slave);
+	}
+}
+EXPORT_SYMBOL_GPL(fsl_dma_slave_free);
+
+/**
+ * fsl_dma_slave_alloc - allocate a struct fsl_dma_slave
+ * @gfp: the flags to pass to kmalloc when allocating this structure
+ *
+ * Allocate a struct fsl_dma_slave for use by the DMA_SLAVE API. Returns a new
+ * struct fsl_dma_slave on success, or NULL on failure.
+ */
+struct fsl_dma_slave *fsl_dma_slave_alloc(gfp_t gfp)
+{
+	struct fsl_dma_slave *slave;
+
+	slave = kzalloc(sizeof(*slave), gfp);
+	if (!slave)
+		return NULL;
+
+	INIT_LIST_HEAD(&slave->addresses);
+	return slave;
+}
+EXPORT_SYMBOL_GPL(fsl_dma_slave_alloc);
+
+/*
+ * Driver Code
+ */
+
 static void dma_init(struct fsldma_chan *chan)
 {
 	/* Reset the channel */
-- 
1.7.1

^ permalink raw reply related

* [PATCH 1/5] fsldma: fix missing header include
From: Ira W. Snyder @ 2010-09-08 16:41 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder
In-Reply-To: <1283964082-30133-1-git-send-email-iws@ovro.caltech.edu>

The slab.h header is required to use the kmalloc() family of functions.
Due to recent kernel changes, this header must be directly included by
code that calls into the memory allocator.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 arch/powerpc/include/asm/fsldma.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/fsldma.h b/arch/powerpc/include/asm/fsldma.h
index a67aeed..debc5ed 100644
--- a/arch/powerpc/include/asm/fsldma.h
+++ b/arch/powerpc/include/asm/fsldma.h
@@ -11,6 +11,7 @@
 #ifndef __ARCH_POWERPC_ASM_FSLDMA_H__
 #define __ARCH_POWERPC_ASM_FSLDMA_H__
 
+#include <linux/slab.h>
 #include <linux/dmaengine.h>
 
 /*
-- 
1.7.1

^ permalink raw reply related

* [PATCH RFCv2 0/5] CARMA Board Support
From: Ira W. Snyder @ 2010-09-08 16:41 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: linux-kernel, Ira W. Snyder

Hello everyone,

This is the second posting of these drivers, taking into account comments
from the RFCv1 post. Thanks to all that contributed.

The changes:
- change comments to kerneldoc format
- Kconfig improvements
- use the videobuf_dma_sg API in the programmer
- updates for Freescale DMAEngine DMA_SLAVE API changes

In a seperate thread, AKPM asked for some changes to the Freescale
DMAEngine driver. They are already part of -mm, and do not need review. To
make testing easier, I have included the changes as the first two patches
in this series. If you are using -mm, please ignore them.

Information about the CARMA board:

The CARMA board is essentially an MPC8349EA MDS reference design with a
1GHz ADC and 4 high powered data processing FPGAs connected to the local
bus. It is all packed into a compact PCI form factor. It is used at the
Owens Valley Radio Observatory as the main component in the correlator
system.

For more information, see this webpage, maintained by the board's hardware
engineer: http://www.mmarray.org/~dwh/carma_board/index.html

These drivers are the necessary pieces to get the data processing FPGAs
working and producing data. Despite the fact that the hardware is custom
and we are the only users, I'd still like to get the drivers upstream.
Several people have suggested that this is possible.

Some further patches will be forthcoming. I have a driver for the LED
subsystem and the PPS subsystem. The LED register layout is expected to
change soon, so I won't post the driver until that is finished. The PPS
driver will be posted seperately from this patch series; it is very
generic.

Thanks for any review and comments you can offer!
Ira

Ira W. Snyder (5):
  fsldma: fix missing header include
  fsldma: move DMA_SLAVE support functions to the driver
  fpga: add basic CARMA board support
  fpga: add CARMA DATA-FPGA Access Driver
  fpga: add CARMA DATA-FPGA Programmer support

 arch/powerpc/include/asm/fsldma.h       |   71 +--
 drivers/Kconfig                         |    2 +
 drivers/Makefile                        |    1 +
 drivers/dma/fsldma.c                    |   77 ++
 drivers/fpga/Kconfig                    |    1 +
 drivers/fpga/Makefile                   |    1 +
 drivers/fpga/carma/Kconfig              |   38 +
 drivers/fpga/carma/Makefile             |    3 +
 drivers/fpga/carma/carma-fpga-program.c | 1023 +++++++++++++++++++++
 drivers/fpga/carma/carma-fpga.c         | 1471 +++++++++++++++++++++++++++++++
 drivers/fpga/carma/carma.c              |   73 ++
 drivers/fpga/carma/carma.h              |   22 +
 12 files changed, 2719 insertions(+), 64 deletions(-)
 create mode 100644 drivers/fpga/Kconfig
 create mode 100644 drivers/fpga/Makefile
 create mode 100644 drivers/fpga/carma/Kconfig
 create mode 100644 drivers/fpga/carma/Makefile
 create mode 100644 drivers/fpga/carma/carma-fpga-program.c
 create mode 100644 drivers/fpga/carma/carma-fpga.c
 create mode 100644 drivers/fpga/carma/carma.c
 create mode 100644 drivers/fpga/carma/carma.h

^ permalink raw reply

* Re: pci_request_regions() failure
From: Ravi Gupta @ 2010-09-08  9:29 UTC (permalink / raw)
  To: tiejun.chen; +Cc: linuxppc-dev
In-Reply-To: <4C8604E1.4080908@windriver.com>

[-- Attachment #1: Type: text/plain, Size: 18787 bytes --]

Hi Tiejun,

Thanks for the reply.


Your PCI device should be one virtual device so I think the above should be
> as
> we understood. You know 0x00000000 ~ 0x00003ffff should not be allowed to
> reserved.
>

Can you explain a little more that what do you mean by "Your PCI device
should be one virtual device"?


> I think you should do the following sequence in the probe function of your
> PCI
> driver.
>
> 1. pci_enable_device(pdev);
> 2. pci_request_regions(pdev, DRV_NAME);
> 3. pci_set_master(pdev);
> ......
>
> Okay, I have  changed my drive code to follow this sequence, but still no
success. It fails with the same errors as before.

# insmod ./pci_skel.ko
PCI driver: Init function
PCI driver: Probe function
pci_skel 0001:02:00.0: device not available (can't reserve [mem
0x00000000-0x0003ffff])
Unable to Enable PCI device:-22
pci_skel: probe of 0001:02:00.0 failed with error -22

Looks we need some pci_fixup to modify them. Firstly I think you'd better
> 'zero'
> all BARs of your PCI device on the function, pci_scan_device, on the file,
> drivers/pci/probe.c. On there you can dedicate that once your device is
> probed.
> Please check the each BAR's value again after the above fix.
>
>
Okay, I have set the BARs with all zeros in the pci_scan_device() function.
Below is the diff of the changes done by me.

--- /data/sources/linux-2.6.35/drivers/pci/probe.c  2010-08-02
03:41:14.000000000 +0530
+++ probe.c 2010-09-08 14:45:40.000000000 +0530
@@ -1172,6 +1172,45 @@ static struct pci_dev *pci_scan_device(s
    }
  }

+ printk(KERN_WARNING "pci : vendor id = 0x%x\n", l & 0xffff);
+ if ((l & 0xffff) == 0x1204) {
+   /* zero's all BAR registers */
+   printk(KERN_WARNING "pci %04x:%02x:%02x.%d: trying to set all zeros in "
+       "BARs\n", pci_domain_nr(bus),
+       bus->number, PCI_SLOT(devfn),
+       PCI_FUNC(devfn));
+
+   if(pci_bus_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_0, 0x0) ||
+       pci_bus_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_1, 0x0) ||
+       pci_bus_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_2, 0x0) ||
+       pci_bus_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_3, 0x0) ||
+       pci_bus_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_4, 0x0) ||
+       pci_bus_write_config_dword(bus, devfn, PCI_BASE_ADDRESS_5, 0x0)) {
+
+     printk(KERN_WARNING "pci %04x:%02x:%02x.%d: failed to reset bits"
+         "of BARs\n", pci_domain_nr(bus),
+         bus->number, PCI_SLOT(devfn),
+         PCI_FUNC(devfn));
+     return NULL;
+   }
+ }
+
  dev = alloc_pci_dev();
  if (!dev)
    return NULL;

The difference  I have seen in the dmesg is that the following two messages
are not coming now.

PCI: Cannot allocate resource region 0 of device 0001:02:00.0, will remap
PCI: Cannot allocate resource region 1 of device 0001:02:00.0, will remap

But my driver is still fails with the same error as before. I am attaching
the new dmesg log.

Dmesg with all BARs set to zero
================================================================
Using MPC837x RDB/WLAN machine description
Initializing cgroup subsys cpuset
Initializing cgroup subsys cpu
Linux version 2.6.35 (okapi@okapi) (gcc version 4.2.3 (Sourcery G++ Lite
4.2-171)) #28 Wed Sep 8 13:20:27 IST 2010
Found initrd at 0xcf46c000:0xcf7b05b7
Found legacy serial port 0 for /immr@e0000000/serial@4500
  mem=e0004500, taddr=e0004500, irq=0, clk=400000002, speed=0
Found legacy serial port 1 for /immr@e0000000/serial@4600
  mem=e0004600, taddr=e0004600, irq=0, clk=400000002, speed=0
bootconsole [udbg0] enabled
Found FSL PCI host bridge at 0x00000000e0008500. Firmware bus number: 0->0
PCI host bridge /pci@e0008500 (primary) ranges:
 MEM 0x0000000090000000..0x000000009fffffff -> 0x0000000090000000
 MEM 0x0000000080000000..0x000000008fffffff -> 0x0000000080000000 Prefetch
  IO 0x00000000e0300000..0x00000000e03fffff -> 0x0000000000000000
No pci config register base in dev tree, using default
Found FSL PCI host bridge at 0x00000000e0009000. Firmware bus number: 0->255
PCI host bridge /pcie@e0009000  ranges:
 MEM 0x00000000a8000000..0x00000000b7ffffff -> 0x00000000a8000000
  IO 0x00000000b8000000..0x00000000b87fffff -> 0x0000000000000000
No pci config register base in dev tree, using default
Found FSL PCI host bridge at 0x00000000e000a000. Firmware bus number: 0->255
PCI host bridge /pcie@e000a000  ranges:
 MEM 0x00000000c8000000..0x00000000d7ffffff -> 0x00000000c8000000
  IO 0x00000000d8000000..0x00000000d87fffff -> 0x0000000000000000
Top of RAM: 0x10000000, Total RAM: 0x10000000
Memory hole size: 0MB
Zone PFN ranges:
  DMA      0x00000000 -> 0x00010000
  Normal   empty
  HighMem  empty
Movable zone start PFN for each node
early_node_map[1] active PFN ranges
    0: 0x00000000 -> 0x00010000
On node 0 totalpages: 65536
free_area_init_node: node 0, pgdat c04265e8, node_mem_map c0800000
  DMA zone: 512 pages used for memmap
  DMA zone: 0 pages reserved
  DMA zone: 65024 pages, LIFO batch:15
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 65024
Kernel command line: root=/dev/ram ramdisk_size=120000 rw ip=10.20.50.230:10
.20.50.70:10.20.50.50:255.255.0.0:PowerQUICC:eth0:off console=ttyS0,115200
mtdparts=nand:4m(kernel),-(jffs2)
PID hash table entries: 1024 (order: 0, 4096 bytes)
Dentry cache hash table entries: 32768 (order: 5, 131072 bytes)
Inode-cache hash table entries: 16384 (order: 4, 65536 bytes)
High memory: 0k
Memory: 249972k/262144k available (4064k kernel code, 12172k reserved, 244k
data, 2187k bss, 188k init)
Kernel virtual memory layout:
  * 0xfffcf000..0xfffff000  : fixmap
  * 0xff800000..0xffc00000  : highmem PTEs
  * 0xfe6f7000..0xff800000  : early ioremap
  * 0xd1000000..0xfe6f7000  : vmalloc & ioremap
Hierarchical RCU implementation.
    RCU-based detection of stalled CPUs is disabled.
    Verbose stalled-CPUs detection is disabled.
NR_IRQS:512
IPIC (128 IRQ sources) at d1000700
time_init: decrementer frequency = 100.000000 MHz
time_init: processor frequency   = 800.000004 MHz
clocksource: timebase mult[2800000] shift[22] registered
clockevent: decrementer mult[19999999] shift[32] cpu[0]
Console: colour dummy device 80x25
pid_max: default: 32768 minimum: 301
Security Framework initialized
SELinux:  Disabled at boot.
Mount-cache hash table entries: 512
Initializing cgroup subsys ns
Initializing cgroup subsys cpuacct
Initializing cgroup subsys devices
NET: Registered protocol family 16
irq: irq 38 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 38
__irq_set_trigger: setting type, irq = 38, flags = 8
ipic_set_irq_type function, with virq = 38, flow = 8
irq: irq 74 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 74
__irq_set_trigger: setting type, irq = 74, flags = 8
ipic_set_irq_type function, with virq = 74, flow = 8
irq: irq 75 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 75
__irq_set_trigger: setting type, irq = 75, flags = 8
ipic_set_irq_type function, with virq = 75, flow = 8
PCI: Probing PCI hardware
PCI: Scanning PHB /pci@e0008500
PCI: PHB IO resource    = 0000000000000000-00000000000fffff [100]
PCI: PHB MEM resource 0 = 0000000090000000-000000009fffffff [200]
PCI: PHB MEM resource 1 = 0000000080000000-000000008fffffff [2200]
PCI: PHB MEM offset     = 0000000000000000
PCI: PHB IO  offset     = 00000000
    probe mode: 0
pci_bus 0000:00: scanning bus
pci : vendor id = 0x1957
pci 0000:00:00.0: found [1957:00c6] class 000b20 header type 00
pci 0000:00:00.0: reg 10: [mem 0x00000000-0x000fffff]
pci 0000:00:00.0: reg 18: [mem 0x00000000-0x0fffffff 64bit pref]
pci 0000:00:00.0: calling fixup_hide_host_resource_fsl+0x0/0x58
pci 0000:00:00.0: calling pcibios_fixup_resources+0x0/0x180
pci 0000:00:00.0: calling quirk_fsl_pcie_header+0x0/0x48
pci 0000:00:00.0: calling quirk_resource_alignment+0x0/0x1c0
pci 0000:00:00.0: supports D1 D2
pci 0000:00:00.0: PME# supported from D0 D1 D2 D3hot
pci 0000:00:00.0: PME# disabled
pci_bus 0000:00: fixups for bus
PCI: Fixup bus devices 0 (PHB)
PCI: Try to map irq for 0000:00:00.0...
pci_bus 0000:00: bus scan returning with max=00
PCI: Scanning PHB /pcie@e0009000
PCI: PHB IO resource    = 00000000ff7fe000-00000000ffffdfff [100]
PCI: PHB MEM resource 0 = 00000000a8000000-00000000b7ffffff [200]
PCI: PHB MEM offset     = 0000000000000000
PCI: PHB IO  offset     = ff7fe000
    probe mode: 0
pci_bus 0001:01: scanning bus
pci : vendor id = 0x1957
pci 0001:01:00.0: found [1957:00c6] class 000b20 header type 01
pci 0001:01:00.0: ignoring class b20 (doesn't match header type 01)
pci 0001:01:00.0: calling fixup_hide_host_resource_fsl+0x0/0x58
pci 0001:01:00.0: calling pcibios_fixup_resources+0x0/0x180
pci 0001:01:00.0: calling quirk_fsl_pcie_header+0x0/0x48
pci 0001:01:00.0: calling quirk_resource_alignment+0x0/0x1c0
pci 0001:01:00.0: supports D1 D2
pci 0001:01:00.0: PME# supported from D0 D1 D2 D3hot
pci 0001:01:00.0: PME# disabled
pci_bus 0001:01: fixups for bus
PCI: Fixup bus devices 1 (PHB)
PCI: Try to map irq for 0001:01:00.0...
pci 0001:01:00.0: scanning [bus 01-ff] behind bridge, pass 0
pci 0001:01:00.0: bus configuration invalid, reconfiguring
pci 0001:01:00.0: scanning [bus 00-00] behind bridge, pass 1
pci_bus 0001:02: scanning bus
pci : vendor id = 0x1204
pci 0001:02:00.0: trying to set all zeros in BARs
pci 0001:02:00.0: found [1204:e250] class 000000 header type 00
pci 0001:02:00.0: reg 10: [mem 0x00000000-0x0003ffff]
pci 0001:02:00.0: reg 14: [mem 0x00000000-0x0003ffff]
pci 0001:02:00.0: calling pcibios_fixup_resources+0x0/0x180
PCI:0001:02:00.0 Resource 0 0000000000000000-000000000003ffff [40200] is
unassigned
PCI:0001:02:00.0 Resource 1 0000000000000000-000000000003ffff [40200] is
unassigned
pci 0001:02:00.0: calling quirk_resource_alignment+0x0/0x1c0
pci_bus 0001:02: fixups for bus
pci 0001:01:00.0: PCI bridge to [bus 02-ff]
pci 0001:01:00.0:   bridge window [io  0x0000-0x0000] (disabled)
pci 0001:01:00.0:   bridge window [mem 0x00000000-0x000fffff] (disabled)
pci 0001:01:00.0:   bridge window [mem 0x00000000-0x000fffff pref]
(disabled)
PCI: Fixup bus devices 2 (0001:01:00.0)
PCI: Try to map irq for 0001:02:00.0...
 Got one, spec 2 cells (0x00000001 0x00000008...) on /immr@e0000000
/interrupt-controller@700
irq: irq 1 on host /immr@e0000000/interrupt-controller@700 mapped to virtual
irq 16
__irq_set_trigger: setting type, irq = 16, flags = 8
ipic_set_irq_type function, with virq = 16, flow = 8
 Mapped to linux irq 16
pci_bus 0001:02: bus scan returning with max=02
pci_bus 0001:01: bus scan returning with max=02
PCI: Scanning PHB /pcie@e000a000
PCI: PHB IO resource    = 00000000feffc000-00000000ff7fbfff [100]
PCI: PHB MEM resource 0 = 00000000c8000000-00000000d7ffffff [200]
PCI: PHB MEM offset     = 0000000000000000
PCI: PHB IO  offset     = feffc000
    probe mode: 0
pci_bus 0002:03: scanning bus
pci_bus 0002:03: fixups for bus
PCI: Fixup bus devices 3 (PHB)
pci_bus 0002:03: bus scan returning with max=03
PCI->OF bus map:
0 -> 0
1 -> 0
3 -> 0
PCI: Allocating bus resources for 0000:00...
PCI: PHB (bus 0) bridge rsrc 0: 0000000000000000-00000000000fffff [0x100],
parent c03fe5a0 (PCI IO)
PCI: PHB (bus 0) bridge rsrc 1: 0000000090000000-000000009fffffff [0x200],
parent c03fe584 (PCI mem)
PCI: PHB (bus 0) bridge rsrc 2: 0000000080000000-000000008fffffff [0x2200],
parent c03fe584 (PCI mem)
PCI: Allocating bus resources for 0001:01...
PCI: PHB (bus 1) bridge rsrc 0: 00000000ff7fe000-00000000ffffdfff [0x100],
parent c03fe5a0 (PCI IO)
PCI: PHB (bus 1) bridge rsrc 1: 00000000a8000000-00000000b7ffffff [0x200],
parent c03fe584 (PCI mem)
PCI: Allocating bus resources for 0001:02...
PCI: Allocating bus resources for 0002:03...
PCI: PHB (bus 3) bridge rsrc 0: 00000000feffc000-00000000ff7fbfff [0x100],
parent c03fe5a0 (PCI IO)
PCI: PHB (bus 3) bridge rsrc 1: 00000000c8000000-00000000d7ffffff [0x200],
parent c03fe584 (PCI mem)
Reserving legacy ranges for domain 0000
Candidate legacy IO: [io  0x0000-0x0fff]
hose mem offset: 0000000000000000
hose mem res: [mem 0x90000000-0x9fffffff]
hose mem res: [mem 0x80000000-0x8fffffff pref]
Reserving legacy ranges for domain 0001
Candidate legacy IO: [io  0xff7fe000-0xff7fefff]
hose mem offset: 0000000000000000
hose mem res: [mem 0xa8000000-0xb7ffffff]
Reserving legacy ranges for domain 0002
Candidate legacy IO: [io  0xfeffc000-0xfeffcfff]
hose mem offset: 0000000000000000
hose mem res: [mem 0xc8000000-0xd7ffffff]
PCI: Assigning unassigned resources...
pci 0001:01:00.0: BAR 8: assigned [mem 0xa8000000-0xa80fffff]
pci 0001:01:00.0: PCI bridge to [bus 02-02]
pci 0001:01:00.0:   bridge window [io  disabled]
pci 0001:01:00.0:   bridge window [mem 0xa8000000-0xa80fffff]
pci 0001:01:00.0:   bridge window [mem pref disabled]
pci_bus 0000:00: resource 0 [io  0x0000-0xfffff]
pci_bus 0000:00: resource 1 [mem 0x90000000-0x9fffffff]
pci_bus 0000:00: resource 2 [mem 0x80000000-0x8fffffff pref]
pci_bus 0001:01: resource 0 [io  0xff7fe000-0xffffdfff]
pci_bus 0001:01: resource 1 [mem 0xa8000000-0xb7ffffff]
pci_bus 0001:02: resource 1 [mem 0xa8000000-0xa80fffff]
pci_bus 0002:03: resource 0 [io  0xfeffc000-0xff7fbfff]
pci_bus 0002:03: resource 1 [mem 0xc8000000-0xd7ffffff]
Registering qe_ic with sysfs...
Registering ipic with sysfs...
bio: create slab <bio-0> at 0
vgaarb: loaded
SCSI subsystem initialized
Switching to clocksource timebase
NET: Registered protocol family 2
IP route cache hash table entries: 2048 (order: 1, 8192 bytes)
TCP established hash table entries: 8192 (order: 4, 65536 bytes)
TCP bind hash table entries: 8192 (order: 3, 32768 bytes)
TCP: Hash tables configured (established 8192 bind 8192)
TCP reno registered
UDP hash table entries: 256 (order: 0, 4096 bytes)
UDP-Lite hash table entries: 256 (order: 0, 4096 bytes)
NET: Registered protocol family 1
pci 0000:00:00.0: calling quirk_cardbus_legacy+0x0/0x44
pci 0000:00:00.0: calling quirk_usb_early_handoff+0x0/0x740
pci 0001:01:00.0: calling quirk_cardbus_legacy+0x0/0x44
pci 0001:01:00.0: calling quirk_usb_early_handoff+0x0/0x740
pci 0001:02:00.0: calling quirk_cardbus_legacy+0x0/0x44
pci 0001:02:00.0: calling quirk_usb_early_handoff+0x0/0x740
PCI: CLS 32 bytes, default 32
Trying to unpack rootfs image as initramfs...
rootfs image is not initramfs (no cpio magic); looks like an initrd
Freeing initrd memory: 3345k freed
irq: irq 9 on host /immr@e0000000/interrupt-controller@700 mapped to virtual
irq 17
__irq_set_trigger: setting type, irq = 17, flags = 8
ipic_set_irq_type function, with virq = 17, flow = 8
irq: irq 10 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 18
__irq_set_trigger: setting type, irq = 18, flags = 8
ipic_set_irq_type function, with virq = 18, flow = 8
irq: irq 80 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 80
__irq_set_trigger: setting type, irq = 80, flags = 8
ipic_set_irq_type function, with virq = 80, flow = 8
audit: initializing netlink socket (disabled)
type=2000 audit(0.212:1): initialized
JFFS2 version 2.2. (NAND) © 2001-2006 Red Hat, Inc.
SGI XFS with security attributes, large block/inode numbers, no debug
enabled
msgmni has been set to 494
alg: No test for cipher_null (cipher_null-generic)
alg: No test for ecb(cipher_null) (ecb-cipher_null)
alg: No test for digest_null (digest_null-generic)
alg: No test for compress_null (compress_null-generic)
alg: No test for stdrng (krng)
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 253)
io scheduler noop registered
io scheduler deadline registered
io scheduler cfq registered (default)
Serial: 8250/16550 driver, 4 ports, IRQ sharing enabled
serial8250.0: ttyS0 at MMIO 0xe0004500 (irq = 17) is a 16550A
console [ttyS0] enabled, bootconsole disabled
serial8250.0: ttyS1 at MMIO 0xe0004600 (irq = 18) is a 16550A
brd: module loaded
of_mpc8xxx_spi_probe function called.
irq: irq 16 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 19
__irq_set_trigger: setting type, irq = 19, flags = 8
ipic_set_irq_type function, with virq = 19, flow = 8
mpc8xxx_spi_probe function called.
mpc8xxx_spi e0007000.spi: at 0xd1078000 (irq = 19), CPU mode
irq: irq 32 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 32
__irq_set_trigger: setting type, irq = 32, flags = 8
ipic_set_irq_type function, with virq = 32, flow = 8
irq: irq 33 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 33
__irq_set_trigger: setting type, irq = 33, flags = 8
ipic_set_irq_type function, with virq = 33, flow = 8
irq: irq 34 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 34
__irq_set_trigger: setting type, irq = 34, flags = 8
ipic_set_irq_type function, with virq = 34, flow = 8
eth0: Gianfar Ethernet Controller Version 1.2, 04:00:00:00:00:0a
eth0: Running with NAPI enabled
eth0: RX BD ring size for Q[0]: 256
eth0: TX BD ring size for Q[0]: 256
irq: irq 35 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 35
__irq_set_trigger: setting type, irq = 35, flags = 8
ipic_set_irq_type function, with virq = 35, flow = 8
irq: irq 36 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 36
__irq_set_trigger: setting type, irq = 36, flags = 8
ipic_set_irq_type function, with virq = 36, flow = 8
irq: irq 37 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 37
__irq_set_trigger: setting type, irq = 37, flags = 8
ipic_set_irq_type function, with virq = 37, flow = 8
eth1: Gianfar Ethernet Controller Version 1.2, 00:00:00:00:00:00
eth1: Running with NAPI enabled
eth1: RX BD ring size for Q[0]: 256
eth1: TX BD ring size for Q[0]: 256
ucc_geth: QE UCC Gigabit Ethernet Controller
Freescale PowerQUICC MII Bus: probed
irq: irq 17 on host /immr@e0000000/interrupt-controller@700 mapped to
virtual irq 20
__irq_set_trigger: setting type, irq = 20, flags = 8
ipic_set_irq_type function, with virq = 20, flow = 8
Freescale PowerQUICC MII Bus: probed
mice: PS/2 mouse device common for all mice
Skipping unavailable LED gpio -19 (pwr)
Skipping unavailable LED gpio -19 (hdd)
TCP cubic registered
NET: Registered protocol family 17
registered taskstats version 1
drivers/rtc/hctosys.c: unable to open rtc device (rtc0)
RAMDISK: gzip image found at block 0
VFS: Mounted root (ext2 filesystem) on device 1:0.
Freeing unused kernel memory: 188k init
PHY: mdio@e0024520:02 - Link is Up - 10/Half
================================================================

Regards,
Ravi

[-- Attachment #2: Type: text/html, Size: 20382 bytes --]

^ permalink raw reply

* Re: [RFC] arch/powerpc: Remove duplicate/redundant Altivec entries
From: Paul Mackerras @ 2010-09-08  1:59 UTC (permalink / raw)
  To: Matthew McClintock; +Cc: linuxppc-dev
In-Reply-To: <1283885815-11175-1-git-send-email-msm@freescale.com>

On Tue, Sep 07, 2010 at 01:56:55PM -0500, Matthew McClintock wrote:

> In lieu of having multiple similiar lines, we can just have one
> generic cpu-as line for CONFIG_ALTIVEC
> 
> ---
> Was hoping to get comments about this change and if anyone sees any potential
> problems?

I have a memory that we can get some altivec instructions even with
CONFIG_ALTIVEC = n, though presumably they never get executed.  We
would have to check that before applying your patch.

Paul.

^ permalink raw reply

* Re: kexec on ppc64
From: Michael Neuling @ 2010-09-07 23:49 UTC (permalink / raw)
  To: Matthew McClintock; +Cc: linuxppc-dev, kexec
In-Reply-To: <0D324CAF-2A5D-4363-9000-7EB48179C11C@freescale.com>

> I'm trying to determine how kexec'ing works on 64 bit powerpc. When
> allocating a region for the kexec'ed kernel is it ever the same as the
> currently running kernel or do you always boot the kexec'ed kernel
> from a different memory region? I understand that a crash kernel will
> be in a different region, however I was hoping to confirm the behavior
> for a normal "kexec -e".

The kernel will be loaded at a non zero address, but it will copy itself
to zero before it starts running.

Mikey

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox