LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] [POWERPC] bootwrapper: build multiple cuImages
From: Grant Likely @ 2008-02-06 18:18 UTC (permalink / raw)
  To: paulus, jwboyer, linuxppc-dev, scottwood, galak, geoffrey.levand,
	arnd

From: Grant Likely <grant.likely@secretlab.ca>

Currently, the kernel uses CONFIG_DEVICE_TREE to wrap a kernel image
with a fdt blob which means for any given configuration only one dts
file can be selected and so support for only one board can be built

This patch moves the selection of the default .dts file out of the kernel
config and into the bootwrapper makefile.  The makefile chooses which
images to build based on the kernel config and the dts source file
name is taken directly from the image name.  For example "cuImage.ebony"
will use "ebony.dts" as the device tree source file.

In addition, this patch allows a specific image to be requested from the
command line by adding "cuImage.%" and "treeImage.%" targets to the list
of valid built targets in arch/powerpc/Makefile.  This allows the default
dts selection to be overridden.

Another advantage to this change is it allows a single defconfig to be
supplied for all boards using the same chip family and only differing in
the device tree.

Important note: This patch adds two new zImage targets; zImage.dtb.% and
zImage.dtb.initrd.% for zImages with embedded dtb files.  Currently
there are 5 platforms which require this: ps3, ep405, mpc885ads, ep88xc,
adder875-redboot and ep8248e.  This patch *changes the zImage filenames*
for those platforms.  ie. 'zImage.ps3' is now 'zImage.dtb.ps3'.

This new zImage.dtb targets were added so that the .dts file could be
part of the dependancies list for building them.

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

---
v4: removed mpc837x_mds and mpc8313erdb cuimages and changed mpc885ads to
a cuImage

v3: I think this patch is complete.  Renamed zImage.dtb.% targets to
zImage-dtb.% to avoid make choosing the wrong rule if the dts file is missing.

v2: This version fixes some bugs and adds new zImage.dtb and
zImage.initrd.dtb targets.

v1: Please review and comment.  I have not exhaustively tested this patch
and I'm sure to have missed some boards.  However, I think the concept
is sound and will be a good change.
---

 arch/powerpc/Kconfig                   |   19 -----
 arch/powerpc/Makefile                  |    9 +-
 arch/powerpc/boot/Makefile             |  126 ++++++++++++++++++++------------
 arch/powerpc/boot/cuboot-hpc2.c        |   48 ------------
 arch/powerpc/boot/cuboot-mpc7448hpc2.c |   48 ++++++++++++
 arch/powerpc/boot/wrapper              |   23 ++++++
 6 files changed, 151 insertions(+), 122 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 2bf2f3f..4903796 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -415,25 +415,6 @@ config WANT_DEVICE_TREE
 	bool
 	default n
 
-config DEVICE_TREE
-	string "Static device tree source file"
-	depends on WANT_DEVICE_TREE
-	help
-	  This specifies the device tree source (.dts) file to be
-	  compiled and included when building the bootwrapper.  If a
-	  relative filename is given, then it will be relative to
-	  arch/powerpc/boot/dts.  If you are not using the bootwrapper,
-	  or do not need to build a dts into the bootwrapper, this
-	  field is ignored.
-
-	  For example, this is required when building a cuImage target
-	  for an older U-Boot, which cannot pass a device tree itself.
-	  Such a kernel will not work with a newer U-Boot that tries to
-	  pass a device tree (unless you tell it not to).  If your U-Boot
-	  does not mention a device tree in "help bootm", then use the
-	  cuImage target and specify a device tree here.  Otherwise, use
-	  the uImage target and leave this field blank.
-
 endmenu
 
 config ISA_DMA_API
diff --git a/arch/powerpc/Makefile b/arch/powerpc/Makefile
index f70df9b..6845482 100644
--- a/arch/powerpc/Makefile
+++ b/arch/powerpc/Makefile
@@ -151,14 +151,11 @@ core-$(CONFIG_XMON)		+= arch/powerpc/xmon/
 drivers-$(CONFIG_OPROFILE)	+= arch/powerpc/oprofile/
 
 # Default to zImage, override when needed
-defaultimage-y			:= zImage
-defaultimage-$(CONFIG_DEFAULT_UIMAGE) := uImage
-KBUILD_IMAGE := $(defaultimage-y)
-all: $(KBUILD_IMAGE)
+all: zImage
 
 CPPFLAGS_vmlinux.lds	:= -Upowerpc
 
-BOOT_TARGETS = zImage zImage.initrd uImage
+BOOT_TARGETS = zImage zImage.initrd uImage treeImage.% cuImage.%
 
 PHONY += $(BOOT_TARGETS)
 
@@ -180,7 +177,7 @@ define archhelp
 endef
 
 install: vdso_install
-	$(Q)$(MAKE) $(build)=$(boot) BOOTIMAGE=$(KBUILD_IMAGE) install
+	$(Q)$(MAKE) $(build)=$(boot) install
 
 vdso_install:
 ifeq ($(CONFIG_PPC64),y)
diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 122a270..49797a4 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -60,8 +60,9 @@ src-wlib := string.S crt0.S stdio.c main.c \
 src-plat := of.c cuboot-52xx.c cuboot-824x.c cuboot-83xx.c cuboot-85xx.c holly.c \
 		cuboot-ebony.c treeboot-ebony.c prpmc2800.c \
 		ps3-head.S ps3-hvcall.S ps3.c treeboot-bamboo.c cuboot-8xx.c \
-		cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c cuboot-bamboo.c \
-		fixed-head.S ep88xc.c cuboot-hpc2.c ep405.c cuboot-taishan.c \
+		cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c \
+		cuboot-bamboo.c cuboot-mpc7448hpc2.c cuboot-taishan.c \
+		fixed-head.S ep88xc.c ep405.c \
 		cuboot-katmai.c cuboot-rainier.c redboot-8xx.c ep8248e.c \
 		cuboot-warp.c cuboot-85xx-cpm2.c
 src-boot := $(src-wlib) $(src-plat) empty.c
@@ -123,6 +124,8 @@ targets		+= $(patsubst $(obj)/%,%,$(obj-boot) wrapper.a)
 extra-y		:= $(obj)/wrapper.a $(obj-plat) $(obj)/empty.o \
 		   $(obj)/zImage.lds $(obj)/zImage.coff.lds $(obj)/zImage.ps3.lds
 
+dtstree		:= $(srctree)/$(src)/dts
+
 wrapper		:=$(srctree)/$(src)/wrapper
 wrapperbits	:= $(extra-y) $(addprefix $(obj)/,addnote hack-coff mktree dtc) \
 			$(wrapper) FORCE
@@ -181,7 +184,7 @@ quiet_cmd_wrap	= WRAP    $@
 image-$(CONFIG_PPC_PSERIES)		+= zImage.pseries
 image-$(CONFIG_PPC_MAPLE)		+= zImage.pseries
 image-$(CONFIG_PPC_IBM_CELL_BLADE)	+= zImage.pseries
-image-$(CONFIG_PPC_PS3)			+= zImage.ps3
+image-$(CONFIG_PPC_PS3)			+= zImage-dtb.ps3
 image-$(CONFIG_PPC_CELLEB)		+= zImage.pseries
 image-$(CONFIG_PPC_CHRP)		+= zImage.chrp
 image-$(CONFIG_PPC_EFIKA)		+= zImage.chrp
@@ -191,33 +194,69 @@ image-$(CONFIG_PPC_PRPMC2800)		+= zImage.prpmc2800
 image-$(CONFIG_PPC_ISERIES)		+= zImage.iseries
 image-$(CONFIG_DEFAULT_UIMAGE)		+= uImage
 
-ifneq ($(CONFIG_DEVICE_TREE),"")
-image-$(CONFIG_PPC_8xx)			+= cuImage.8xx
-image-$(CONFIG_PPC_EP88XC)		+= zImage.ep88xc
-image-$(CONFIG_EP405)			+= zImage.ep405
-image-$(CONFIG_8260)			+= cuImage.pq2
-image-$(CONFIG_EP8248E)			+= zImage.ep8248e
-image-$(CONFIG_PPC_MPC52xx)		+= cuImage.52xx
-image-$(CONFIG_STORCENTER)		+= cuImage.824x
-image-$(CONFIG_PPC_83xx)		+= cuImage.83xx
-image-$(CONFIG_PPC_85xx)		+= cuImage.85xx
-ifeq ($(CONFIG_CPM2),y)
-image-$(CONFIG_PPC_85xx)		+= cuImage.85xx-cpm2
-endif
-image-$(CONFIG_MPC7448HPC2)		+= cuImage.hpc2
+#
+# Targets which embed a device tree blob
+#
+# Theses are default targets to build images which embed device tree blobs.
+# They are only required on boards which do not have FDT support in firmware.
+# Boards with newish u-boot firmare can use the uImage target above
+#
+
+# Board ports in arch/powerpc/platform/40x/Kconfig
+image-$(CONFIG_EP405)			+= zImage-dtb.ep405
+image-$(CONFIG_WALNUT)			+= treeImage.walnut
+
+# Board ports in arch/powerpc/platform/44x/Kconfig
 image-$(CONFIG_EBONY)			+= treeImage.ebony cuImage.ebony
 image-$(CONFIG_BAMBOO)			+= treeImage.bamboo cuImage.bamboo
 image-$(CONFIG_SEQUOIA)			+= cuImage.sequoia
 image-$(CONFIG_RAINIER)			+= cuImage.rainier
-image-$(CONFIG_WALNUT)			+= treeImage.walnut
 image-$(CONFIG_TAISHAN)			+= cuImage.taishan
 image-$(CONFIG_KATMAI)			+= cuImage.katmai
 image-$(CONFIG_WARP)			+= cuImage.warp
-endif
 
-ifneq ($(CONFIG_REDBOOT),"")
-image-$(CONFIG_PPC_8xx)			+= zImage.redboot-8xx
-endif
+# Board ports in arch/powerpc/platform/8xx/Kconfig
+image-$(CONFIG_PPC_MPC86XADS)		+= cuImage.mpc866ads
+image-$(CONFIG_PPC_MPC885ADS)		+= cuImage.mpc885ads
+image-$(CONFIG_PPC_EP88XC)		+= zImage-dtb.ep88xc
+image-$(CONFIG_PPC_ADDER875)		+= cuImage.adder875-uboot \
+					   zImage-dtb.adder875-redboot
+
+# Board ports in arch/powerpc/platform/52xx/Kconfig
+image-$(CONFIG_PPC_LITE5200)		+= cuImage.lite5200 cuImage.lite5200b
+
+# Board ports in arch/powerpc/platform/82xx/Kconfig
+image-$(CONFIG_MPC8272_ADS)		+= cuImage.mpc8272ads
+image-$(CONFIG_PQ2FADS)			+= cuImage.pq2fads
+image-$(CONFIG_EP8248E)			+= zImage-dtb.ep8248e
+
+# Board ports in arch/powerpc/platform/83xx/Kconfig
+image-$(CONFIG_MPC832x_MDS)		+= cuImage.mpc832x_mds
+image-$(CONFIG_MPC832x_RDB)		+= cuImage.mpc832x_rdb
+image-$(CONFIG_MPC834x_ITX)		+= cuImage.mpc8349emitx \
+					   cuImage.mpc8349emitxgp
+image-$(CONFIG_MPC834x_MDS)		+= cuImage.mpc834x_mds
+image-$(CONFIG_MPC836x_MDS)		+= cuImage.mpc836x_mds
+
+# Board ports in arch/powerpc/platform/85xx/Kconfig
+image-$(CONFIG_MPC8540_ADS)		+= cuImage.mpc8540ads
+image-$(CONFIG_MPC8560_ADS)		+= cuImage.mpc8560ads
+image-$(CONFIG_MPC85xx_CDS)		+= cuImage.mpc8541cds \
+					   cuImage.mpc8548cds \
+					   cuImage.mpc8555cds
+image-$(CONFIG_MPC85xx_MDS)		+= cuImage.mpc8568mds
+image-$(CONFIG_MPC85xx_DS)		+= cuImage.mpc8544ds \
+					   cuImage.mpc8572ds
+image-$(CONFIG_TQM8540)			+= cuImage.tqm8540
+image-$(CONFIG_TQM8541)			+= cuImage.tqm8541
+image-$(CONFIG_TQM8555)			+= cuImage.tqm8555
+image-$(CONFIG_TQM8560)			+= cuImage.tqm8560
+image-$(CONFIG_SBC8548)			+= cuImage.tqm8548
+image-$(CONFIG_SBC8560)			+= cuImage.tqm8560
+
+# Board ports in arch/powerpc/platform/embedded6xx/Kconfig
+image-$(CONFIG_STORCENTER)		+= cuImage.storcenter
+image-$(CONFIG_MPC7448HPC2)		+= cuImage.mpc7448hpc2
 
 # For 32-bit powermacs, build the COFF and miboot images
 # as well as the ELF images.
@@ -233,24 +272,20 @@ targets	+= $(image-y) $(initrd-y)
 
 $(addprefix $(obj)/, $(initrd-y)): $(obj)/ramdisk.image.gz
 
-# If CONFIG_WANT_DEVICE_TREE is set and CONFIG_DEVICE_TREE isn't an
-# empty string, define 'dts' to be path to the dts
-# CONFIG_DEVICE_TREE will have "" around it, make sure to strip them
-ifeq ($(CONFIG_WANT_DEVICE_TREE),y)
-ifneq ($(CONFIG_DEVICE_TREE),"")
-dts = $(if $(shell echo $(CONFIG_DEVICE_TREE) | grep '^/'),\
-	,$(srctree)/$(src)/dts/)$(CONFIG_DEVICE_TREE:"%"=%)
-endif
-endif
-
 # Don't put the ramdisk on the pattern rule; when its missing make will try
 # the pattern rule with less dependencies that also matches (even with the
 # hard dependency listed).
-$(obj)/zImage.initrd.%: vmlinux $(wrapperbits) $(dts)
-	$(call if_changed,wrap,$*,$(dts),,$(obj)/ramdisk.image.gz)
+$(obj)/zImage.initrd.%: vmlinux $(wrapperbits)
+	$(call if_changed,wrap,$*,,,$(obj)/ramdisk.image.gz)
 
-$(obj)/zImage.%: vmlinux $(wrapperbits) $(dts)
-	$(call if_changed,wrap,$*,$(dts))
+$(obj)/zImage.%: vmlinux $(wrapperbits)
+	$(call if_changed,wrap,$*)
+
+$(obj)/zImage-dtb.initrd.%: vmlinux $(wrapperbits) $(dtstree)/%.dts
+	$(call if_changed,wrap,$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz)
+
+$(obj)/zImage-dtb.%: vmlinux $(wrapperbits) $(dtstree)/%.dts
+	$(call if_changed,wrap,$*,$(dtstree)/$*.dts)
 
 # This cannot be in the root of $(src) as the zImage rule always adds a $(obj)
 # prefix
@@ -260,24 +295,17 @@ $(obj)/vmlinux.strip: vmlinux
 $(obj)/zImage.iseries: vmlinux
 	$(STRIP) -s -R .comment $< -o $@
 
-$(obj)/zImage.ps3: vmlinux  $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts
-	$(STRIP) -s -R .comment $< -o vmlinux.strip
-	$(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,)
-
-$(obj)/zImage.initrd.ps3: vmlinux  $(wrapper) $(wrapperbits) $(srctree)/$(src)/dts/ps3.dts $(obj)/ramdisk.image.gz
-	$(call cmd,wrap,ps3,$(srctree)/$(src)/dts/ps3.dts,,$(obj)/ramdisk.image.gz)
-
 $(obj)/uImage: vmlinux $(wrapperbits)
 	$(call if_changed,wrap,uboot)
 
-$(obj)/cuImage.%: vmlinux $(dts) $(wrapperbits)
-	$(call if_changed,wrap,cuboot-$*,$(dts))
+$(obj)/cuImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits)
+	$(call if_changed,wrap,cuboot-$*,$(dtstree)/$*.dts)
 
-$(obj)/treeImage.initrd.%: vmlinux $(dts) $(wrapperbits)
-	$(call if_changed,wrap,treeboot-$*,$(dts),,$(obj)/ramdisk.image.gz)
+$(obj)/treeImage.initrd.%: vmlinux $(dtstree)/%.dts $(wrapperbits)
+	$(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts,,$(obj)/ramdisk.image.gz)
 
-$(obj)/treeImage.%: vmlinux $(dts) $(wrapperbits)
-	$(call if_changed,wrap,treeboot-$*,$(dts))
+$(obj)/treeImage.%: vmlinux $(dtstree)/%.dts $(wrapperbits)
+	$(call if_changed,wrap,treeboot-$*,$(dtstree)/$*.dts)
 
 # If there isn't a platform selected then just strip the vmlinux.
 ifeq (,$(image-y))
diff --git a/arch/powerpc/boot/cuboot-hpc2.c b/arch/powerpc/boot/cuboot-hpc2.c
deleted file mode 100644
index 1b89532..0000000
--- a/arch/powerpc/boot/cuboot-hpc2.c
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
- *
- * Author: Roy Zang <tie-fei.zang@freescale.com>
- *
- * Description:
- * Old U-boot compatibility for mpc7448hpc2 board
- * Based on the code of Scott Wood <scottwood@freescale.com>
- * for 83xx and 85xx.
- *
- * This 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 "ops.h"
-#include "stdio.h"
-#include "cuboot.h"
-
-#define TARGET_HAS_ETH1
-#include "ppcboot.h"
-
-static bd_t bd;
-extern char _dtb_start[], _dtb_end[];
-
-static void platform_fixups(void)
-{
-	void *tsi;
-
-	dt_fixup_memory(bd.bi_memstart, bd.bi_memsize);
-	dt_fixup_mac_addresses(bd.bi_enetaddr, bd.bi_enet1addr);
-	dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 4, bd.bi_busfreq);
-	tsi = find_node_by_devtype(NULL, "tsi-bridge");
-	if (tsi)
-		setprop(tsi, "bus-frequency", &bd.bi_busfreq,
-			sizeof(bd.bi_busfreq));
-}
-
-void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
-		unsigned long r6, unsigned long r7)
-{
-	CUBOOT_INIT();
-	fdt_init(_dtb_start);
-	serial_console_init();
-	platform_ops.fixups = platform_fixups;
-}
diff --git a/arch/powerpc/boot/cuboot-mpc7448hpc2.c b/arch/powerpc/boot/cuboot-mpc7448hpc2.c
new file mode 100644
index 0000000..1b89532
--- /dev/null
+++ b/arch/powerpc/boot/cuboot-mpc7448hpc2.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * Author: Roy Zang <tie-fei.zang@freescale.com>
+ *
+ * Description:
+ * Old U-boot compatibility for mpc7448hpc2 board
+ * Based on the code of Scott Wood <scottwood@freescale.com>
+ * for 83xx and 85xx.
+ *
+ * This 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 "ops.h"
+#include "stdio.h"
+#include "cuboot.h"
+
+#define TARGET_HAS_ETH1
+#include "ppcboot.h"
+
+static bd_t bd;
+extern char _dtb_start[], _dtb_end[];
+
+static void platform_fixups(void)
+{
+	void *tsi;
+
+	dt_fixup_memory(bd.bi_memstart, bd.bi_memsize);
+	dt_fixup_mac_addresses(bd.bi_enetaddr, bd.bi_enet1addr);
+	dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 4, bd.bi_busfreq);
+	tsi = find_node_by_devtype(NULL, "tsi-bridge");
+	if (tsi)
+		setprop(tsi, "bus-frequency", &bd.bi_busfreq,
+			sizeof(bd.bi_busfreq));
+}
+
+void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+		unsigned long r6, unsigned long r7)
+{
+	CUBOOT_INIT();
+	fdt_init(_dtb_start);
+	serial_console_init();
+	platform_ops.fixups = platform_fixups;
+}
diff --git a/arch/powerpc/boot/wrapper b/arch/powerpc/boot/wrapper
index 763a0c4..c317815 100755
--- a/arch/powerpc/boot/wrapper
+++ b/arch/powerpc/boot/wrapper
@@ -158,6 +158,29 @@ miboot|uboot)
 cuboot*)
     binary=y
     gzip=
+    case "$platform" in
+    *-mpc885ads|*-adder875*|*-ep88xc)
+        platformo=$object/cuboot-8xx.o
+        ;;
+    *5200*|*-motionpro)
+        platformo=$object/cuboot-52xx.o
+        ;;
+    *-pq2fads|*-ep8248e|*-mpc8272*|*-storcenter)
+        platformo=$object/cuboot-pq2.o
+        ;;
+    *-mpc824*)
+        platformo=$object/cuboot-824x.o
+        ;;
+    *-mpc83*)
+        platformo=$object/cuboot-83xx.o
+        ;;
+    *-tqm8541|*-mpc8560*|*-tqm8560|*-tqm8555*)
+        platformo=$object/cuboot-85xx-cpm2.o
+        ;;
+    *-mpc85*)
+        platformo=$object/cuboot-85xx.o
+        ;;
+    esac
     ;;
 ps3)
     platformo="$object/ps3-head.o $object/ps3-hvcall.o $object/ps3.o"

^ permalink raw reply related

* RE: [Virtex4] kernel2.6 or distribution
From: Stephen Neuendorffer @ 2008-02-06 19:06 UTC (permalink / raw)
  To: Desroches L., linuxppc-embedded
In-Reply-To: <15291528.post@talk.nabble.com>



> -----Original Message-----
> From: linuxppc-embedded-bounces+stephen=3Dneuendorffer.name@ozlabs.org =
[mailto:linuxppc-embedded-
> bounces+stephen=3Dneuendorffer.name@ozlabs.org] On Behalf Of Desroches =
L.
> Sent: Tuesday, February 05, 2008 7:49 AM
> To: linuxppc-embedded@ozlabs.org
> Subject: [Virtex4] kernel2.6 or distribution
>=20
>=20
> Hi,
>=20
> Sorry for my English, I am a French student. For my internship, I have =
to
> implement Linux on a Virtex4.
>=20
> I know a few things about the Virtex4 because I am specialized in =
digital
> electronic (VHDL). I have to choose a solution for the Linux to =
implement.
> At this stage, I have some problems to determine the best solution =
probably
> because things are not clear for me whereas I have read a book about =
Linux
> embedded and seen a lot of websites.
>=20
> I have seen several distributions (maybe my vocable is false) from
> MontaVista, LynuxWorks and others. I have seen also kernel2.6 for =
Virtex4
> =
(http://git.xilinx.com/cgi-bin/gitweb.cgi?p=3Dlinux-2.6-xlnx.git;a=3Dsumm=
ary). I
> would like to understand the difference between these two things.

The distributions contain kernel code that is heavily tested and stable, =
in addition to a toolchain, debuggers, rootfilesystem, etc.  =
git.xilinx.com is just the kernel code, without anything else.  =
git.xilinx.com also contains new code that is under development, rather =
than primarily stable code.

> If I have understood MontaVista for example offers a solution with a C
> library and drivers, isn't it ? So is it a better solution than =
kernel2.6 ?

'better' for what?
development of new code? yes, most distributions use older kernels.
building a sytem? Maybe... it depends on what features you need.
learning? Maybe... It depends on what you already know and what you want =
to learn.

> I prefer to start with the kernel given by xilinx, it is an internship =
so
> the aim is to learn the more I can but I have to convince my tutor =
that it
> is a better solution than MontaVista, BlueCat or =B5C/OSII. Do you =
have some
> arguments ?
>=20
> Thanks for your help
>=20
> Best Regards
>=20
> ---
>=20
> Desroches Ludovic
> ESIEE engineer : http://www.esiee.fr
>=20
>=20
> --
> View this message in context: =
http://www.nabble.com/-Virtex4--kernel2.6-or-distribution-
> tp15291528p15291528.html
> Sent from the linuxppc-embedded mailing list archive at Nabble.com.
>=20
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded

^ permalink raw reply

* RE: [Virtex 4 PPC] Which Linux?
From: Stephen Neuendorffer @ 2008-02-06 19:29 UTC (permalink / raw)
  To: IngoM, linuxppc-embedded
In-Reply-To: <15268468.post@talk.nabble.com>



> -----Original Message-----
> From: linuxppc-embedded-bounces+stephen=3Dneuendorffer.name@ozlabs.org
[mailto:linuxppc-embedded-
> bounces+stephen=3Dneuendorffer.name@ozlabs.org] On Behalf Of IngoM
> Sent: Monday, February 04, 2008 5:55 AM
> To: linuxppc-embedded@ozlabs.org
> Subject: [Virtex 4 PPC] Which Linux?
>=20
>=20
> Hello,
>=20
> we develop a new laser-ranging-system and our hardware freaks have
choosen
> the Virtex 4 FX 12 on the "AVNET FX12 Mini-Module". The system had to
> deliver the raw data via UDP (12 Mbyte/sec) and on TCP the processed
data
> (about 6 Mbyte/sec). When you get the processed data via TCP then no
data
> send by UDP.
>=20
> I'm confused by the following:
>=20
> 1) Hard-TEMAC vs. Soft-TEMAC.
> Avnet provide a demo for the module which using Soft-TEMAC. If I get
it
> right this core has to be licenced. But when ther is a hard-TEMAC why
pay
> for it?

Generally speaking, if you have the hard temac, you should use it.  The
new EDK cores do this transparently. (see
http://www.xilinx.com/bvdocs/ipcenter/data_sheet/xps_ll_temac.pdf)  Note
that the emaclite license has recently been made free, although this
core doesn't support gigabit rates, which it looks like youll need.

Steve

^ permalink raw reply

* System Clock runaway on Xilinx platform
From: khollan @ 2008-02-06 19:41 UTC (permalink / raw)
  To: linuxppc-embedded


Hi all,

Thanks for the help so far!
I'm now running linux 2.6.21 on my custom virtex 4 board modeled after the
ml410.  The cpu clock is 300MHz and the PLB bus is 175MHz.

My question is which clock is the linux system clock that keeps track of the
date derived from?  I set my date with rdate -s time.mit.edu at boot and
then compare with the date command and rdate -p time.mit.edu and they are
off by 20 or so seconds even just after a few minutes, this trend continues
and it will be off by a day after a few hours.  I think I just don't have
something defined correctly but I can't figure out which.

Thanks

Kevin
-- 
View this message in context: http://www.nabble.com/System-Clock-runaway-on-Xilinx-platform-tp15312437p15312437.html
Sent from the linuxppc-embedded mailing list archive at Nabble.com.

^ permalink raw reply

* Re: System Clock runaway on Xilinx platform
From: Grant Likely @ 2008-02-06 20:29 UTC (permalink / raw)
  To: khollan; +Cc: linuxppc-embedded
In-Reply-To: <15312437.post@talk.nabble.com>

On 2/6/08, khollan <khollan@daktronics.com> wrote:
>
> Hi all,
>
> Thanks for the help so far!
> I'm now running linux 2.6.21 on my custom virtex 4 board modeled after the
> ml410.  The cpu clock is 300MHz and the PLB bus is 175MHz.
>
> My question is which clock is the linux system clock that keeps track of the
> date derived from?  I set my date with rdate -s time.mit.edu at boot and
> then compare with the date command and rdate -p time.mit.edu and they are
> off by 20 or so seconds even just after a few minutes, this trend continues
> and it will be off by a day after a few hours.  I think I just don't have
> something defined correctly but I can't figure out which.

The clock is maintained off of the PowerPC time base register (TBL,
TBU).  On Virtex ppc platforms, the time base frequency is supposed to
be the same as the CPU clock.  Can you figure out *exactly* how
quickly the clock is drifting?

How are you booting your Virtex platform?  u-boot or a zImage.elf?  If
it's a zImage, the CPU clock frequency is set in
arch/ppc/boot/simple/embed_config.c (search for CONFIG_XILINX and look
for the line that sets bd->bi_intfreq).  Make XPAR_CORE_CLOCK_FREQ_HZ
in xparameters matches you CPU clock speed.

Side note: you may notice that bd->bi_tbfreq is also used but not set;
that's because bd->bi_intfreq and bd->tbfreq point to the same memory
location.  (It's kind of messed up that way, don't worry too much
about it)

If you're using u-boot, then u-boot will set the cpu frequency in the
board info structure.

Cheers,
g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* [PATCH 0/4] [POWERPC] Updates to allow cpu dlpar remove to work again
From: Nathan Fontenot @ 2008-02-06 20:37 UTC (permalink / raw)
  To: linuxppc-dev

The following patches make some small corrections to allow cpu dlpar remove to
work again.  Cpu dlpar remove is currently failing because the cpu we are 
removing may still have irq's pinned to it or have it's CPPR reset during
the cpu teardown process.

-Nathan Fontenot

^ permalink raw reply

* [PATCH 1/4][POWERPC] Update irq descriptor affinity
From: Nathan Fontenot @ 2008-02-06 20:37 UTC (permalink / raw)
  To: linuxppc-dev

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

The affinity mask in the virq descriptor needs to be set before we reset the 
affinity for the virq.  Without doing this the call to get the new irq server
fails and we end up leaving the virq pinned to the cpu we are removing.

This does not fail the cpu remove from the kernel, but it does prevent cpu dlpar
remove operations from returning the cpu to the hypervisor.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>

---

[-- Attachment #2: irq_mapping.patch --]
[-- Type: text/x-patch, Size: 578 bytes --]

Index: linux-2.6/arch/powerpc/platforms/pseries/xics.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/xics.c	2008-02-04 14:27:43.000000000 -0600
+++ linux-2.6/arch/powerpc/platforms/pseries/xics.c	2008-02-05 10:40:06.000000000 -0600
@@ -880,8 +880,8 @@
 		       virq, cpu);
 
 		/* Reset affinity to all cpus */
+		irq_desc[virq].affinity = CPU_MASK_ALL;
 		desc->chip->set_affinity(virq, CPU_MASK_ALL);
-		irq_desc[irq].affinity = CPU_MASK_ALL;
 unlock:
 		spin_unlock_irqrestore(&desc->lock, flags);
 	}

^ permalink raw reply

* [PATCH 2/4] [POWERPC] Split xics_teardown_cpu()
From: Nathan Fontenot @ 2008-02-06 20:37 UTC (permalink / raw)
  To: linuxppc-dev

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

This patch splits off the kexec path bits of the xics_teardown_cpu()
routine into its own xics_kexec_teardown_cpu() routine.  With the
previous combined routine the CPPR for a cpu that is being removed may have its 
CPPR reset in the plpar_eoi() call (which explicitly sets the CPPR to a non-zero 
value).  Splitting of the kexec bits of the code prevents this from happening in
the cpu remove path.

Once again, this does not cause the cpu remove from the kernel to fail, but it 
does cause cpu dlpar operations to not be able to return the cpu to the hypervisor.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---


[-- Attachment #2: kexec_teardown_cpu.patch --]
[-- Type: text/x-patch, Size: 2742 bytes --]

Index: linux-2.6/arch/powerpc/platforms/pseries/hotplug-cpu.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/hotplug-cpu.c	2008-02-04 14:27:43.000000000 -0600
+++ linux-2.6/arch/powerpc/platforms/pseries/hotplug-cpu.c	2008-02-04 16:29:27.000000000 -0600
@@ -58,7 +58,7 @@
 {
 	local_irq_disable();
 	idle_task_exit();
-	xics_teardown_cpu(0);
+	xics_teardown_cpu();
 	unregister_slb_shadow(hard_smp_processor_id(), __pa(get_slb_shadow()));
 	rtas_stop_self();
 	/* Should never get here... */
Index: linux-2.6/arch/powerpc/platforms/pseries/kexec.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/kexec.c	2008-02-04 14:27:43.000000000 -0600
+++ linux-2.6/arch/powerpc/platforms/pseries/kexec.c	2008-02-04 16:29:46.000000000 -0600
@@ -54,7 +54,7 @@
 static void pseries_kexec_cpu_down_xics(int crash_shutdown, int secondary)
 {
 	pseries_kexec_cpu_down(crash_shutdown, secondary);
-	xics_teardown_cpu(secondary);
+	xics_kexec_teardown_cpu(secondary);
 }
 
 void __init setup_kexec_cpu_down_xics(void)
Index: linux-2.6/arch/powerpc/platforms/pseries/xics.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/xics.c	2008-02-04 16:25:36.000000000 -0600
+++ linux-2.6/arch/powerpc/platforms/pseries/xics.c	2008-02-04 16:28:50.000000000 -0600
@@ -776,11 +776,9 @@
 }
 #endif /* CONFIG_SMP */
 
-void xics_teardown_cpu(int secondary)
+void xics_teardown_cpu()
 {
 	int cpu = smp_processor_id();
-	unsigned int ipi;
-	struct irq_desc *desc;
 
 	xics_set_cpu_priority(0);
 
@@ -791,9 +789,17 @@
 		lpar_qirr_info(cpu, 0xff);
 	else
 		direct_qirr_info(cpu, 0xff);
+}
+
+void xics_kexec_teardown_cpu(int secondary)
+{
+	unsigned int ipi;
+	struct irq_desc *desc;
+
+	xics_teardown_cpu();
 
 	/*
-	 * we need to EOI the IPI if we got here from kexec down IPI
+	 * we need to EOI the IPI
 	 *
 	 * probably need to check all the other interrupts too
 	 * should we be flagging idle loop instead?
Index: linux-2.6/arch/powerpc/platforms/pseries/xics.h
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/xics.h	2008-02-04 14:27:43.000000000 -0600
+++ linux-2.6/arch/powerpc/platforms/pseries/xics.h	2008-02-04 16:29:08.000000000 -0600
@@ -16,7 +16,8 @@
 
 extern void xics_init_IRQ(void);
 extern void xics_setup_cpu(void);
-extern void xics_teardown_cpu(int secondary);
+extern void xics_teardown_cpu(void);
+extern void xics_kexec_teardown_cpu(int secondary);
 extern void xics_cause_IPI(int cpu);
 extern  void xics_request_IPIs(void);
 extern void xics_migrate_irqs_away(void);

^ permalink raw reply

* [PATCH 3/4][POWERPC] Remove redundant of_get_cpu_node routine
From: Nathan Fontenot @ 2008-02-06 20:37 UTC (permalink / raw)
  To: linuxppc-dev

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

It appears that xics.c has its own of_get_cpu_node().  Remove this and use the 
common one from prom.c.

Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com>
---

[-- Attachment #2: of_get_cpu_node.patch --]
[-- Type: text/x-patch, Size: 1172 bytes --]

Index: linux-2.6/arch/powerpc/platforms/pseries/xics.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/xics.c	2008-02-06 11:31:44.000000000 -0600
+++ linux-2.6/arch/powerpc/platforms/pseries/xics.c	2008-02-06 11:34:34.000000000 -0600
@@ -658,31 +658,6 @@
 	set_irq_chained_handler(cascade, pseries_8259_cascade);
 }
 
-static struct device_node *cpuid_to_of_node(int cpu)
-{
-	struct device_node *np;
-	u32 hcpuid = get_hard_smp_processor_id(cpu);
-
-	for_each_node_by_type(np, "cpu") {
-		int i, len;
-		const u32 *intserv;
-
-		intserv = of_get_property(np, "ibm,ppc-interrupt-server#s",
-					&len);
-
-		if (!intserv)
-			intserv = of_get_property(np, "reg", &len);
-
-		i = len / sizeof(u32);
-
-		while (i--)
-			if (intserv[i] == hcpuid)
-				return np;
-	}
-
-	return NULL;
-}
-
 void __init xics_init_IRQ(void)
 {
 	int i, j;
@@ -711,7 +686,7 @@
 	xics_init_host();
 
 	/* Find the server numbers for the boot cpu. */
-	np = cpuid_to_of_node(boot_cpuid);
+	np = of_get_cpu_node(boot_cpuid, NULL);
 	BUG_ON(!np);
 	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
 	if (!ireg)

^ permalink raw reply

* [PATCH 4/4][POWERPC] Update default irq servers when boot cpu is removed
From: Nathan Fontenot @ 2008-02-06 20:37 UTC (permalink / raw)
  To: linuxppc-dev

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

The xics code does update the default server information when the boot cpu is 
removed.  This patches recognizes when the boot cpu is being removed and updates 
the appropriate information based on the new 'boot cpu'.

Failure to update this information can causes us to leave irqs pinned to cpus 
that are being removed, especially when removing the boot cpu.  The cpu is 
removed from the kernel, but cpu dlpar remove operations fail since we cannot
return the cpu to the hypervisor.

Signed-off-by: Nathan Fonteno <nfont@austin.ibm.com>
---

[-- Attachment #2: gserver_update.patch --]
[-- Type: text/x-patch, Size: 2922 bytes --]

Index: linux-2.6/arch/powerpc/platforms/pseries/xics.c
===================================================================
--- linux-2.6.orig/arch/powerpc/platforms/pseries/xics.c	2008-02-06 11:34:34.000000000 -0600
+++ linux-2.6/arch/powerpc/platforms/pseries/xics.c	2008-02-06 13:28:25.000000000 -0600
@@ -160,6 +160,46 @@
 
 /* High level handlers and init code */
 
+static void xics_update_irq_servers(void)
+{
+	int i, j;
+	struct device_node *np;
+	u32 ilen;
+	const u32 *ireg, *isize;
+	u32 hcpuid;
+
+	/* Find the server numbers for the boot cpu. */
+	np = of_get_cpu_node(boot_cpuid, NULL);
+	BUG_ON(!np);
+
+	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
+	if (!ireg) {
+		of_node_put(np);
+		return;
+	}
+
+	i = ilen / sizeof(int);
+	hcpuid = get_hard_smp_processor_id(boot_cpuid);
+
+	/* Global interrupt distribution server is specified in the last
+	 * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
+	 * entry fom this property for current boot cpu id and use it as
+	 * default distribution server
+	 */
+	for (j = 0; j < i; j += 2) {
+		if (ireg[j] == hcpuid) {
+			default_server = hcpuid;
+			default_distrib_server = ireg[j+1];
+
+			isize = of_get_property(np,
+					"ibm,interrupt-server#-size", NULL);
+			if (isize)
+				interrupt_server_size = *isize;
+		}
+	}
+
+	of_node_put(np);
+}
 
 #ifdef CONFIG_SMP
 static int get_irq_server(unsigned int virq, unsigned int strict_check)
@@ -169,6 +209,9 @@
 	cpumask_t cpumask = irq_desc[virq].affinity;
 	cpumask_t tmp = CPU_MASK_NONE;
 
+	if (! cpu_isset(default_server, cpu_online_map))
+		xics_update_irq_servers();
+
 	if (!distribute_irqs)
 		return default_server;
 
@@ -660,12 +703,9 @@
 
 void __init xics_init_IRQ(void)
 {
-	int i, j;
 	struct device_node *np;
-	u32 ilen, indx = 0;
-	const u32 *ireg, *isize;
+	u32 indx = 0;
 	int found = 0;
-	u32 hcpuid;
 
 	ppc64_boot_msg(0x20, "XICS Init");
 
@@ -684,34 +724,7 @@
 		return;
 
 	xics_init_host();
-
-	/* Find the server numbers for the boot cpu. */
-	np = of_get_cpu_node(boot_cpuid, NULL);
-	BUG_ON(!np);
-	ireg = of_get_property(np, "ibm,ppc-interrupt-gserver#s", &ilen);
-	if (!ireg)
-		goto skip_gserver_check;
-	i = ilen / sizeof(int);
-	hcpuid = get_hard_smp_processor_id(boot_cpuid);
-
-	/* Global interrupt distribution server is specified in the last
-	 * entry of "ibm,ppc-interrupt-gserver#s" property. Get the last
-	 * entry fom this property for current boot cpu id and use it as
-	 * default distribution server
-	 */
-	for (j = 0; j < i; j += 2) {
-		if (ireg[j] == hcpuid) {
-			default_server = hcpuid;
-			default_distrib_server = ireg[j+1];
-
-			isize = of_get_property(np,
-					"ibm,interrupt-server#-size", NULL);
-			if (isize)
-				interrupt_server_size = *isize;
-		}
-	}
-skip_gserver_check:
-	of_node_put(np);
+	xics_update_irq_servers();
 
 	if (firmware_has_feature(FW_FEATURE_LPAR))
 		ppc_md.get_irq = xics_get_irq_lpar;

^ permalink raw reply

* Fwd: Please pull linux-2.6-mpc52xx.git
From: Grant Likely @ 2008-02-06 21:34 UTC (permalink / raw)
  To: Paul Mackerras, Rigby John-R61273, linuxppc-dev
In-Reply-To: <fa686aa40802061333kd6567bay92c530bb08a2aac8@mail.gmail.com>

(resend; including the mailing list this time)

Paul,

Please pull my mpc52xx tree.  It contains the MPC512x work which John
Rigby has been doing.  I should have got these out to you sooner, but
I forgot about them.  :-(

Cheers,
g.

The following changes since commit 99e139126ab2e84be67969650f92eb37c12ab5cd:
  Michael Ellerman (1):
        [POWERPC] Cell IOMMU fixed mapping support

are available in the git repository at:

  git://git.secretlab.ca/git/linux-2.6-mpc52xx.git for-2.6.25

John Rigby (4):
      [POWERPC] mpc512x: Basic platform support
      [POWERPC] mpc512x: Device tree for MPC5121 ADS
      [POWERPC] mpc512x: Factor out 5200 dependencies from 52xx psc driver
      [POWERPC] mpc512x: Add MPC512x PSC support to MPC52xx psc driver

 arch/powerpc/Kconfig                      |    2 +-
 arch/powerpc/boot/dts/mpc5121ads.dts      |  122 ++++++++
 arch/powerpc/platforms/512x/Kconfig       |   20 ++
 arch/powerpc/platforms/512x/Makefile      |    4 +
 arch/powerpc/platforms/512x/mpc5121_ads.c |  104 +++++++
 arch/powerpc/platforms/Kconfig            |    1 +
 arch/powerpc/platforms/Kconfig.cputype    |    6 +-
 arch/powerpc/platforms/Makefile           |    1 +
 drivers/serial/Kconfig                    |   12 +-
 drivers/serial/mpc52xx_uart.c             |  431 ++++++++++++++++++++++++-----
 include/asm-powerpc/mpc512x.h             |   22 ++
 include/asm-powerpc/mpc52xx_psc.h         |   48 ++++
 12 files changed, 694 insertions(+), 79 deletions(-)
 create mode 100644 arch/powerpc/boot/dts/mpc5121ads.dts
 create mode 100644 arch/powerpc/platforms/512x/Kconfig
 create mode 100644 arch/powerpc/platforms/512x/Makefile
 create mode 100644 arch/powerpc/platforms/512x/mpc5121_ads.c
 create mode 100644 include/asm-powerpc/mpc512x.h


--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.


-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* [PATCH] Freescale enhanced Local Bus Controller FCM NAND support.
From: Scott Wood @ 2008-02-06 21:36 UTC (permalink / raw)
  To: dwmw2; +Cc: linuxppc-dev, linux-mtd

Signed-off-by: Nick Spence <nick.spence@freescale.com>
Signed-off-by: Scott Wood <scottwood@freescale.com>
---
 drivers/mtd/nand/Kconfig         |    9 +
 drivers/mtd/nand/Makefile        |    1 +
 drivers/mtd/nand/fsl_elbc_nand.c | 1244 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1254 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/fsl_elbc_nand.c

diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 0a840d5..4a3c675 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -321,4 +321,13 @@ config MTD_NAND_ORION
 	  No board specific support is done by this driver, each board
 	  must advertise a platform_device for the driver to attach.
 
+config MTD_NAND_FSL_ELBC
+	tristate "NAND support for Freescale eLBC controllers"
+	depends on MTD_NAND && PPC_OF
+	help
+	  Various Freescale chips, including the 8313, include a NAND Flash
+	  Controller Module with built-in hardware ECC capabilities.
+	  Enabling this option will enable you to use this to control
+	  external NAND devices.
+
 endif # MTD_NAND
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index e35f5ea..80d575e 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -31,5 +31,6 @@ obj-$(CONFIG_MTD_NAND_PLATFORM)		+= plat_nand.o
 obj-$(CONFIG_MTD_ALAUDA)		+= alauda.o
 obj-$(CONFIG_MTD_NAND_PASEMI)		+= pasemi_nand.o
 obj-$(CONFIG_MTD_NAND_ORION)		+= orion_nand.o
+obj-$(CONFIG_MTD_NAND_FSL_ELBC)		+= fsl_elbc_nand.o
 
 nand-objs := nand_base.o nand_bbt.o
diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c
new file mode 100644
index 0000000..b025dfe
--- /dev/null
+++ b/drivers/mtd/nand/fsl_elbc_nand.c
@@ -0,0 +1,1244 @@
+/* Freescale Enhanced Local Bus Controller NAND driver
+ *
+ * Copyright (c) 2006-2007 Freescale Semiconductor
+ *
+ * Authors: Nick Spence <nick.spence@freescale.com>,
+ *          Scott Wood <scottwood@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/of_platform.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+
+#define MAX_BANKS 8
+#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
+#define FCM_TIMEOUT_MSECS 500 /* Maximum number of mSecs to wait for FCM */
+
+struct elbc_bank {
+	__be32 br;             /**< Base Register  */
+#define BR_BA           0xFFFF8000
+#define BR_BA_SHIFT             15
+#define BR_PS           0x00001800
+#define BR_PS_SHIFT             11
+#define BR_PS_8         0x00000800  /* Port Size 8 bit */
+#define BR_PS_16        0x00001000  /* Port Size 16 bit */
+#define BR_PS_32        0x00001800  /* Port Size 32 bit */
+#define BR_DECC         0x00000600
+#define BR_DECC_SHIFT            9
+#define BR_DECC_OFF     0x00000000  /* HW ECC checking and generation off */
+#define BR_DECC_CHK     0x00000200  /* HW ECC checking on, generation off */
+#define BR_DECC_CHK_GEN 0x00000400  /* HW ECC checking and generation on */
+#define BR_WP           0x00000100
+#define BR_WP_SHIFT              8
+#define BR_MSEL         0x000000E0
+#define BR_MSEL_SHIFT            5
+#define BR_MS_GPCM      0x00000000  /* GPCM */
+#define BR_MS_FCM       0x00000020  /* FCM */
+#define BR_MS_SDRAM     0x00000060  /* SDRAM */
+#define BR_MS_UPMA      0x00000080  /* UPMA */
+#define BR_MS_UPMB      0x000000A0  /* UPMB */
+#define BR_MS_UPMC      0x000000C0  /* UPMC */
+#define BR_V            0x00000001
+#define BR_V_SHIFT               0
+#define BR_RES          ~(BR_BA|BR_PS|BR_DECC|BR_WP|BR_MSEL|BR_V)
+
+	__be32 or;             /**< Base Register  */
+#define OR0 0x5004
+#define OR1 0x500C
+#define OR2 0x5014
+#define OR3 0x501C
+#define OR4 0x5024
+#define OR5 0x502C
+#define OR6 0x5034
+#define OR7 0x503C
+
+#define OR_FCM_AM               0xFFFF8000
+#define OR_FCM_AM_SHIFT                 15
+#define OR_FCM_BCTLD            0x00001000
+#define OR_FCM_BCTLD_SHIFT              12
+#define OR_FCM_PGS              0x00000400
+#define OR_FCM_PGS_SHIFT                10
+#define OR_FCM_CSCT             0x00000200
+#define OR_FCM_CSCT_SHIFT                9
+#define OR_FCM_CST              0x00000100
+#define OR_FCM_CST_SHIFT                 8
+#define OR_FCM_CHT              0x00000080
+#define OR_FCM_CHT_SHIFT                 7
+#define OR_FCM_SCY              0x00000070
+#define OR_FCM_SCY_SHIFT                 4
+#define OR_FCM_SCY_1            0x00000010
+#define OR_FCM_SCY_2            0x00000020
+#define OR_FCM_SCY_3            0x00000030
+#define OR_FCM_SCY_4            0x00000040
+#define OR_FCM_SCY_5            0x00000050
+#define OR_FCM_SCY_6            0x00000060
+#define OR_FCM_SCY_7            0x00000070
+#define OR_FCM_RST              0x00000008
+#define OR_FCM_RST_SHIFT                 3
+#define OR_FCM_TRLX             0x00000004
+#define OR_FCM_TRLX_SHIFT                2
+#define OR_FCM_EHTR             0x00000002
+#define OR_FCM_EHTR_SHIFT                1
+};
+
+struct elbc_regs {
+	struct elbc_bank bank[8];
+	u8 res0[0x28];
+	__be32 mar;             /**< UPM Address Register */
+	u8 res1[0x4];
+	__be32 mamr;            /**< UPMA Mode Register */
+	__be32 mbmr;            /**< UPMB Mode Register */
+	__be32 mcmr;            /**< UPMC Mode Register */
+	u8 res2[0x8];
+	__be32 mrtpr;           /**< Memory Refresh Timer Prescaler Register */
+	__be32 mdr;             /**< UPM Data Register */
+	u8 res3[0x4];
+	__be32 lsor;            /**< Special Operation Initiation Register */
+	__be32 lsdmr;           /**< SDRAM Mode Register */
+	u8 res4[0x8];
+	__be32 lurt;            /**< UPM Refresh Timer */
+	__be32 lsrt;            /**< SDRAM Refresh Timer */
+	u8 res5[0x8];
+	__be32 ltesr;           /**< Transfer Error Status Register */
+#define LTESR_BM   0x80000000
+#define LTESR_FCT  0x40000000
+#define LTESR_PAR  0x20000000
+#define LTESR_WP   0x04000000
+#define LTESR_ATMW 0x00800000
+#define LTESR_ATMR 0x00400000
+#define LTESR_CS   0x00080000
+#define LTESR_CC   0x00000001
+#define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC)
+	__be32 ltedr;           /**< Transfer Error Disable Register */
+	__be32 lteir;           /**< Transfer Error Interrupt Register */
+	__be32 lteatr;          /**< Transfer Error Attributes Register */
+	__be32 ltear;           /**< Transfer Error Address Register */
+	u8 res6[0xC];
+	__be32 lbcr;            /**< Configuration Register */
+#define LBCR_LDIS  0x80000000
+#define LBCR_LDIS_SHIFT    31
+#define LBCR_BCTLC 0x00C00000
+#define LBCR_BCTLC_SHIFT   22
+#define LBCR_AHD   0x00200000
+#define LBCR_LPBSE 0x00020000
+#define LBCR_LPBSE_SHIFT   17
+#define LBCR_EPAR  0x00010000
+#define LBCR_EPAR_SHIFT    16
+#define LBCR_BMT   0x0000FF00
+#define LBCR_BMT_SHIFT      8
+#define LBCR_INIT  0x00040000
+	__be32 lcrr;            /**< Clock Ratio Register */
+#define LCRR_DBYP    0x80000000
+#define LCRR_DBYP_SHIFT      31
+#define LCRR_BUFCMDC 0x30000000
+#define LCRR_BUFCMDC_SHIFT   28
+#define LCRR_ECL     0x03000000
+#define LCRR_ECL_SHIFT       24
+#define LCRR_EADC    0x00030000
+#define LCRR_EADC_SHIFT      16
+#define LCRR_CLKDIV  0x0000000F
+#define LCRR_CLKDIV_SHIFT     0
+	u8 res7[0x8];
+	__be32 fmr;             /**< Flash Mode Register */
+#define FMR_CWTO     0x0000F000
+#define FMR_CWTO_SHIFT       12
+#define FMR_BOOT     0x00000800
+#define FMR_ECCM     0x00000100
+#define FMR_AL       0x00000030
+#define FMR_AL_SHIFT          4
+#define FMR_OP       0x00000003
+#define FMR_OP_SHIFT          0
+	__be32 fir;             /**< Flash Instruction Register */
+#define FIR_OP0      0xF0000000
+#define FIR_OP0_SHIFT        28
+#define FIR_OP1      0x0F000000
+#define FIR_OP1_SHIFT        24
+#define FIR_OP2      0x00F00000
+#define FIR_OP2_SHIFT        20
+#define FIR_OP3      0x000F0000
+#define FIR_OP3_SHIFT        16
+#define FIR_OP4      0x0000F000
+#define FIR_OP4_SHIFT        12
+#define FIR_OP5      0x00000F00
+#define FIR_OP5_SHIFT         8
+#define FIR_OP6      0x000000F0
+#define FIR_OP6_SHIFT         4
+#define FIR_OP7      0x0000000F
+#define FIR_OP7_SHIFT         0
+#define FIR_OP_NOP   0x0	/* No operation and end of sequence */
+#define FIR_OP_CA    0x1        /* Issue current column address */
+#define FIR_OP_PA    0x2        /* Issue current block+page address */
+#define FIR_OP_UA    0x3        /* Issue user defined address */
+#define FIR_OP_CM0   0x4        /* Issue command from FCR[CMD0] */
+#define FIR_OP_CM1   0x5        /* Issue command from FCR[CMD1] */
+#define FIR_OP_CM2   0x6        /* Issue command from FCR[CMD2] */
+#define FIR_OP_CM3   0x7        /* Issue command from FCR[CMD3] */
+#define FIR_OP_WB    0x8        /* Write FBCR bytes from FCM buffer */
+#define FIR_OP_WS    0x9        /* Write 1 or 2 bytes from MDR[AS] */
+#define FIR_OP_RB    0xA        /* Read FBCR bytes to FCM buffer */
+#define FIR_OP_RS    0xB        /* Read 1 or 2 bytes to MDR[AS] */
+#define FIR_OP_CW0   0xC        /* Wait then issue FCR[CMD0] */
+#define FIR_OP_CW1   0xD        /* Wait then issue FCR[CMD1] */
+#define FIR_OP_RBW   0xE        /* Wait then read FBCR bytes */
+#define FIR_OP_RSW   0xE        /* Wait then read 1 or 2 bytes */
+	__be32 fcr;             /**< Flash Command Register */
+#define FCR_CMD0     0xFF000000
+#define FCR_CMD0_SHIFT       24
+#define FCR_CMD1     0x00FF0000
+#define FCR_CMD1_SHIFT       16
+#define FCR_CMD2     0x0000FF00
+#define FCR_CMD2_SHIFT        8
+#define FCR_CMD3     0x000000FF
+#define FCR_CMD3_SHIFT        0
+	__be32 fbar;            /**< Flash Block Address Register */
+#define FBAR_BLK     0x00FFFFFF
+	__be32 fpar;            /**< Flash Page Address Register */
+#define FPAR_SP_PI   0x00007C00
+#define FPAR_SP_PI_SHIFT     10
+#define FPAR_SP_MS   0x00000200
+#define FPAR_SP_CI   0x000001FF
+#define FPAR_SP_CI_SHIFT      0
+#define FPAR_LP_PI   0x0003F000
+#define FPAR_LP_PI_SHIFT     12
+#define FPAR_LP_MS   0x00000800
+#define FPAR_LP_CI   0x000007FF
+#define FPAR_LP_CI_SHIFT      0
+	__be32 fbcr;            /**< Flash Byte Count Register */
+#define FBCR_BC      0x00000FFF
+	u8 res11[0x8];
+	u8 res8[0xF00];
+};
+
+struct fsl_elbc_ctrl;
+
+/* mtd information per set */
+
+struct fsl_elbc_mtd {
+	struct mtd_info mtd;
+	struct nand_chip chip;
+	struct fsl_elbc_ctrl *ctrl;
+
+	struct device *dev;
+	int bank;               /* Chip select bank number           */
+	u8 __iomem *vbase;      /* Chip select base virtual address  */
+	int page_size;          /* NAND page size (0=512, 1=2048)    */
+	unsigned int fmr;       /* FCM Flash Mode Register value     */
+};
+
+/* overview of the fsl elbc controller */
+
+struct fsl_elbc_ctrl {
+	struct nand_hw_control controller;
+	struct fsl_elbc_mtd *chips[MAX_BANKS];
+
+	/* device info */
+	struct device *dev;
+	struct elbc_regs __iomem *regs;
+	int irq;
+	wait_queue_head_t irq_wait;
+	unsigned int irq_status; /* status read from LTESR by irq handler */
+	u8 __iomem *addr;        /* Address of assigned FCM buffer        */
+	unsigned int page;       /* Last page written to / read from      */
+	unsigned int read_bytes; /* Number of bytes read during command   */
+	unsigned int column;     /* Saved column from SEQIN               */
+	unsigned int index;      /* Pointer to next byte to 'read'        */
+	unsigned int status;     /* status read from LTESR after last op  */
+	unsigned int mdr;        /* UPM/FCM Data Register value           */
+	unsigned int use_mdr;    /* Non zero if the MDR is to be set      */
+	unsigned int oob;        /* Non zero if operating on OOB data     */
+	char *oob_poi;           /* Place to write ECC after read back    */
+};
+
+/* These map to the positions used by the FCM hardware ECC generator */
+
+/* Small Page FLASH with FMR[ECCM] = 0 */
+static struct nand_ecclayout fsl_elbc_oob_sp_eccm0 = {
+	.eccbytes = 3,
+	.eccpos = {6, 7, 8},
+	.oobfree = { {0, 5}, {9, 7} },
+	.oobavail = 12,
+};
+
+/* Small Page FLASH with FMR[ECCM] = 1 */
+static struct nand_ecclayout fsl_elbc_oob_sp_eccm1 = {
+	.eccbytes = 3,
+	.eccpos = {8, 9, 10},
+	.oobfree = { {0, 5}, {6, 2}, {11, 5} },
+	.oobavail = 12,
+};
+
+/* Large Page FLASH with FMR[ECCM] = 0 */
+static struct nand_ecclayout fsl_elbc_oob_lp_eccm0 = {
+	.eccbytes = 12,
+	.eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
+	.oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} },
+	.oobavail = 48,
+};
+
+/* Large Page FLASH with FMR[ECCM] = 1 */
+static struct nand_ecclayout fsl_elbc_oob_lp_eccm1 = {
+	.eccbytes = 12,
+	.eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
+	.oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} },
+	.oobavail = 48,
+};
+
+/*=================================*/
+
+/*
+ * Set up the FCM hardware block and page address fields, and the fcm
+ * structure addr field to point to the correct FCM buffer in memory
+ */
+static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+	int buf_num;
+
+	ctrl->page = page_addr;
+
+	out_be32(&lbc->fbar,
+	         page_addr >> (chip->phys_erase_shift - chip->page_shift));
+
+	if (priv->page_size) {
+		out_be32(&lbc->fpar,
+		         ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
+		         (oob ? FPAR_LP_MS : 0) | column);
+		buf_num = (page_addr & 1) << 2;
+	} else {
+		out_be32(&lbc->fpar,
+		         ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
+		         (oob ? FPAR_SP_MS : 0) | column);
+		buf_num = page_addr & 7;
+	}
+
+	ctrl->addr = priv->vbase + buf_num * 1024;
+	ctrl->index = column;
+
+	/* for OOB data point to the second half of the buffer */
+	if (oob)
+		ctrl->index += priv->page_size ? 2048 : 512;
+
+	dev_vdbg(ctrl->dev, "set_addr: bank=%d, ctrl->addr=0x%p (0x%p), "
+	                    "index %x, pes %d ps %d\n",
+	         buf_num, ctrl->addr, priv->vbase, ctrl->index,
+	         chip->phys_erase_shift, chip->page_shift);
+}
+
+/*
+ * execute FCM command and wait for it to complete
+ */
+static int fsl_elbc_run_command(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+
+	/* Setup the FMR[OP] to execute without write protection */
+	out_be32(&lbc->fmr, priv->fmr | 3);
+	if (ctrl->use_mdr)
+		out_be32(&lbc->mdr, ctrl->mdr);
+
+	dev_vdbg(ctrl->dev,
+	         "fsl_elbc_run_command: fmr=%08x fir=%08x fcr=%08x\n",
+	         in_be32(&lbc->fmr), in_be32(&lbc->fir), in_be32(&lbc->fcr));
+	dev_vdbg(ctrl->dev,
+	         "fsl_elbc_run_command: fbar=%08x fpar=%08x "
+	         "fbcr=%08x bank=%d\n",
+	         in_be32(&lbc->fbar), in_be32(&lbc->fpar),
+	         in_be32(&lbc->fbcr), priv->bank);
+
+	/* execute special operation */
+	out_be32(&lbc->lsor, priv->bank);
+
+	/* wait for FCM complete flag or timeout */
+	ctrl->irq_status = 0;
+	wait_event_timeout(ctrl->irq_wait, ctrl->irq_status,
+	                   FCM_TIMEOUT_MSECS * HZ/1000);
+	ctrl->status = ctrl->irq_status;
+
+	/* store mdr value in case it was needed */
+	if (ctrl->use_mdr)
+		ctrl->mdr = in_be32(&lbc->mdr);
+
+	ctrl->use_mdr = 0;
+
+	dev_vdbg(ctrl->dev,
+	         "fsl_elbc_run_command: stat=%08x mdr=%08x fmr=%08x\n",
+	         ctrl->status, ctrl->mdr, in_be32(&lbc->fmr));
+
+	/* returns 0 on success otherwise non-zero) */
+	return ctrl->status == LTESR_CC ? 0 : -EIO;
+}
+
+static void fsl_elbc_do_read(struct nand_chip *chip, int oob)
+{
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+
+	if (priv->page_size) {
+		out_be32(&lbc->fir,
+		         (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+		         (FIR_OP_CA  << FIR_OP1_SHIFT) |
+		         (FIR_OP_PA  << FIR_OP2_SHIFT) |
+		         (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+		         (FIR_OP_RBW << FIR_OP4_SHIFT));
+
+		out_be32(&lbc->fcr, (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+		                    (NAND_CMD_READSTART << FCR_CMD1_SHIFT));
+	} else {
+		out_be32(&lbc->fir,
+		         (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+		         (FIR_OP_CA  << FIR_OP1_SHIFT) |
+		         (FIR_OP_PA  << FIR_OP2_SHIFT) |
+		         (FIR_OP_RBW << FIR_OP3_SHIFT));
+
+		if (oob)
+			out_be32(&lbc->fcr, NAND_CMD_READOOB << FCR_CMD0_SHIFT);
+		else
+			out_be32(&lbc->fcr, NAND_CMD_READ0 << FCR_CMD0_SHIFT);
+	}
+}
+
+/* cmdfunc send commands to the FCM */
+static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command,
+                             int column, int page_addr)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+
+	ctrl->use_mdr = 0;
+
+	/* clear the read buffer */
+	ctrl->read_bytes = 0;
+	if (command != NAND_CMD_PAGEPROG)
+		ctrl->index = 0;
+
+	switch (command) {
+	/* READ0 and READ1 read the entire buffer to use hardware ECC. */
+	case NAND_CMD_READ1:
+		column += 256;
+
+	/* fall-through */
+	case NAND_CMD_READ0:
+		dev_dbg(ctrl->dev,
+		        "fsl_elbc_cmdfunc: NAND_CMD_READ0, page_addr:"
+		        " 0x%x, column: 0x%x.\n", page_addr, column);
+
+
+		out_be32(&lbc->fbcr, 0); /* read entire page to enable ECC */
+		set_addr(mtd, 0, page_addr, 0);
+
+		ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+		ctrl->index += column;
+
+		fsl_elbc_do_read(chip, 0);
+		fsl_elbc_run_command(mtd);
+		return;
+
+	/* READOOB reads only the OOB because no ECC is performed. */
+	case NAND_CMD_READOOB:
+		dev_vdbg(ctrl->dev,
+		         "fsl_elbc_cmdfunc: NAND_CMD_READOOB, page_addr:"
+			 " 0x%x, column: 0x%x.\n", page_addr, column);
+
+		out_be32(&lbc->fbcr, mtd->oobsize - column);
+		set_addr(mtd, column, page_addr, 1);
+
+		ctrl->read_bytes = mtd->writesize + mtd->oobsize;
+
+		fsl_elbc_do_read(chip, 1);
+		fsl_elbc_run_command(mtd);
+		return;
+
+	/* READID must read all 5 possible bytes while CEB is active */
+	case NAND_CMD_READID:
+		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_READID.\n");
+
+		out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+		                    (FIR_OP_UA  << FIR_OP1_SHIFT) |
+		                    (FIR_OP_RBW << FIR_OP2_SHIFT));
+		out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT);
+		/* 5 bytes for manuf, device and exts */
+		out_be32(&lbc->fbcr, 5);
+		ctrl->read_bytes = 5;
+		ctrl->use_mdr = 1;
+		ctrl->mdr = 0;
+
+		set_addr(mtd, 0, 0, 0);
+		fsl_elbc_run_command(mtd);
+		return;
+
+	/* ERASE1 stores the block and page address */
+	case NAND_CMD_ERASE1:
+		dev_vdbg(ctrl->dev,
+		         "fsl_elbc_cmdfunc: NAND_CMD_ERASE1, "
+		         "page_addr: 0x%x.\n", page_addr);
+		set_addr(mtd, 0, page_addr, 0);
+		return;
+
+	/* ERASE2 uses the block and page address from ERASE1 */
+	case NAND_CMD_ERASE2:
+		dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_ERASE2.\n");
+
+		out_be32(&lbc->fir,
+		         (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+		         (FIR_OP_PA  << FIR_OP1_SHIFT) |
+		         (FIR_OP_CM1 << FIR_OP2_SHIFT));
+
+		out_be32(&lbc->fcr,
+		         (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
+		         (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT));
+
+		out_be32(&lbc->fbcr, 0);
+		ctrl->read_bytes = 0;
+
+		fsl_elbc_run_command(mtd);
+		return;
+
+	/* SEQIN sets up the addr buffer and all registers except the length */
+	case NAND_CMD_SEQIN: {
+		__be32 fcr;
+		dev_vdbg(ctrl->dev,
+		         "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, "
+		         "page_addr: 0x%x, column: 0x%x.\n",
+		         page_addr, column);
+
+		ctrl->column = column;
+		ctrl->oob = 0;
+
+		fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) |
+		      (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
+
+		if (priv->page_size) {
+			out_be32(&lbc->fir,
+			         (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+			         (FIR_OP_CA  << FIR_OP1_SHIFT) |
+			         (FIR_OP_PA  << FIR_OP2_SHIFT) |
+			         (FIR_OP_WB  << FIR_OP3_SHIFT) |
+			         (FIR_OP_CW1 << FIR_OP4_SHIFT));
+
+			fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
+		} else {
+			out_be32(&lbc->fir,
+			         (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+			         (FIR_OP_CM2 << FIR_OP1_SHIFT) |
+			         (FIR_OP_CA  << FIR_OP2_SHIFT) |
+			         (FIR_OP_PA  << FIR_OP3_SHIFT) |
+			         (FIR_OP_WB  << FIR_OP4_SHIFT) |
+			         (FIR_OP_CW1 << FIR_OP5_SHIFT));
+
+			if (column >= mtd->writesize) {
+				/* OOB area --> READOOB */
+				column -= mtd->writesize;
+				fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT;
+				ctrl->oob = 1;
+			} else if (column < 256) {
+				/* First 256 bytes --> READ0 */
+				fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT;
+			} else {
+				/* Second 256 bytes --> READ1 */
+				fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT;
+			}
+		}
+
+		out_be32(&lbc->fcr, fcr);
+		set_addr(mtd, column, page_addr, ctrl->oob);
+		return;
+	}
+
+	/* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+	case NAND_CMD_PAGEPROG: {
+		int full_page;
+		dev_vdbg(ctrl->dev,
+		         "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG "
+		         "writing %d bytes.\n", ctrl->index);
+
+		/* if the write did not start at 0 or is not a full page
+		 * then set the exact length, otherwise use a full page
+		 * write so the HW generates the ECC.
+		 */
+		if (ctrl->oob || ctrl->column != 0 ||
+		    ctrl->index != mtd->writesize + mtd->oobsize) {
+			out_be32(&lbc->fbcr, ctrl->index);
+			full_page = 0;
+		} else {
+			out_be32(&lbc->fbcr, 0);
+			full_page = 1;
+		}
+
+		fsl_elbc_run_command(mtd);
+
+		/* Read back the page in order to fill in the ECC for the
+		 * caller.  Is this really needed?
+		 */
+		if (full_page && ctrl->oob_poi) {
+			out_be32(&lbc->fbcr, 3);
+			set_addr(mtd, 6, page_addr, 1);
+
+			ctrl->read_bytes = mtd->writesize + 9;
+
+			fsl_elbc_do_read(chip, 1);
+			fsl_elbc_run_command(mtd);
+
+			memcpy_fromio(ctrl->oob_poi + 6,
+			              &ctrl->addr[ctrl->index], 3);
+			ctrl->index += 3;
+		}
+
+		ctrl->oob_poi = NULL;
+		return;
+	}
+
+	/* CMD_STATUS must read the status byte while CEB is active */
+	/* Note - it does not wait for the ready line */
+	case NAND_CMD_STATUS:
+		out_be32(&lbc->fir,
+		         (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+		         (FIR_OP_RBW << FIR_OP1_SHIFT));
+		out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
+		out_be32(&lbc->fbcr, 1);
+		set_addr(mtd, 0, 0, 0);
+		ctrl->read_bytes = 1;
+
+		fsl_elbc_run_command(mtd);
+
+		/* The chip always seems to report that it is
+		 * write-protected, even when it is not.
+		 */
+		setbits8(ctrl->addr, NAND_STATUS_WP);
+		return;
+
+	/* RESET without waiting for the ready line */
+	case NAND_CMD_RESET:
+		dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n");
+		out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT);
+		out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT);
+		fsl_elbc_run_command(mtd);
+		return;
+
+	default:
+		dev_err(ctrl->dev,
+		        "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n",
+		        command);
+	}
+}
+
+static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip)
+{
+	/* The hardware does not seem to support multiple
+	 * chips per bank.
+	 */
+}
+
+/*
+ * Write buf to the FCM Controller Data Buffer
+ */
+static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	unsigned int bufsize = mtd->writesize + mtd->oobsize;
+
+	if (len < 0) {
+		dev_err(ctrl->dev, "write_buf of %d bytes", len);
+		ctrl->status = 0;
+		return;
+	}
+
+	if ((unsigned int)len > bufsize - ctrl->index) {
+		dev_err(ctrl->dev,
+		        "write_buf beyond end of buffer "
+		        "(%d requested, %u available)\n",
+		        len, bufsize - ctrl->index);
+		len = bufsize - ctrl->index;
+	}
+
+	memcpy_toio(&ctrl->addr[ctrl->index], buf, len);
+	ctrl->index += len;
+}
+
+/*
+ * read a byte from either the FCM hardware buffer if it has any data left
+ * otherwise issue a command to read a single byte.
+ */
+static u8 fsl_elbc_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+
+	/* If there are still bytes in the FCM, then use the next byte. */
+	if (ctrl->index < ctrl->read_bytes)
+		return in_8(&ctrl->addr[ctrl->index++]);
+
+	dev_err(ctrl->dev, "read_byte beyond end of buffer\n");
+	return ERR_BYTE;
+}
+
+/*
+ * Read from the FCM Controller Data Buffer
+ */
+static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	int avail;
+
+	if (len < 0)
+		return;
+
+	avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index);
+	memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail);
+	ctrl->index += avail;
+
+	if (len > avail)
+		dev_err(ctrl->dev,
+		        "read_buf beyond end of buffer "
+		        "(%d requested, %d available)\n",
+		        len, avail);
+}
+
+/*
+ * Verify buffer against the FCM Controller Data Buffer
+ */
+static int fsl_elbc_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	int i;
+
+	if (len < 0) {
+		dev_err(ctrl->dev, "write_buf of %d bytes", len);
+		return -EINVAL;
+	}
+
+	if ((unsigned int)len > ctrl->read_bytes - ctrl->index) {
+		dev_err(ctrl->dev,
+		        "verify_buf beyond end of buffer "
+		        "(%d requested, %u available)\n",
+		        len, ctrl->read_bytes - ctrl->index);
+
+		ctrl->index = ctrl->read_bytes;
+		return -EINVAL;
+	}
+
+	for (i = 0; i < len; i++)
+		if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i])
+			break;
+
+	ctrl->index += len;
+	return i == len && ctrl->status == LTESR_CC ? 0 : -EIO;
+}
+
+/* This function is called after Program and Erase Operations to
+ * check for success or failure.
+ */
+static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+
+	if (ctrl->status != LTESR_CC)
+		return NAND_STATUS_FAIL;
+
+	/* Use READ_STATUS command, but wait for the device to be ready */
+	ctrl->use_mdr = 0;
+	out_be32(&lbc->fir,
+	         (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+	         (FIR_OP_RBW << FIR_OP1_SHIFT));
+	out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT);
+	out_be32(&lbc->fbcr, 1);
+	set_addr(mtd, 0, 0, 0);
+	ctrl->read_bytes = 1;
+
+	fsl_elbc_run_command(mtd);
+
+	if (ctrl->status != LTESR_CC)
+		return NAND_STATUS_FAIL;
+
+	/* The chip always seems to report that it is
+	 * write-protected, even when it is not.
+	 */
+	setbits8(ctrl->addr, NAND_STATUS_WP);
+	return fsl_elbc_read_byte(mtd);
+}
+
+static int fsl_elbc_chip_init_tail(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+	unsigned int al;
+
+	/* calculate FMR Address Length field */
+	al = 0;
+	if (chip->pagemask & 0xffff0000)
+		al++;
+	if (chip->pagemask & 0xff000000)
+		al++;
+
+	/* add to ECCM mode set in fsl_elbc_init */
+	priv->fmr |= (12 << FMR_CWTO_SHIFT) |  /* Timeout > 12 ms */
+	             (al << FMR_AL_SHIFT);
+
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n",
+	        chip->numchips);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %ld\n",
+	        chip->chipsize);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n",
+	        chip->pagemask);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n",
+	        chip->chip_delay);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n",
+	        chip->badblockpos);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n",
+	        chip->chip_shift);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n",
+	        chip->page_shift);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n",
+	        chip->phys_erase_shift);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n",
+	        chip->ecclayout);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n",
+	        chip->ecc.mode);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n",
+	        chip->ecc.steps);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n",
+	        chip->ecc.bytes);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n",
+	        chip->ecc.total);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n",
+	        chip->ecc.layout);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %d\n", mtd->size);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n",
+	        mtd->erasesize);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n",
+	        mtd->writesize);
+	dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n",
+	        mtd->oobsize);
+
+	/* adjust Option Register and ECC to match Flash page size */
+	if (mtd->writesize == 512) {
+		priv->page_size = 0;
+		clrbits32(&lbc->bank[priv->bank].or, ~OR_FCM_PGS);
+	} else if (mtd->writesize == 2048) {
+		priv->page_size = 1;
+		setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS);
+		/* adjust ecc setup if needed */
+		if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
+		    BR_DECC_CHK_GEN) {
+			chip->ecc.size = 512;
+			chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
+			                   &fsl_elbc_oob_lp_eccm1 :
+			                   &fsl_elbc_oob_lp_eccm0;
+			mtd->ecclayout = chip->ecc.layout;
+			mtd->oobavail = chip->ecc.layout->oobavail;
+		}
+	} else {
+		dev_err(ctrl->dev,
+		        "fsl_elbc_init: page size %d is not supported\n",
+		        mtd->writesize);
+		return -1;
+	}
+
+	/* The default u-boot configuration on MPC8313ERDB causes errors;
+	 * more delay is needed.  This should be safe for other boards
+	 * as well.
+	 */
+	setbits32(&lbc->bank[priv->bank].or, 0x70);
+	return 0;
+}
+
+static int fsl_elbc_read_page(struct mtd_info *mtd,
+                              struct nand_chip *chip,
+                              uint8_t *buf)
+{
+	fsl_elbc_read_buf(mtd, buf, mtd->writesize);
+	fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	if (fsl_elbc_wait(mtd, chip) & NAND_STATUS_FAIL)
+		mtd->ecc_stats.failed++;
+
+	return 0;
+}
+
+/* ECC will be calculated automatically, and errors will be detected in
+ * waitfunc.
+ */
+static void fsl_elbc_write_page(struct mtd_info *mtd,
+                                struct nand_chip *chip,
+                                const uint8_t *buf)
+{
+	struct fsl_elbc_mtd *priv = chip->priv;
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+
+	fsl_elbc_write_buf(mtd, buf, mtd->writesize);
+	fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	ctrl->oob_poi = chip->oob_poi;
+}
+
+static int fsl_elbc_chip_init(struct fsl_elbc_mtd *priv)
+{
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+	struct nand_chip *chip = &priv->chip;
+
+	dev_dbg(priv->dev, "eLBC Set Information for bank %d\n", priv->bank);
+
+	/* Fill in fsl_elbc_mtd structure */
+	priv->mtd.priv = chip;
+	priv->mtd.owner = THIS_MODULE;
+	priv->fmr = 0; /* rest filled in later */
+
+	/* fill in nand_chip structure */
+	/* set up function call table */
+	chip->read_byte = fsl_elbc_read_byte;
+	chip->write_buf = fsl_elbc_write_buf;
+	chip->read_buf = fsl_elbc_read_buf;
+	chip->verify_buf = fsl_elbc_verify_buf;
+	chip->select_chip = fsl_elbc_select_chip;
+	chip->cmdfunc = fsl_elbc_cmdfunc;
+	chip->waitfunc = fsl_elbc_wait;
+
+	/* set up nand options */
+	chip->options = NAND_NO_READRDY | NAND_NO_AUTOINCR;
+
+	chip->controller = &ctrl->controller;
+	chip->priv = priv;
+
+	chip->ecc.read_page = fsl_elbc_read_page;
+	chip->ecc.write_page = fsl_elbc_write_page;
+
+	/* If CS Base Register selects full hardware ECC then use it */
+	if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) ==
+	    BR_DECC_CHK_GEN) {
+		chip->ecc.mode = NAND_ECC_HW;
+		/* put in small page settings and adjust later if needed */
+		chip->ecc.layout = (priv->fmr & FMR_ECCM) ?
+				&fsl_elbc_oob_sp_eccm1 : &fsl_elbc_oob_sp_eccm0;
+		chip->ecc.size = 512;
+		chip->ecc.bytes = 3;
+	} else {
+		/* otherwise fall back to default software ECC */
+		chip->ecc.mode = NAND_ECC_SOFT;
+	}
+
+	return 0;
+}
+
+static int fsl_elbc_chip_remove(struct fsl_elbc_mtd *priv)
+{
+	struct fsl_elbc_ctrl *ctrl = priv->ctrl;
+
+	nand_release(&priv->mtd);
+
+	if (priv->vbase)
+		iounmap(priv->vbase);
+
+	ctrl->chips[priv->bank] = NULL;
+	kfree(priv);
+
+	return 0;
+}
+
+static int fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl,
+                               struct device_node *node)
+{
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+	struct fsl_elbc_mtd *priv;
+	struct resource res;
+#ifdef CONFIG_MTD_PARTITIONS
+	static const char *part_probe_types[]
+		= { "cmdlinepart", "RedBoot", NULL };
+	struct mtd_partition *parts;
+#endif
+	int ret;
+	int bank;
+
+	/* get, allocate and map the memory resource */
+	ret = of_address_to_resource(node, 0, &res);
+	if (ret) {
+		dev_err(ctrl->dev, "failed to get resource\n");
+		return ret;
+	}
+
+	/* find which chip select it is connected to */
+	for (bank = 0; bank < MAX_BANKS; bank++)
+		if ((in_be32(&lbc->bank[bank].br) & BR_V) &&
+		    (in_be32(&lbc->bank[bank].br) & BR_MSEL) == BR_MS_FCM &&
+		    (in_be32(&lbc->bank[bank].br) &
+		     in_be32(&lbc->bank[bank].or) & BR_BA)
+		     == res.start)
+			break;
+
+	if (bank >= MAX_BANKS) {
+		dev_err(ctrl->dev, "address did not match any chip selects\n");
+		return -ENODEV;
+	}
+
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	ctrl->chips[bank] = priv;
+	priv->bank = bank;
+	priv->ctrl = ctrl;
+	priv->dev = ctrl->dev;
+
+	priv->vbase = ioremap(res.start, res.end - res.start + 1);
+	if (!priv->vbase) {
+		dev_err(ctrl->dev, "failed to map chip region\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	ret = fsl_elbc_chip_init(priv);
+	if (ret)
+		goto err;
+
+	ret = nand_scan_ident(&priv->mtd, 1);
+	if (ret)
+		goto err;
+
+	ret = fsl_elbc_chip_init_tail(&priv->mtd);
+	if (ret)
+		goto err;
+
+	ret = nand_scan_tail(&priv->mtd);
+	if (ret)
+		goto err;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	/* First look for RedBoot table or partitions on the command
+	 * line, these take precedence over device tree information */
+	ret = parse_mtd_partitions(&priv->mtd, part_probe_types, &parts, 0);
+	if (ret < 0)
+		goto err;
+
+#ifdef CONFIG_MTD_OF_PARTS
+	if (ret == 0) {
+		ret = of_mtd_parse_partitions(priv->dev, &priv->mtd,
+		                              node, &parts);
+		if (ret < 0)
+			goto err;
+	}
+#endif
+
+	if (ret > 0)
+		add_mtd_partitions(&priv->mtd, parts, ret);
+	else
+#endif
+		add_mtd_device(&priv->mtd);
+
+	printk(KERN_INFO "eLBC NAND device at 0x%zx, bank %d\n",
+	       res.start, priv->bank);
+	return 0;
+
+err:
+	fsl_elbc_chip_remove(priv);
+	return ret;
+}
+
+static int __devinit fsl_elbc_ctrl_init(struct fsl_elbc_ctrl *ctrl)
+{
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+
+	/* clear event registers */
+	setbits32(&lbc->ltesr, LTESR_NAND_MASK);
+	out_be32(&lbc->lteatr, 0);
+
+	/* Enable interrupts for any detected events */
+	out_be32(&lbc->lteir, LTESR_NAND_MASK);
+
+	ctrl->read_bytes = 0;
+	ctrl->index = 0;
+	ctrl->addr = NULL;
+
+	return 0;
+}
+
+static int __devexit fsl_elbc_ctrl_remove(struct of_device *ofdev)
+{
+	struct fsl_elbc_ctrl *ctrl = dev_get_drvdata(&ofdev->dev);
+	int i;
+
+	for (i = 0; i < MAX_BANKS; i++)
+		if (ctrl->chips[i])
+			fsl_elbc_chip_remove(ctrl->chips[i]);
+
+	if (ctrl->irq)
+		free_irq(ctrl->irq, ctrl);
+
+	if (ctrl->regs)
+		iounmap(ctrl->regs);
+
+	dev_set_drvdata(&ofdev->dev, NULL);
+	kfree(ctrl);
+	return 0;
+}
+
+/* NOTE: This interrupt is also used to report other localbus events,
+ * such as transaction errors on other chipselects.  If we want to
+ * capture those, we'll need to move the IRQ code into a shared
+ * LBC driver.
+ */
+
+static irqreturn_t fsl_elbc_ctrl_irq(int irqno, void *data)
+{
+	struct fsl_elbc_ctrl *ctrl = data;
+	struct elbc_regs __iomem *lbc = ctrl->regs;
+	__be32 status = in_be32(&lbc->ltesr) & LTESR_NAND_MASK;
+
+	if (status) {
+		out_be32(&lbc->ltesr, status);
+		out_be32(&lbc->lteatr, 0);
+
+		ctrl->irq_status = status;
+		smp_wmb();
+		wake_up(&ctrl->irq_wait);
+
+		return IRQ_HANDLED;
+	}
+
+	return IRQ_NONE;
+}
+
+/* fsl_elbc_ctrl_probe
+ *
+ * called by device layer when it finds a device matching
+ * one our driver can handled. This code allocates all of
+ * the resources needed for the controller only.  The
+ * resources for the NAND banks themselves are allocated
+ * in the chip probe function.
+*/
+
+static int __devinit fsl_elbc_ctrl_probe(struct of_device *ofdev,
+                                         const struct of_device_id *match)
+{
+	struct device_node *child;
+	struct fsl_elbc_ctrl *ctrl;
+	int ret;
+
+	ctrl = kzalloc(sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	dev_set_drvdata(&ofdev->dev, ctrl);
+
+	spin_lock_init(&ctrl->controller.lock);
+	init_waitqueue_head(&ctrl->controller.wq);
+	init_waitqueue_head(&ctrl->irq_wait);
+
+	ctrl->regs = of_iomap(ofdev->node, 0);
+	if (!ctrl->regs) {
+		dev_err(&ofdev->dev, "failed to get memory region\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ctrl->irq = of_irq_to_resource(ofdev->node, 0, NULL);
+	if (ctrl->irq == NO_IRQ) {
+		dev_err(&ofdev->dev, "failed to get irq resource\n");
+		ret = -ENODEV;
+		goto err;
+	}
+
+	ctrl->dev = &ofdev->dev;
+
+	ret = fsl_elbc_ctrl_init(ctrl);
+	if (ret < 0)
+		goto err;
+
+	ret = request_irq(ctrl->irq, fsl_elbc_ctrl_irq, 0, "fsl-elbc", ctrl);
+	if (ret != 0) {
+		dev_err(&ofdev->dev, "failed to install irq (%d)\n",
+		        ctrl->irq);
+		ret = ctrl->irq;
+		goto err;
+	}
+
+	for_each_child_of_node(ofdev->node, child)
+		if (of_device_is_compatible(child, "fsl,elbc-fcm-nand"))
+			fsl_elbc_chip_probe(ctrl, child);
+
+	return 0;
+
+err:
+	fsl_elbc_ctrl_remove(ofdev);
+	return ret;
+}
+
+static const struct of_device_id fsl_elbc_match[] = {
+	{
+		.compatible = "fsl,elbc",
+	},
+	{}
+};
+
+static struct of_platform_driver fsl_elbc_ctrl_driver = {
+	.driver = {
+		.name	= "fsl-elbc",
+	},
+	.match_table = fsl_elbc_match,
+	.probe = fsl_elbc_ctrl_probe,
+	.remove = __devexit_p(fsl_elbc_ctrl_remove),
+};
+
+static int __init fsl_elbc_init(void)
+{
+	return of_register_platform_driver(&fsl_elbc_ctrl_driver);
+}
+
+static void __exit fsl_elbc_exit(void)
+{
+	of_unregister_platform_driver(&fsl_elbc_ctrl_driver);
+}
+
+module_init(fsl_elbc_init);
+module_exit(fsl_elbc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale");
+MODULE_DESCRIPTION("Freescale Enhanced Local Bus Controller MTD NAND driver");
-- 
1.5.3.8

^ permalink raw reply related

* Re: System Clock runaway on Xilinx platform
From: Grant Likely @ 2008-02-06 22:52 UTC (permalink / raw)
  To: Kevin Holland; +Cc: linuxppc-embedded
In-Reply-To: <1202337576.20550.6.camel@kevin-laptop>

On 2/6/08, Kevin Holland <khollan@daktronics.com> wrote:
> I load with the zImage.elf, so my xparameters are set like this:
>
> #define XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ 300000000
> #define XPAR_CORE_CLOCK_FREQ_HZ XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ
>
> I took readings every 5 min over a 15 min period and it seems to be
> increasing by approximately 50 sec every 5 mins.

Hmm; 5 min == 300 seconds
300 - 50 == 250 seconds

300/250 = 1.2

So, timebase_period_ns is set 1.2 times faster than the actual period.
 Can you confirm that the EDK frequency is actually correct?  Are you
*sure* that the CPU is running at 300MHz.

Cheers,
g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.

^ permalink raw reply

* Re: System Clock runaway on Xilinx platform
From: Kevin Holland @ 2008-02-06 22:39 UTC (permalink / raw)
  To: Grant Likely; +Cc: linuxppc-embedded
In-Reply-To: <fa686aa40802061229w3c6ae051kec18d1d25ad1da4@mail.gmail.com>

I load with the zImage.elf, so my xparameters are set like this:

#define XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ 300000000
#define XPAR_CORE_CLOCK_FREQ_HZ XPAR_CPU_PPC405_CORE_CLOCK_FREQ_HZ

I took readings every 5 min over a 15 min period and it seems to be
increasing by approximately 50 sec every 5 mins.

Thanks

Kevin

On Wed, 2008-02-06 at 13:29 -0700, Grant Likely wrote:
> On 2/6/08, khollan <khollan@daktronics.com> wrote:
> >
> > Hi all,
> >
> > Thanks for the help so far!
> > I'm now running linux 2.6.21 on my custom virtex 4 board modeled after the
> > ml410.  The cpu clock is 300MHz and the PLB bus is 175MHz.
> >
> > My question is which clock is the linux system clock that keeps track of the
> > date derived from?  I set my date with rdate -s time.mit.edu at boot and
> > then compare with the date command and rdate -p time.mit.edu and they are
> > off by 20 or so seconds even just after a few minutes, this trend continues
> > and it will be off by a day after a few hours.  I think I just don't have
> > something defined correctly but I can't figure out which.
> 
> The clock is maintained off of the PowerPC time base register (TBL,
> TBU).  On Virtex ppc platforms, the time base frequency is supposed to
> be the same as the CPU clock.  Can you figure out *exactly* how
> quickly the clock is drifting?
> 
> How are you booting your Virtex platform?  u-boot or a zImage.elf?  If
> it's a zImage, the CPU clock frequency is set in
> arch/ppc/boot/simple/embed_config.c (search for CONFIG_XILINX and look
> for the line that sets bd->bi_intfreq).  Make XPAR_CORE_CLOCK_FREQ_HZ
> in xparameters matches you CPU clock speed.
> 
> Side note: you may notice that bd->bi_tbfreq is also used but not set;
> that's because bd->bi_intfreq and bd->tbfreq point to the same memory
> location.  (It's kind of messed up that way, don't worry too much
> about it)
> 
> If you're using u-boot, then u-boot will set the cpu frequency in the
> board info structure.
> 
> Cheers,
> g.
> 

^ permalink raw reply

* Re: drivers/char/hvc_* and /Documentation/powerpc/hvcs.txt
From: Timur Tabi @ 2008-02-06 23:16 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev
In-Reply-To: <20080126010409.GA25717@lixom.net>

Olof Johansson wrote:

> Yep, pretty much. It's real easy to write a new backend to hvc, I've got
> a couple for various bringup environments here as well.

Could you tell me which kernel options I need to set/unset in order to get my 
driver to be called?  My driver loads and initializes, but its put_char function 
is never called.  Trying to debug to the console subsystem is making me dizzy.

-- 
Timur Tabi
Linux kernel developer at Freescale

^ permalink raw reply

* Re: [RFC][PATCH] remove section mappinng
From: Paul Mackerras @ 2008-02-07  0:09 UTC (permalink / raw)
  To: Badari Pulavarty; +Cc: linux-mm, linuxppc-dev, anton
In-Reply-To: <1201558765.29357.1.camel@dyn9047017100.beaverton.ibm.com>

Badari Pulavarty writes:

> Thank you for your input and suggestions. Does this look reasonable
> to you ?
> 
> Thanks,
> Badari
> 
> For memory remove, we need to clean up htab mappings for the
> section of the memory we are removing.
> 
> This patch implements support for removing htab bolted mappings
> for ppc64 lpar. Other sub-archs, may need to implement similar
> functionality for the hotplug memory remove to work. 

Looks OK to me.

Paul.

^ permalink raw reply

* RE: System Clock runaway on Xilinx platform
From: Stephen Neuendorffer @ 2008-02-07  0:10 UTC (permalink / raw)
  To: khollan, linuxppc-embedded
In-Reply-To: <15312437.post@talk.nabble.com>


The processor is an integer factor faster than the bus, unless you're
doing something really unusual.  Are you sure that's not 300 and 150?

175/150 =3D 1.16, which might be the difference you're seeing?

Steve

> -----Original Message-----
> From: linuxppc-embedded-bounces+stephen=3Dneuendorffer.name@ozlabs.org
[mailto:linuxppc-embedded-
> bounces+stephen=3Dneuendorffer.name@ozlabs.org] On Behalf Of khollan
> Sent: Wednesday, February 06, 2008 11:42 AM
> To: linuxppc-embedded@ozlabs.org
> Subject: System Clock runaway on Xilinx platform
>=20
>=20
> Hi all,
>=20
> Thanks for the help so far!
> I'm now running linux 2.6.21 on my custom virtex 4 board modeled after
the
> ml410.  The cpu clock is 300MHz and the PLB bus is 175MHz.
>=20
> My question is which clock is the linux system clock that keeps track
of the
> date derived from?  I set my date with rdate -s time.mit.edu at boot
and
> then compare with the date command and rdate -p time.mit.edu and they
are
> off by 20 or so seconds even just after a few minutes, this trend
continues
> and it will be off by a day after a few hours.  I think I just don't
have
> something defined correctly but I can't figure out which.
>=20
> Thanks
>=20
> Kevin
> --
> View this message in context:
http://www.nabble.com/System-Clock-runaway-on-Xilinx-platform-
> tp15312437p15312437.html
> Sent from the linuxppc-embedded mailing list archive at Nabble.com.
>=20
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded

^ permalink raw reply

* 440EPX is not booting from 1Gb/128MB NAND flash
From: Nikhil Gautam @ 2008-02-07  0:36 UTC (permalink / raw)
  To: linuxppc-embedded

Hi,

I am trying to boot my board which is based on Sequoia 440EPX. The
only difference between my board and the reference board is that I
have a 1 Gb NAND flash ( NAND01GW3B2A) instead of 256Mb flash present
on reference board.

In U-boot I have changed the board configuration file to reflect correct specs.

 * Now the NAND chip has to be defined (no autodetection used!)
 */
#define CFG_NAND_PAGE_SIZE	(2048)		/* NAND chip page size		*/
#define CFG_NAND_BLOCK_SIZE	(128 << 10)	/* NAND chip block size		*/
#define CFG_NAND_PAGE_COUNT	(64)		/* NAND chip page count		*/
#define CFG_NAND_BAD_BLOCK_POS	(5)		/* Location of bad block marker	*/
#define CFG_NAND_4_ADDR_CYCLE			/* Fourth addr used (>32MB)	*/

I have also changed the eeprom configuration word to
0x87788252
0x0947D010
0xA0A82358
0x0D050000

Is there any other place where some NAND configuration is defined?

Thanks
Nikhil

^ permalink raw reply

* Reorg of 32-bit dma code
From: Becky Bruce @ 2008-02-07  0:32 UTC (permalink / raw)
  To: linuxppc-dev

Guys,

I've been looking at converting 32-bit powerpc's DMA code over to the 
64-bit method, where there is a dma_ops structure inside archdata
that tells us which operations a device should use for DMA.  I'll be 
needing this shortly because I need to implement swiotlb to deal with 
PCI and large amounts of RAM on 32-bit systems that support 36-bit
physical addressing. (Yes, I know.  Fun for me.  Woohoo.)

Anyway, I have an initial booting first pass, and wanted to get some 
feedback.  What I've done at this point is to make dma_64 common to both 
architectures (will rename it when I send a real patch...).  The 
dma_direct_* functions have been changed to work on both 32/64, and the 
old dma_* functionality in dma-mapping.h has been removed.   For now, to 
avoid whacking on every 32-bit platform, the get_dma_ops() function has 
been changed to return &dma_direct_ops if the device pointer exists but 
the dma_ops field is NULL.   I'm not sure if this needs to be ifdef'd for 
64-bit?

I've copied a bit of code over from pci_64.c into pci_common.c - some of 
it isn't in use yet but will be once I start doing actual setup in the 
platform code. pcibios_setup_new_device() becomes common as well.  I've 
also temporarily hacked the 32-bit code to set archdata.dma_data to 
PCI_DRAM_OFFSET, so we can eliminate the use of virt_to_bus() and instead 
use the 64-bit method, which gets rid of some ugly ifdefs in the dma code.  

That's really about it - the preliminary patch is below - clearly it will 
need some cleanup, but I wanted to post early and often.  Any feedback or 
suggestions on cleaning this up are greatly appreciated. 

Thanks!
-Becky

-----------

diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index ebf8d16..34a6a82 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -55,10 +55,10 @@ extra-$(CONFIG_8xx)		:= head_8xx.o
 extra-y				+= vmlinux.lds
 
 obj-y				+= time.o prom.o traps.o setup-common.o \
-				   udbg.o misc.o io.o \
+				   udbg.o misc.o io.o dma_64.o\
 				   misc_$(CONFIG_WORD_SIZE).o
 obj-$(CONFIG_PPC32)		+= entry_32.o setup_32.o
-obj-$(CONFIG_PPC64)		+= dma_64.o iommu.o
+obj-$(CONFIG_PPC64)		+= iommu.o
 obj-$(CONFIG_PPC_MULTIPLATFORM)	+= prom_init.o
 obj-$(CONFIG_MODULES)		+= ppc_ksyms.o
 obj-$(CONFIG_BOOTX_TEXT)	+= btext.o
diff --git a/arch/powerpc/kernel/dma.c b/arch/powerpc/kernel/dma_64.c
index 8423907..0199a3b 100644
--- a/arch/powerpc/kernel/dma_64.c
+++ b/arch/powerpc/kernel/dma_64.c
@@ -8,9 +8,11 @@
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
 #include <asm/bug.h>
-#include <asm/iommu.h>
 #include <asm/abs_addr.h>
 
+#ifdef CONFIG_PPC64
+#include <asm/iommu.h>
+
 /*
  * Generic iommu implementation
  */
@@ -108,6 +110,8 @@ struct dma_mapping_ops dma_iommu_ops = {
 	.dma_supported	= dma_iommu_dma_supported,
 };
 EXPORT_SYMBOL(dma_iommu_ops);
+#endif /* CONFIG_PPC64 */
+
 
 /*
  * Generic direct DMA implementation
@@ -126,14 +130,28 @@ static unsigned long get_dma_direct_offset(struct device *dev)
 static void *dma_direct_alloc_coherent(struct device *dev, size_t size,
 				       dma_addr_t *dma_handle, gfp_t flag)
 {
-	struct page *page;
 	void *ret;
+
+#ifdef CONFIG_PPC64
+	struct page *page;
 	int node = dev->archdata.numa_node;
 
 	page = alloc_pages_node(node, flag, get_order(size));
 	if (page == NULL)
 		return NULL;
 	ret = page_address(page);
+#else
+	/* ignore region specifiers */
+	flag  &= ~(__GFP_DMA | __GFP_HIGHMEM);
+
+	if (dev == NULL || dev->coherent_dma_mask < 0xffffffff)
+		flag |= GFP_DMA;
+
+	ret = (void *)__get_free_pages(flag, get_order(size));
+	if (ret == NULL) 
+		return NULL;
+#endif
+
 	memset(ret, 0, size);
 	*dma_handle = virt_to_abs(ret) + get_dma_direct_offset(dev);
 
@@ -146,10 +164,29 @@ static void dma_direct_free_coherent(struct device *dev, size_t size,
 	free_pages((unsigned long)vaddr, get_order(size));
 }
 
+/* 
+ * Version of dma_direct_[alloc/free]_coherent for non-coherent devices or
+ * processors. 
+ */
+#ifdef CONFIG_NOT_COHERENT_CACHE
+static void *dma_direct_alloc_noncoherent(struct device *dev, size_t size,
+				       dma_addr_t *dma_handle, gfp_t flag)
+{
+	return __dma_alloc_coherent(size, dma_handle, flag);
+}
+
+static void dma_direct_free_noncoherent(struct device *dev, size_t size,
+				     void *vaddr, dma_addr_t dma_handle)
+{
+	__dma_free_coherent(size, vaddr);
+}
+#endif
+
 static dma_addr_t dma_direct_map_single(struct device *dev, void *ptr,
 					size_t size,
 					enum dma_data_direction direction)
 {
+	__dma_sync(ptr, size, direction);
 	return virt_to_abs(ptr) + get_dma_direct_offset(dev);
 }
 
@@ -180,16 +217,25 @@ static void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sg,
 
 static int dma_direct_dma_supported(struct device *dev, u64 mask)
 {
+#ifdef CONFIG_PPC64
 	/* Could be improved to check for memory though it better be
 	 * done via some global so platforms can set the limit in case
 	 * they have limited DMA windows
 	 */
 	return mask >= DMA_32BIT_MASK;
+#else
+	return 1;
+#endif
 }
 
 struct dma_mapping_ops dma_direct_ops = {
+#ifdef CONFIG_NOT_COHERENT_CACHE
+	.alloc_coherent	= dma_direct_alloc_noncoherent,
+	.free_coherent	= dma_direct_free_noncoherent,
+#else
 	.alloc_coherent	= dma_direct_alloc_coherent,
 	.free_coherent	= dma_direct_free_coherent,
+#endif
 	.map_single	= dma_direct_map_single,
 	.unmap_single	= dma_direct_unmap_single,
 	.map_sg		= dma_direct_map_sg,
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 980fe32..35d5b87 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -56,6 +56,34 @@ resource_size_t isa_mem_base;
 /* Default PCI flags is 0 */
 unsigned int ppc_pci_flags;
 
+static struct dma_mapping_ops *pci_dma_ops;
+
+void set_pci_dma_ops(struct dma_mapping_ops *dma_ops)
+{
+	pci_dma_ops = dma_ops;
+}
+
+struct dma_mapping_ops *get_pci_dma_ops(void)
+{
+	return pci_dma_ops;
+}
+EXPORT_SYMBOL(get_pci_dma_ops);
+
+int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
+{
+	return dma_set_mask(&dev->dev, mask);
+}
+
+int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
+{
+	int rc;
+
+	rc = dma_set_mask(&dev->dev, mask);
+	dev->dev.coherent_dma_mask = dev->dma_mask;
+
+	return rc;
+}
+
 struct pci_controller *pcibios_alloc_controller(struct device_node *dev)
 {
 	struct pci_controller *phb;
@@ -180,6 +208,36 @@ char __devinit *pcibios_setup(char *str)
 	return str;
 }
 
+void __devinit pcibios_setup_new_device(struct pci_dev *dev)
+{
+	struct dev_archdata *sd = &dev->dev.archdata;
+
+	sd->of_node = pci_device_to_OF_node(dev);
+
+	DBG("PCI: device %s OF node: %s\n", pci_name(dev),
+	    sd->of_node ? sd->of_node->full_name : "<none>");
+
+	sd->dma_ops = pci_dma_ops;
+#ifdef CONFIG_PPC32
+	/* Hack for now until the platforms do setup correctly.
+	 * This value is *only* good in the generic dma_direct code,
+	 * setting of this needs to be done by plat code based on which
+	 * set of dma_ops it has.  Since we only currently use
+	 * dma_direct_ops on ppc32, this works for now.
+	 */
+	sd->dma_data = (void *)PCI_DRAM_OFFSET;
+#endif
+
+#ifdef CONFIG_NUMA
+	sd->numa_node = pcibus_to_node(dev->bus);
+#else
+	sd->numa_node = -1;
+#endif
+	if (ppc_md.pci_dma_dev_setup)
+		ppc_md.pci_dma_dev_setup(dev);
+}
+EXPORT_SYMBOL(pcibios_setup_new_device);
+
 /*
  * Reads the interrupt pin to determine if interrupt is use by card.
  * If the interrupt is used, then gets the interrupt line from the
diff --git a/arch/powerpc/kernel/pci_32.c b/arch/powerpc/kernel/pci_32.c
index 88db4ff..174b77e 100644
--- a/arch/powerpc/kernel/pci_32.c
+++ b/arch/powerpc/kernel/pci_32.c
@@ -424,6 +424,7 @@ void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
 	unsigned long io_offset;
 	struct resource *res;
 	int i;
+	struct pci_dev *dev;
 
 	/* Hookup PHB resources */
 	io_offset = (unsigned long)hose->io_base_virt - isa_io_base;
@@ -457,6 +458,12 @@ void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
 			bus->resource[i+1] = res;
 		}
 	}
+
+	if (ppc_md.pci_dma_bus_setup)
+		ppc_md.pci_dma_bus_setup(bus);
+
+	list_for_each_entry(dev, &bus->devices, bus_list)
+		pcibios_setup_new_device(dev);
 }
 
 /* the next one is stolen from the alpha port... */
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c
index 5275074..2a5487c 100644
--- a/arch/powerpc/kernel/pci_64.c
+++ b/arch/powerpc/kernel/pci_64.c
@@ -52,35 +52,6 @@ EXPORT_SYMBOL(pci_io_base);
 
 LIST_HEAD(hose_list);
 
-static struct dma_mapping_ops *pci_dma_ops;
-
-void set_pci_dma_ops(struct dma_mapping_ops *dma_ops)
-{
-	pci_dma_ops = dma_ops;
-}
-
-struct dma_mapping_ops *get_pci_dma_ops(void)
-{
-	return pci_dma_ops;
-}
-EXPORT_SYMBOL(get_pci_dma_ops);
-
-
-int pci_set_dma_mask(struct pci_dev *dev, u64 mask)
-{
-	return dma_set_mask(&dev->dev, mask);
-}
-
-int pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask)
-{
-	int rc;
-
-	rc = dma_set_mask(&dev->dev, mask);
-	dev->dev.coherent_dma_mask = dev->dma_mask;
-
-	return rc;
-}
-
 static void fixup_broken_pcnet32(struct pci_dev* dev)
 {
 	if ((dev->class>>8 == PCI_CLASS_NETWORK_ETHERNET)) {
@@ -548,26 +519,6 @@ int __devinit pcibios_map_io_space(struct pci_bus *bus)
 }
 EXPORT_SYMBOL_GPL(pcibios_map_io_space);
 
-void __devinit pcibios_setup_new_device(struct pci_dev *dev)
-{
-	struct dev_archdata *sd = &dev->dev.archdata;
-
-	sd->of_node = pci_device_to_OF_node(dev);
-
-	DBG("PCI: device %s OF node: %s\n", pci_name(dev),
-	    sd->of_node ? sd->of_node->full_name : "<none>");
-
-	sd->dma_ops = pci_dma_ops;
-#ifdef CONFIG_NUMA
-	sd->numa_node = pcibus_to_node(dev->bus);
-#else
-	sd->numa_node = -1;
-#endif
-	if (ppc_md.pci_dma_dev_setup)
-		ppc_md.pci_dma_dev_setup(dev);
-}
-EXPORT_SYMBOL(pcibios_setup_new_device);
-
 void __devinit pcibios_do_bus_setup(struct pci_bus *bus)
 {
 	struct pci_dev *dev;
diff --git a/include/asm-powerpc/dma-mapping.h b/include/asm-powerpc/dma-mapping.h
index bbefb69..b177bac 100644
--- a/include/asm-powerpc/dma-mapping.h
+++ b/include/asm-powerpc/dma-mapping.h
@@ -43,9 +43,10 @@ extern void __dma_sync_page(struct page *page, unsigned long offset,
 
 #endif /* ! CONFIG_NOT_COHERENT_CACHE */
 
-#ifdef CONFIG_PPC64
+extern struct dma_mapping_ops dma_direct_ops;
+
 /*
- * DMA operations are abstracted for G5 vs. i/pSeries, PCI vs. VIO
+ * DMA operations are abstracted for G5 vs. i/pSeries, PCI vs. VIO, etc.
  */
 struct dma_mapping_ops {
 	void *		(*alloc_coherent)(struct device *dev, size_t size,
@@ -71,8 +72,18 @@ static inline struct dma_mapping_ops *get_dma_ops(struct device *dev)
 	 * only ISA DMA device we support is the floppy and we have a hack
 	 * in the floppy driver directly to get a device for us.
 	 */
-	if (unlikely(dev == NULL || dev->archdata.dma_ops == NULL))
+	if (unlikely(dev == NULL))
 		return NULL;
+
+	/* Some legacy devices don't support archdata dma ops. 
+	 * Assume those devices can use dma_direct_ops.
+	 * This also serves as a default for 32-bit platforms
+	 * on which all devices use the direct ops and so we haven't
+	 * set up the archdata dma_ops.
+	 */
+	if (dev->archdata.dma_ops == NULL)
+		return &dma_direct_ops;
+
 	return dev->archdata.dma_ops;
 }
 
@@ -192,121 +203,6 @@ static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
  * Available generic sets of operations
  */
 extern struct dma_mapping_ops dma_iommu_ops;
-extern struct dma_mapping_ops dma_direct_ops;
-
-#else /* CONFIG_PPC64 */
-
-#define dma_supported(dev, mask)	(1)
-
-static inline int dma_set_mask(struct device *dev, u64 dma_mask)
-{
-	if (!dev->dma_mask || !dma_supported(dev, mask))
-		return -EIO;
-
-	*dev->dma_mask = dma_mask;
-
-	return 0;
-}
-
-static inline void *dma_alloc_coherent(struct device *dev, size_t size,
-				       dma_addr_t * dma_handle,
-				       gfp_t gfp)
-{
-#ifdef CONFIG_NOT_COHERENT_CACHE
-	return __dma_alloc_coherent(size, dma_handle, gfp);
-#else
-	void *ret;
-	/* ignore region specifiers */
-	gfp &= ~(__GFP_DMA | __GFP_HIGHMEM);
-
-	if (dev == NULL || dev->coherent_dma_mask < 0xffffffff)
-		gfp |= GFP_DMA;
-
-	ret = (void *)__get_free_pages(gfp, get_order(size));
-
-	if (ret != NULL) {
-		memset(ret, 0, size);
-		*dma_handle = virt_to_bus(ret);
-	}
-
-	return ret;
-#endif
-}
-
-static inline void
-dma_free_coherent(struct device *dev, size_t size, void *vaddr,
-		  dma_addr_t dma_handle)
-{
-#ifdef CONFIG_NOT_COHERENT_CACHE
-	__dma_free_coherent(size, vaddr);
-#else
-	free_pages((unsigned long)vaddr, get_order(size));
-#endif
-}
-
-static inline dma_addr_t
-dma_map_single(struct device *dev, void *ptr, size_t size,
-	       enum dma_data_direction direction)
-{
-	BUG_ON(direction == DMA_NONE);
-
-	__dma_sync(ptr, size, direction);
-
-	return virt_to_bus(ptr);
-}
-
-static inline void dma_unmap_single(struct device *dev, dma_addr_t dma_addr,
-				    size_t size,
-				    enum dma_data_direction direction)
-{
-	/* We do nothing. */
-}
-
-static inline dma_addr_t
-dma_map_page(struct device *dev, struct page *page,
-	     unsigned long offset, size_t size,
-	     enum dma_data_direction direction)
-{
-	BUG_ON(direction == DMA_NONE);
-
-	__dma_sync_page(page, offset, size, direction);
-
-	return page_to_bus(page) + offset;
-}
-
-static inline void dma_unmap_page(struct device *dev, dma_addr_t dma_address,
-				  size_t size,
-				  enum dma_data_direction direction)
-{
-	/* We do nothing. */
-}
-
-static inline int
-dma_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
-	   enum dma_data_direction direction)
-{
-	struct scatterlist *sg;
-	int i;
-
-	BUG_ON(direction == DMA_NONE);
-
-	for_each_sg(sgl, sg, nents, i) {
-		BUG_ON(!sg_page(sg));
-		__dma_sync_page(sg_page(sg), sg->offset, sg->length, direction);
-		sg->dma_address = page_to_bus(sg_page(sg)) + sg->offset;
-	}
-
-	return nents;
-}
-
-static inline void dma_unmap_sg(struct device *dev, struct scatterlist *sg,
-				int nhwentries,
-				enum dma_data_direction direction)
-{
-	/* We don't do anything here. */
-}
-
-#endif /* CONFIG_PPC64 */
 
 static inline void dma_sync_single_for_cpu(struct device *dev,
 		dma_addr_t dma_handle, size_t size,
diff --git a/include/asm-powerpc/machdep.h b/include/asm-powerpc/machdep.h
index 0872ec2..7652495 100644
--- a/include/asm-powerpc/machdep.h
+++ b/include/asm-powerpc/machdep.h
@@ -85,8 +85,6 @@ struct machdep_calls {
 	unsigned long	(*tce_get)(struct iommu_table *tbl,
 				    long index);
 	void		(*tce_flush)(struct iommu_table *tbl);
-	void		(*pci_dma_dev_setup)(struct pci_dev *dev);
-	void		(*pci_dma_bus_setup)(struct pci_bus *bus);
 
 	void __iomem *	(*ioremap)(phys_addr_t addr, unsigned long size,
 				   unsigned long flags);
@@ -98,6 +96,9 @@ struct machdep_calls {
 #endif
 #endif /* CONFIG_PPC64 */
 
+	void		(*pci_dma_dev_setup)(struct pci_dev *dev);
+	void		(*pci_dma_bus_setup)(struct pci_bus *bus);
+
 	int		(*probe)(void);
 	void		(*setup_arch)(void); /* Optional, may be NULL */
 	void		(*init_early)(void);
diff --git a/include/asm-powerpc/pci.h b/include/asm-powerpc/pci.h
index a05a942..0e52c78 100644
--- a/include/asm-powerpc/pci.h
+++ b/include/asm-powerpc/pci.h
@@ -60,6 +60,14 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 	return channel ? 15 : 14;
 }
 
+#ifdef CONFIG_PCI
+extern void set_pci_dma_ops(struct dma_mapping_ops *dma_ops);
+extern struct dma_mapping_ops *get_pci_dma_ops(void);
+#else	/* CONFIG_PCI */
+#define set_pci_dma_ops(d)
+#define get_pci_dma_ops()	NULL
+#endif
+
 #ifdef CONFIG_PPC64
 
 /*
@@ -70,9 +78,6 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel)
 #define PCI_DISABLE_MWI
 
 #ifdef CONFIG_PCI
-extern void set_pci_dma_ops(struct dma_mapping_ops *dma_ops);
-extern struct dma_mapping_ops *get_pci_dma_ops(void);
-
 static inline void pci_dma_burst_advice(struct pci_dev *pdev,
 					enum pci_dma_burst_strategy *strat,
 					unsigned long *strategy_parameter)
@@ -89,9 +94,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev,
 	*strat = PCI_DMA_BURST_MULTIPLE;
 	*strategy_parameter = cacheline_size;
 }
-#else	/* CONFIG_PCI */
-#define set_pci_dma_ops(d)
-#define get_pci_dma_ops()	NULL
 #endif
 
 #else /* 32-bit */

^ permalink raw reply related

* Re: [PATCH 2/8] pseries: phyp dump: reserve-release proof-of-concept
From: Paul Mackerras @ 2008-02-07  0:42 UTC (permalink / raw)
  To: Manish Ahuja; +Cc: ppc-dev, linasvepstas, Larry Kessler, Michael Strosaker
In-Reply-To: <47964422.7080505@austin.ibm.com>

Manish Ahuja writes:

> Initial patch for reserving memory in early boot, and freeing it later.
> If the previous boot had ended with a crash, the reserved memory would contain
> a copy of the crashed kernel data.

[snip]

> +static void __init reserve_crashed_mem(void)
> +{
> +	unsigned long base, size;
> +
> +	if (phyp_dump_info->phyp_dump_is_active) {
> +		/* Reserve *everything* above RMR. We'll free this real soon.*/
> +		base = PHYP_DUMP_RMR_END;
> +		size = lmb_end_of_DRAM() - base;
> +
> +		/* XXX crashed_ram_end is wrong, since it may be beyond
> +	 	* the memory_limit, it will need to be adjusted. */
> +		lmb_reserve(base, size);
> +
> +		phyp_dump_info->init_reserve_start = base;
> +		phyp_dump_info->init_reserve_size = size;
> +	}
> +	else {
> +		size = phyp_dump_info->cpu_state_size +
> +			phyp_dump_info->hpte_region_size +
> +			PHYP_DUMP_RMR_END;
> +		base = lmb_end_of_DRAM() - size;
> +	printk(KERN_ERR "Manish reserve regular kernel space is %ld %ld\n", base, size);
> +		lmb_reserve(base, size);

This is still reserving memory even on systems that aren't running on
pHyp at all.  Please rework this so that no memory is reserved if the
system doesn't support phyp-assisted dump.

Paul.

^ permalink raw reply

* Re: Reorg of 32-bit dma code
From: Benjamin Herrenschmidt @ 2008-02-07  1:26 UTC (permalink / raw)
  To: Becky Bruce; +Cc: linuxppc-dev
In-Reply-To: <Pine.LNX.4.64.0802061802310.1616@monty.am.freescale.net>


On Wed, 2008-02-06 at 18:32 -0600, Becky Bruce wrote:
> 
> I've been looking at converting 32-bit powerpc's DMA code over to the 
> 64-bit method, where there is a dma_ops structure inside archdata
> that tells us which operations a device should use for DMA.  I'll be 
> needing this shortly because I need to implement swiotlb to deal with 
> PCI and large amounts of RAM on 32-bit systems that support 36-bit
> physical addressing. (Yes, I know.  Fun for me.  Woohoo.)
> 
> Anyway, I have an initial booting first pass, and wanted to get some 
> feedback.  What I've done at this point is to make dma_64 common to both 
> architectures (will rename it when I send a real patch...).  The 
> dma_direct_* functions have been changed to work on both 32/64, and the 
> old dma_* functionality in dma-mapping.h has been removed.   For now, to 
> avoid whacking on every 32-bit platform, the get_dma_ops() function has 
> been changed to return &dma_direct_ops if the device pointer exists but 
> the dma_ops field is NULL.   I'm not sure if this needs to be ifdef'd for 
> 64-bit?

I'd prefer so yes.

> I've copied a bit of code over from pci_64.c into pci_common.c - some of 
> it isn't in use yet but will be once I start doing actual setup in the 
> platform code. pcibios_setup_new_device() becomes common as well.

Cool !

>   I've 
> also temporarily hacked the 32-bit code to set archdata.dma_data to 
> PCI_DRAM_OFFSET, so we can eliminate the use of virt_to_bus() and instead 
> use the 64-bit method, which gets rid of some ugly ifdefs in the dma code.  

Ok.

> That's really about it - the preliminary patch is below - clearly it will 
> need some cleanup, but I wanted to post early and often.  Any feedback or 
> suggestions on cleaning this up are greatly appreciated. 

I'll have a look asap, thanks for doing this !

Ben.

^ permalink raw reply

* Re: Reorg of 32-bit dma code
From: Kumar Gala @ 2008-02-07  2:39 UTC (permalink / raw)
  To: benh; +Cc: linuxppc-dev
In-Reply-To: <1202347594.7079.107.camel@pasglop>


On Feb 6, 2008, at 7:26 PM, Benjamin Herrenschmidt wrote:

>
> On Wed, 2008-02-06 at 18:32 -0600, Becky Bruce wrote:
>>
>> I've been looking at converting 32-bit powerpc's DMA code over to the
>> 64-bit method, where there is a dma_ops structure inside archdata
>> that tells us which operations a device should use for DMA.  I'll be
>> needing this shortly because I need to implement swiotlb to deal with
>> PCI and large amounts of RAM on 32-bit systems that support 36-bit
>> physical addressing. (Yes, I know.  Fun for me.  Woohoo.)
>>
>> Anyway, I have an initial booting first pass, and wanted to get some
>> feedback.  What I've done at this point is to make dma_64 common to  
>> both
>> architectures (will rename it when I send a real patch...).  The
>> dma_direct_* functions have been changed to work on both 32/64, and  
>> the
>> old dma_* functionality in dma-mapping.h has been removed.   For  
>> now, to
>> avoid whacking on every 32-bit platform, the get_dma_ops() function  
>> has
>> been changed to return &dma_direct_ops if the device pointer exists  
>> but
>> the dma_ops field is NULL.   I'm not sure if this needs to be  
>> ifdef'd for
>> 64-bit?
>
> I'd prefer so yes.

Can you explain why?  Got no issue with doing this, but its good to  
know hy.

>> I've copied a bit of code over from pci_64.c into pci_common.c -  
>> some of
>> it isn't in use yet but will be once I start doing actual setup in  
>> the
>> platform code. pcibios_setup_new_device() becomes common as well.
>
> Cool !
>
>>  I've
>> also temporarily hacked the 32-bit code to set archdata.dma_data to
>> PCI_DRAM_OFFSET, so we can eliminate the use of virt_to_bus() and  
>> instead
>> use the 64-bit method, which gets rid of some ugly ifdefs in the  
>> dma code.
>
> Ok.
>
>> That's really about it - the preliminary patch is below - clearly  
>> it will
>> need some cleanup, but I wanted to post early and often.  Any  
>> feedback or
>> suggestions on cleaning this up are greatly appreciated.
>
> I'll have a look asap, thanks for doing this !

Also, any ideas on how to handle setting dev->archdata for non-PCI  
devices would be welcome.

- k

^ permalink raw reply

* Re: Reorg of 32-bit dma code
From: Becky Bruce @ 2008-02-07  2:45 UTC (permalink / raw)
  To: benh; +Cc: linuxppc-dev
In-Reply-To: <1202347594.7079.107.camel@pasglop>

On Feb 6, 2008, at 7:26 PM, Benjamin Herrenschmidt wrote:

>
> On Wed, 2008-02-06 at 18:32 -0600, Becky Bruce wrote:
>>
>> I've been looking at converting 32-bit powerpc's DMA code over to the
>> 64-bit method, where there is a dma_ops structure inside archdata
>> that tells us which operations a device should use for DMA.  I'll be
>> needing this shortly because I need to implement swiotlb to deal with
>> PCI and large amounts of RAM on 32-bit systems that support 36-bit
>> physical addressing. (Yes, I know.  Fun for me.  Woohoo.)
>>
>> Anyway, I have an initial booting first pass, and wanted to get some
>> feedback.  What I've done at this point is to make dma_64 common  
>> to both
>> architectures (will rename it when I send a real patch...).  The
>> dma_direct_* functions have been changed to work on both 32/64,  
>> and the
>> old dma_* functionality in dma-mapping.h has been removed.   For  
>> now, to
>> avoid whacking on every 32-bit platform, the get_dma_ops()  
>> function has
>> been changed to return &dma_direct_ops if the device pointer  
>> exists but
>> the dma_ops field is NULL.   I'm not sure if this needs to be  
>> ifdef'd for
>> 64-bit?
>
> I'd prefer so yes.

Ok. I'll put that in, but I'd like to understand why it's necessary....

>
>> I've copied a bit of code over from pci_64.c into pci_common.c -  
>> some of
>> it isn't in use yet but will be once I start doing actual setup in  
>> the
>> platform code. pcibios_setup_new_device() becomes common as well.
>
> Cool !
>
>>   I've
>> also temporarily hacked the 32-bit code to set archdata.dma_data to
>> PCI_DRAM_OFFSET, so we can eliminate the use of virt_to_bus() and  
>> instead
>> use the 64-bit method, which gets rid of some ugly ifdefs in the  
>> dma code.
>
> Ok.
>
>> That's really about it - the preliminary patch is below - clearly  
>> it will
>> need some cleanup, but I wanted to post early and often.  Any  
>> feedback or
>> suggestions on cleaning this up are greatly appreciated.
>
> I'll have a look asap, thanks for doing this !
>

Heh, don't thank me until you've looked at it :)

Another thing that has to be done here is that I will need to audit  
all the driver code for places where the dev pointer passed to  
dma_alloc_coherent() and friends is NULL.  I've already fixed the  
gianfar driver locally, but I expect there are a pile of them.  If  
you have any other suggestions for dealing with this, speak up.

Cheers,
B

^ permalink raw reply

* Re: Reorg of 32-bit dma code
From: Benjamin Herrenschmidt @ 2008-02-07  2:45 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <77A19FBD-7861-4D32-84BA-BDEEC39A4982@kernel.crashing.org>


On Wed, 2008-02-06 at 20:39 -0600, Kumar Gala wrote:
> > I'd prefer so yes.
> 
> Can you explain why?  Got no issue with doing this, but its good to  
> know hy.

Just in case ... let's not modify 64 bits behaviour especially to
something that will ultimately go away. I prefer failing if DMA ops are
not set rather than using a set of ops that will cause DMA to the wrong
place.
 
> Also, any ideas on how to handle setting dev->archdata for non-PCI  
> devices would be welcome.

This has to be done by whoever creates the struct device. Can be a bit
annoying with platform devices, I agree. One way to do it is to use the
bus notifiers mechanism, which allows you for example to have a default
for any platform device.

Ben.

^ permalink raw reply

* Re: [PATCH] net: NEWEMAC: Remove "rgmii-interface" from rgmii matching table
From: Benjamin Herrenschmidt @ 2008-02-07  2:47 UTC (permalink / raw)
  To: Olof Johansson; +Cc: linuxppc-dev, Stefan Roese, jeff, netdev
In-Reply-To: <20080206163554.GA18575@lixom.net>


On Wed, 2008-02-06 at 10:35 -0600, Olof Johansson wrote:
> On Wed, Feb 06, 2008 at 10:23:40AM -0600, Josh Boyer wrote:
> > On Wed, 6 Feb 2008 10:01:57 -0600
> > Olof Johansson <olof@lixom.net> wrote:
> > 
> > > On Wed, Feb 06, 2008 at 01:21:59PM +0100, Stefan Roese wrote:
> > > > With the removal the the "rgmii-interface" device_type property from the
> > > > dts files, the newemac driver needs an update to only rely on compatible
> > > > property.
> > > 
> > > What about systems using an older dts, such as one kexec:ing from an
> > > older kernel?
> > 
> > Like what?  Kexec doesn't work on 4xx yet.
> 
> Sure, but similar restrictions would apply for customers who have a
> flashed device tree that want to boot a newer kernel. Deprecating old
> device tree support is something that should be done very carefully.

In that case, we -know- there was no such thing as the proper support
for 4xx in powerpc isn't really there before 2.6.25 anyway. The only
other is Cell/Axon which we verified still works.

Ben.

^ 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