LinuxPPC-Dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 6/21] [POWERPC] PCI support for 4xx Ebony board
From: Benjamin Herrenschmidt @ 2007-12-21  4:39 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This wires up the 4xx PCI support & device tree bits for
440GP based Ebony platform.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 arch/powerpc/boot/dts/ebony.dts    |   41 ++++++++++++++++++++++++++++++++-----
 arch/powerpc/platforms/44x/Kconfig |    1 
 2 files changed, 37 insertions(+), 5 deletions(-)

--- linux-merge.orig/arch/powerpc/boot/dts/ebony.dts	2007-12-21 14:10:34.000000000 +1100
+++ linux-merge/arch/powerpc/boot/dts/ebony.dts	2007-12-21 14:11:51.000000000 +1100
@@ -284,12 +284,43 @@
 
 		};
 
-		PCIX0: pci@1234 {
+		PCIX0: pci@20ec00000 {
 			device_type = "pci";
-			/* FIXME */
-			reg = <2 0ec00000 8
-			       2 0ec80000 f0
-			       2 0ec80100 fc>;
+			#interrupt-cells = <1>;
+			#size-cells = <2>;
+			#address-cells = <3>;
+			compatible = "ibm,plb440gp-pcix", "ibm,plb-pcix";
+			primary;
+			reg = <2 0ec00000 8	/* Config space access */
+			       0 0 0		/* no IACK cycles */
+			       2 0ed00000 4     /* Special cycles */
+			       2 0ec80000 f0	/* Internal registers */
+			       2 0ec80100 fc>;	/* Internal messaging registers */
+
+			/* Outbound ranges, one memory and one IO,
+			 * later cannot be changed
+			 */
+			ranges = <02000000 0 80000000 00000003 80000000 0 80000000
+				  01000000 0 00000000 00000002 08000000 0 00010000>;
+
+			/* Inbound 2GB range starting at 0 */
+			dma-ranges = <42000000 0 0 0 0 0 80000000>;
+
+			/* Ebony has all 4 IRQ pins tied together per slot */
+			interrupt-map-mask = <f800 0 0 0>;
+			interrupt-map = <
+				/* IDSEL 1 */
+				0800 0 0 0 &UIC0 17 8
+
+				/* IDSEL 2 */
+				1000 0 0 0 &UIC0 18 8
+
+				/* IDSEL 3 */
+				1800 0 0 0 &UIC0 19 8
+
+				/* IDSEL 4 */
+				2000 0 0 0 &UIC0 1a 8
+			>;
 		};
 	};
 
Index: linux-merge/arch/powerpc/platforms/44x/Kconfig
===================================================================
--- linux-merge.orig/arch/powerpc/platforms/44x/Kconfig	2007-12-21 14:12:05.000000000 +1100
+++ linux-merge/arch/powerpc/platforms/44x/Kconfig	2007-12-21 14:12:09.000000000 +1100
@@ -11,6 +11,7 @@ config EBONY
 	depends on 44x
 	default y
 	select 440GP
+	select PCI
 	help
 	  This option enables support for the IBM PPC440GP evaluation board.
 

^ permalink raw reply

* [PATCH 5/21] [POWERPC] 4xx PLB to PCI Express support
From: Benjamin Herrenschmidt @ 2007-12-21  4:39 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds to the previous 2 patches the support for the 4xx PCI Express
cells as found in the 440SPe revA, revB and 405EX.

Unfortunately, due to significant differences between these, and other
interesting "features" of those pieces of HW, the code isn't as simple
as it is for PCI and PCI-X and some of the functions differ significantly
between the 3 implementations. Thus, not only this code can only support
those 3 implementations for now and will refuse to operate on any other,
but there are added ifdef's to avoid the bloat of building a fairly large
amount of code on platforms that don't need it.

Also, this code currently only supports fully initializing root complex
nodes, not endpoint. Some more code will have to be lifted from the
arch/ppc implementation to add the endpoint support, though it's mostly
differences in memory mapping, and the question on how to represent
endpoint mode PCI in the device-tree is thus open.

Many thanks to Stefan Roese for testing & fixing up the 405EX bits !

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Stefan Roese <sr@denx.de>
---

 arch/powerpc/Kconfig             |    1 
 arch/powerpc/sysdev/Kconfig      |    8 
 arch/powerpc/sysdev/ppc4xx_pci.c |  994 ++++++++++++++++++++++++++++++++++++++-
 arch/powerpc/sysdev/ppc4xx_pci.h |  242 +++++++++
 4 files changed, 1227 insertions(+), 18 deletions(-)

--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-14 15:49:43.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-14 15:49:43.000000000 +1100
@@ -3,16 +3,31 @@
  *
  * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
  *
+ * Most PCI Express code is coming from Stefan Roese implementation for
+ * arch/ppc in the Denx tree, slightly reworked by me.
+ *
+ * Copyright 2007 DENX Software Engineering, Stefan Roese <sr@denx.de>
+ *
+ * Some of that comes itself from a previous implementation for 440SPE only
+ * by Roland Dreier:
+ *
+ * Copyright (c) 2005 Cisco Systems.  All rights reserved.
+ * Roland Dreier <rolandd@cisco.com>
+ *
  */
 
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <linux/of.h>
+#include <linux/bootmem.h>
+#include <linux/delay.h>
 
 #include <asm/io.h>
 #include <asm/pci-bridge.h>
 #include <asm/machdep.h>
+#include <asm/dcr.h>
+#include <asm/dcr-regs.h>
 
 #include "ppc4xx_pci.h"
 
@@ -21,6 +36,17 @@ static int dma_offset_set;
 /* Move that to a useable header */
 extern unsigned long total_memory;
 
+#define U64_TO_U32_LOW(val)	((u32)((val) & 0x00000000ffffffffULL))
+#define U64_TO_U32_HIGH(val)	((u32)((val) >> 32))
+
+#ifdef CONFIG_RESOURCES_64BIT
+#define RES_TO_U32_LOW(val)	U64_TO_U32_LOW(val)
+#define RES_TO_U32_HIGH(val)	U64_TO_U32_HIGH(val)
+#else
+#define RES_TO_U32_LOW(val)	(val)
+#define RES_TO_U32_HIGH(val)	(0)
+#endif
+
 static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev)
 {
 	struct pci_controller *hose;
@@ -178,13 +204,8 @@ static void __init ppc4xx_configure_pci_
 
 		/* Calculate register values */
 		la = res->start;
-#ifdef CONFIG_RESOURCES_64BIT
-		pciha = (res->start - hose->pci_mem_offset) >> 32;
-		pcila = (res->start - hose->pci_mem_offset) & 0xffffffffu;
-#else
-		pciha = 0;
-		pcila = res->start - hose->pci_mem_offset;
-#endif
+		pciha = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
+		pcila = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
 
 		ma = res->end + 1 - res->start;
 		if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
@@ -333,16 +354,10 @@ static void __init ppc4xx_configure_pcix
 		}
 
 		/* Calculate register values */
-#ifdef CONFIG_RESOURCES_64BIT
-		lah = res->start >> 32;
-		lal = res->start & 0xffffffffu;
-		pciah = (res->start - hose->pci_mem_offset) >> 32;
-		pcial = (res->start - hose->pci_mem_offset) & 0xffffffffu;
-#else
-		lah = pciah = 0;
-		lal = res->start;
-		pcial = res->start - hose->pci_mem_offset;
-#endif
+		lah = RES_TO_U32_HIGH(res->start);
+		lal = RES_TO_U32_LOW(res->start);
+		pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
+		pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
 		sa = res->end + 1 - res->start;
 		if (!is_power_of_2(sa) || sa < 0x100000 ||
 		    sa > 0xffffffffu) {
@@ -492,20 +507,963 @@ static void __init ppc4xx_probe_pcix_bri
 		iounmap(reg);
 }
 
+#ifdef CONFIG_PPC4xx_PCI_EXPRESS
+
 /*
  * 4xx PCI-Express part
+ *
+ * We support 3 parts currently based on the compatible property:
+ *
+ * ibm,plb-pciex-440speA
+ * ibm,plb-pciex-440speB
+ * ibm,plb-pciex-405ex
+ *
+ * Anything else will be rejected for now as they are all subtly
+ * different unfortunately.
+ *
  */
+
+#define MAX_PCIE_BUS_MAPPED	0x10
+
+struct ppc4xx_pciex_port
+{
+	struct pci_controller	*hose;
+	struct device_node	*node;
+	unsigned int		index;
+	int			endpoint;
+	unsigned int		sdr_base;
+	dcr_host_t		dcrs;
+	struct resource		cfg_space;
+	struct resource		utl_regs;
+};
+
+static struct ppc4xx_pciex_port *ppc4xx_pciex_ports;
+static unsigned int ppc4xx_pciex_port_count;
+
+struct ppc4xx_pciex_hwops
+{
+	int (*core_init)(struct device_node *np);
+	int (*port_init_hw)(struct ppc4xx_pciex_port *port);
+	int (*setup_utl)(struct ppc4xx_pciex_port *port);
+};
+
+static struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops;
+
+#ifdef CONFIG_44x
+
+/* Check various reset bits of the 440SPe PCIe core */
+static int __init ppc440spe_pciex_check_reset(struct device_node *np)
+{
+	u32 valPE0, valPE1, valPE2;
+	int err = 0;
+
+	/* SDR0_PEGPLLLCT1 reset */
+	if (!(mfdcri(SDR0, PESDR0_PLLLCT1) & 0x01000000)) {
+		/*
+		 * the PCIe core was probably already initialised
+		 * by firmware - let's re-reset RCSSET regs
+		 *
+		 * -- Shouldn't we also re-reset the whole thing ? -- BenH
+		 */
+		pr_debug("PCIE: SDR0_PLLLCT1 already reset.\n");
+		mtdcri(SDR0, PESDR0_440SPE_RCSSET, 0x01010000);
+		mtdcri(SDR0, PESDR1_440SPE_RCSSET, 0x01010000);
+		mtdcri(SDR0, PESDR2_440SPE_RCSSET, 0x01010000);
+	}
+
+	valPE0 = mfdcri(SDR0, PESDR0_440SPE_RCSSET);
+	valPE1 = mfdcri(SDR0, PESDR1_440SPE_RCSSET);
+	valPE2 = mfdcri(SDR0, PESDR2_440SPE_RCSSET);
+
+	/* SDR0_PExRCSSET rstgu */
+	if (!(valPE0 & 0x01000000) ||
+	    !(valPE1 & 0x01000000) ||
+	    !(valPE2 & 0x01000000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstgu error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET rstdl */
+	if (!(valPE0 & 0x00010000) ||
+	    !(valPE1 & 0x00010000) ||
+	    !(valPE2 & 0x00010000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstdl error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET rstpyn */
+	if ((valPE0 & 0x00001000) ||
+	    (valPE1 & 0x00001000) ||
+	    (valPE2 & 0x00001000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstpyn error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET hldplb */
+	if ((valPE0 & 0x10000000) ||
+	    (valPE1 & 0x10000000) ||
+	    (valPE2 & 0x10000000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET hldplb error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET rdy */
+	if ((valPE0 & 0x00100000) ||
+	    (valPE1 & 0x00100000) ||
+	    (valPE2 & 0x00100000)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET rdy error\n");
+		err = -1;
+	}
+
+	/* SDR0_PExRCSSET shutdown */
+	if ((valPE0 & 0x00000100) ||
+	    (valPE1 & 0x00000100) ||
+	    (valPE2 & 0x00000100)) {
+		printk(KERN_INFO "PCIE: SDR0_PExRCSSET shutdown error\n");
+		err = -1;
+	}
+
+	return err;
+}
+
+/* Global PCIe core initializations for 440SPe core */
+static int __init ppc440spe_pciex_core_init(struct device_node *np)
+{
+	int time_out = 20;
+
+	/* Set PLL clock receiver to LVPECL */
+	mtdcri(SDR0, PESDR0_PLLLCT1, mfdcri(SDR0, PESDR0_PLLLCT1) | 1 << 28);
+
+	/* Shouldn't we do all the calibration stuff etc... here ? */
+	if (ppc440spe_pciex_check_reset(np))
+		return -ENXIO;
+
+	if (!(mfdcri(SDR0, PESDR0_PLLLCT2) & 0x10000)) {
+		printk(KERN_INFO "PCIE: PESDR_PLLCT2 resistance calibration "
+		       "failed (0x%08x)\n",
+		       mfdcri(SDR0, PESDR0_PLLLCT2));
+		return -1;
+	}
+
+	/* De-assert reset of PCIe PLL, wait for lock */
+	mtdcri(SDR0, PESDR0_PLLLCT1,
+	       mfdcri(SDR0, PESDR0_PLLLCT1) & ~(1 << 24));
+	udelay(3);
+
+	while (time_out) {
+		if (!(mfdcri(SDR0, PESDR0_PLLLCT3) & 0x10000000)) {
+			time_out--;
+			udelay(1);
+		} else
+			break;
+	}
+	if (!time_out) {
+		printk(KERN_INFO "PCIE: VCO output not locked\n");
+		return -1;
+	}
+
+	pr_debug("PCIE initialization OK\n");
+
+	return 3;
+}
+
+static int ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+	u32 val = 1 << 24;
+
+	if (port->endpoint)
+		val = PTYPE_LEGACY_ENDPOINT << 20;
+	else
+		val = PTYPE_ROOT_PORT << 20;
+
+	if (port->index == 0)
+		val |= LNKW_X8 << 12;
+	else
+		val |= LNKW_X4 << 12;
+
+	mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val);
+	mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x20222222);
+	if (of_device_is_compatible(port->node, "ibm,plb-pciex-440speA"))
+		mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x11000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL0SET1, 0x35000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL1SET1, 0x35000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL2SET1, 0x35000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL3SET1, 0x35000000);
+	if (port->index == 0) {
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL4SET1,
+		       0x35000000);
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL5SET1,
+		       0x35000000);
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL6SET1,
+		       0x35000000);
+		mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL7SET1,
+		       0x35000000);
+	}
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET);
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+	       (val & ~(1 << 24 | 1 << 16)) | 1 << 12);
+
+	return 0;
+}
+
+static int ppc440speA_pciex_init_utl(struct ppc4xx_pciex_port *port)
+{
+	void __iomem *utl_base;
+
+	/* XXX Check what that value means... I hate magic */
+	dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x68782800);
+
+	utl_base = ioremap(port->utl_regs.start, 0x100);
+	BUG_ON(utl_base == NULL);
+
+	/*
+	 * Set buffer allocations and then assert VRB and TXE.
+	 */
+	out_be32(utl_base + PEUTL_OUTTR,   0x08000000);
+	out_be32(utl_base + PEUTL_INTR,    0x02000000);
+	out_be32(utl_base + PEUTL_OPDBSZ,  0x10000000);
+	out_be32(utl_base + PEUTL_PBBSZ,   0x53000000);
+	out_be32(utl_base + PEUTL_IPHBSZ,  0x08000000);
+	out_be32(utl_base + PEUTL_IPDBSZ,  0x10000000);
+	out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000);
+	out_be32(utl_base + PEUTL_PCTL,    0x80800066);
+
+	iounmap(utl_base);
+
+	return 0;
+}
+
+static struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata =
+{
+	.core_init	= ppc440spe_pciex_core_init,
+	.port_init_hw	= ppc440spe_pciex_init_port_hw,
+	.setup_utl	= ppc440speA_pciex_init_utl,
+};
+
+static struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata =
+{
+	.core_init	= ppc440spe_pciex_core_init,
+	.port_init_hw	= ppc440spe_pciex_init_port_hw,
+};
+
+
+#endif /* CONFIG_44x */
+
+#ifdef CONFIG_40x
+
+static int __init ppc405ex_pciex_core_init(struct device_node *np)
+{
+	/* Nothing to do, return 2 ports */
+	return 2;
+}
+
+static void ppc405ex_pcie_phy_reset(struct ppc4xx_pciex_port *port)
+{
+	/* Assert the PE0_PHY reset */
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01010000);
+	msleep(1);
+
+	/* deassert the PE0_hotreset */
+	if (port->endpoint)
+		mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01111000);
+	else
+		mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01101000);
+
+	/* poll for phy !reset */
+	/* XXX FIXME add timeout */
+	while (!(mfdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSTA) & 0x00001000))
+		;
+
+	/* deassert the PE0_gpl_utl_reset */
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x00101000);
+}
+
+static int ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port)
+{
+	u32 val;
+
+	if (port->endpoint)
+		val = PTYPE_LEGACY_ENDPOINT;
+	else
+		val = PTYPE_ROOT_PORT;
+
+	mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET,
+	       1 << 24 | val << 20 | LNKW_X1 << 12);
+
+	mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x00000000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET1, 0x720F0000);
+	mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET2, 0x70600003);
+
+	/*
+	 * Only reset the PHY when no link is currently established.
+	 * This is for the Atheros PCIe board which has problems to establish
+	 * the link (again) after this PHY reset. All other currently tested
+	 * PCIe boards don't show this problem.
+	 * This has to be re-tested and fixed in a later release!
+	 */
+#if 0 /* XXX FIXME: Not resetting the PHY will leave all resources
+       * configured as done previously by U-Boot. Then Linux will currently
+       * not reassign them. So the PHY reset is now done always. This will
+       * lead to problems with the Atheros PCIe board again.
+       */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
+	if (!(val & 0x00001000))
+		ppc405ex_pcie_phy_reset(port);
+#else
+	ppc405ex_pcie_phy_reset(port);
+#endif
+
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, 0x10000000);  /* guarded on */
+
+	return 0;
+}
+
+static int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port)
+{
+	void __iomem *utl_base;
+
+	dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0);
+
+	utl_base = ioremap(port->utl_regs.start, 0x100);
+	BUG_ON(utl_base == NULL);
+
+	/*
+	 * Set buffer allocations and then assert VRB and TXE.
+	 */
+	out_be32(utl_base + PEUTL_OUTTR,   0x02000000);
+	out_be32(utl_base + PEUTL_INTR,    0x02000000);
+	out_be32(utl_base + PEUTL_OPDBSZ,  0x04000000);
+	out_be32(utl_base + PEUTL_PBBSZ,   0x21000000);
+	out_be32(utl_base + PEUTL_IPHBSZ,  0x02000000);
+	out_be32(utl_base + PEUTL_IPDBSZ,  0x04000000);
+	out_be32(utl_base + PEUTL_RCIRQEN, 0x00f00000);
+	out_be32(utl_base + PEUTL_PCTL,    0x80800066);
+
+	out_be32(utl_base + PEUTL_PBCTL,   0x0800000c);
+	out_be32(utl_base + PEUTL_RCSTA,
+		 in_be32(utl_base + PEUTL_RCSTA) | 0x000040000);
+
+	iounmap(utl_base);
+
+	return 0;
+}
+
+static struct ppc4xx_pciex_hwops ppc405ex_pcie_hwops __initdata =
+{
+	.core_init	= ppc405ex_pciex_core_init,
+	.port_init_hw	= ppc405ex_pciex_init_port_hw,
+	.setup_utl	= ppc405ex_pciex_init_utl,
+};
+
+#endif /* CONFIG_40x */
+
+
+/* Check that the core has been initied and if not, do it */
+static int __init ppc4xx_pciex_check_core_init(struct device_node *np)
+{
+	static int core_init;
+	int count = -ENODEV;
+
+	if (core_init++)
+		return 0;
+
+#ifdef CONFIG_44x
+	if (of_device_is_compatible(np, "ibm,plb-pciex-440speA"))
+		ppc4xx_pciex_hwops = &ppc440speA_pcie_hwops;
+	else if (of_device_is_compatible(np, "ibm,plb-pciex-440speB"))
+		ppc4xx_pciex_hwops = &ppc440speB_pcie_hwops;
+#endif /* CONFIG_44x    */
+#ifdef CONFIG_40x
+	if (of_device_is_compatible(np, "ibm,plb-pciex-405ex"))
+		ppc4xx_pciex_hwops = &ppc405ex_pcie_hwops;
+#endif
+	if (ppc4xx_pciex_hwops == NULL) {
+		printk(KERN_WARNING "PCIE: unknown host type %s\n",
+		       np->full_name);
+		return -ENODEV;
+	}
+
+	count = ppc4xx_pciex_hwops->core_init(np);
+	if (count > 0) {
+		ppc4xx_pciex_ports =
+		       kzalloc(count * sizeof(struct ppc4xx_pciex_port),
+			       GFP_KERNEL);
+		if (ppc4xx_pciex_ports) {
+			ppc4xx_pciex_port_count = count;
+			return 0;
+		}
+		printk(KERN_WARNING "PCIE: failed to allocate ports array\n");
+		return -ENOMEM;
+	}
+	return -ENODEV;
+}
+
+static void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port)
+{
+	/* We map PCI Express configuration based on the reg property */
+	dcr_write(port->dcrs, DCRO_PEGPL_CFGBAH,
+		  RES_TO_U32_HIGH(port->cfg_space.start));
+	dcr_write(port->dcrs, DCRO_PEGPL_CFGBAL,
+		  RES_TO_U32_LOW(port->cfg_space.start));
+
+	/* XXX FIXME: Use size from reg property. For now, map 512M */
+	dcr_write(port->dcrs, DCRO_PEGPL_CFGMSK, 0xe0000001);
+
+	/* We map UTL registers based on the reg property */
+	dcr_write(port->dcrs, DCRO_PEGPL_REGBAH,
+		  RES_TO_U32_HIGH(port->utl_regs.start));
+	dcr_write(port->dcrs, DCRO_PEGPL_REGBAL,
+		  RES_TO_U32_LOW(port->utl_regs.start));
+
+	/* XXX FIXME: Use size from reg property */
+	dcr_write(port->dcrs, DCRO_PEGPL_REGMSK, 0x00007001);
+
+	/* Disable all other outbound windows */
+	dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, 0);
+	dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, 0);
+	dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0);
+	dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0);
+}
+
+static int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port)
+{
+	int attempts, rc = 0;
+	u32 val;
+
+	/* Check if it's endpoint or root complex
+	 *
+	 * XXX Do we want to use the device-tree instead ? --BenH.
+	 */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_DLPSET);
+	port->endpoint = (((val >> 20) & 0xf) != PTYPE_ROOT_PORT);
+
+	/* Init HW */
+	if (ppc4xx_pciex_hwops->port_init_hw)
+		rc = ppc4xx_pciex_hwops->port_init_hw(port);
+	if (rc != 0)
+		return rc;
+
+	/*
+	 * Notice: the following delay has critical impact on device
+	 * initialization - if too short (<50ms) the link doesn't get up.
+	 *
+	 * XXX FIXME: There are various issues with that link up thingy,
+	 * we could just wait for the link with a timeout but Stefan says
+	 * some cards need more time even after the link is up. I'll
+	 * investigate. For now, we keep a fixed 1s delay.
+	 *
+	 * Ultimately, it should be made asynchronous so all ports are
+	 * brought up simultaneously though.
+	 */
+	printk(KERN_INFO "PCIE%d: Waiting for link to go up...\n",
+	       port->index);
+	msleep(1000);
+
+	/*
+	 * Check that we exited the reset state properly
+	 */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_RCSSTS);
+	if (val & (1 << 20)) {
+		printk(KERN_WARNING "PCIE%d: PGRST failed %08x\n",
+		       port->index, val);
+		return -1;
+	}
+
+	/*
+	 * Verify link is up
+	 */
+	val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP);
+	if (!(val & 0x00001000)) {
+		printk(KERN_INFO "PCIE%d: link is not up !\n",
+		       port->index);
+		return -1;
+	}
+
+	printk(KERN_INFO "PCIE%d: link is up !\n",
+	       port->index);
+
+	/*
+	 * Initialize mapping: disable all regions and configure
+	 * CFG and REG regions based on resources in the device tree
+	 */
+	ppc4xx_pciex_port_init_mapping(port);
+
+	/*
+	 * Setup UTL registers - but only on revA!
+	 * We use default settings for revB chip.
+	 *
+	 * To be reworked. We may also be able to move that to
+	 * before the link wait
+	 * --BenH.
+	 */
+	if (ppc4xx_pciex_hwops->setup_utl)
+		ppc4xx_pciex_hwops->setup_utl(port);
+
+	/*
+	 * Check for VC0 active and assert RDY.
+	 */
+	attempts = 10;
+	while (!(mfdcri(SDR0, port->sdr_base + PESDRn_RCSSTS) & (1 << 16))) {
+		if (!(attempts--)) {
+			printk(KERN_INFO "PCIE%d: VC0 not active\n",
+			       port->index);
+			return -1;
+		}
+		msleep(1000);
+	}
+	mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET,
+	       mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 1 << 20);
+	msleep(100);
+
+	return 0;
+}
+
+static int ppc4xx_pciex_validate_bdf(struct ppc4xx_pciex_port *port,
+				     struct pci_bus *bus,
+				     unsigned int devfn)
+{
+	static int message;
+
+	/* Endpoint can not generate upstream(remote) config cycles */
+	if (port->endpoint && bus->number != port->hose->first_busno)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/* Check we are within the mapped range */
+	if (bus->number > port->hose->last_busno) {
+		if (!message) {
+			printk(KERN_WARNING "Warning! Probing bus %u"
+			       " out of range !\n", bus->number);
+			message++;
+		}
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	/* The root complex has only one device / function */
+	if (bus->number == port->hose->first_busno && devfn != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/* The other side of the RC has only one device as well */
+	if (bus->number == (port->hose->first_busno + 1) &&
+	    PCI_SLOT(devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	return 0;
+}
+
+static void __iomem *ppc4xx_pciex_get_config_base(struct ppc4xx_pciex_port *port,
+						  struct pci_bus *bus,
+						  unsigned int devfn)
+{
+	int relbus;
+
+	/* Remove the casts when we finally remove the stupid volatile
+	 * in struct pci_controller
+	 */
+	if (bus->number == port->hose->first_busno)
+		return (void __iomem *)port->hose->cfg_addr;
+
+	relbus = bus->number - (port->hose->first_busno + 1);
+	return (void __iomem *)port->hose->cfg_data +
+		((relbus  << 20) | (devfn << 12));
+}
+
+static int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn,
+				    int offset, int len, u32 *val)
+{
+	struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+	struct ppc4xx_pciex_port *port =
+		&ppc4xx_pciex_ports[hose->indirect_type];
+	void __iomem *addr;
+	u32 gpl_cfg;
+
+	BUG_ON(hose != port->hose);
+
+	if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = ppc4xx_pciex_get_config_base(port, bus, devfn);
+
+	/*
+	 * Reading from configuration space of non-existing device can
+	 * generate transaction errors. For the read duration we suppress
+	 * assertion of machine check exceptions to avoid those.
+	 */
+	gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG);
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA);
+
+	switch (len) {
+	case 1:
+		*val = in_8((u8 *)(addr + offset));
+		break;
+	case 2:
+		*val = in_le16((u16 *)(addr + offset));
+		break;
+	default:
+		*val = in_le32((u32 *)(addr + offset));
+		break;
+	}
+
+	pr_debug("pcie-config-read: bus=%3d [%3d..%3d] devfn=0x%04x"
+		 " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n",
+		 bus->number, hose->first_busno, hose->last_busno,
+		 devfn, offset, len, addr + offset, *val);
+
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int ppc4xx_pciex_write_config(struct pci_bus *bus, unsigned int devfn,
+				     int offset, int len, u32 val)
+{
+	struct pci_controller *hose = (struct pci_controller *) bus->sysdata;
+	struct ppc4xx_pciex_port *port =
+		&ppc4xx_pciex_ports[hose->indirect_type];
+	void __iomem *addr;
+	u32 gpl_cfg;
+
+	if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = ppc4xx_pciex_get_config_base(port, bus, devfn);
+
+	/*
+	 * Reading from configuration space of non-existing device can
+	 * generate transaction errors. For the read duration we suppress
+	 * assertion of machine check exceptions to avoid those.
+	 */
+	gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG);
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA);
+
+	pr_debug("pcie-config-write: bus=%3d [%3d..%3d] devfn=0x%04x"
+		 " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n",
+		 bus->number, hose->first_busno, hose->last_busno,
+		 devfn, offset, len, addr + offset, val);
+
+	switch (len) {
+	case 1:
+		out_8((u8 *)(addr + offset), val);
+		break;
+	case 2:
+		out_le16((u16 *)(addr + offset), val);
+		break;
+	default:
+		out_le32((u32 *)(addr + offset), val);
+		break;
+	}
+
+	dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops ppc4xx_pciex_pci_ops =
+{
+	.read  = ppc4xx_pciex_read_config,
+	.write = ppc4xx_pciex_write_config,
+};
+
+static void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port,
+					       struct pci_controller *hose,
+					       void __iomem *mbase)
+{
+	u32 lah, lal, pciah, pcial, sa;
+	int i, j;
+
+	/* Setup outbound memory windows */
+	for (i = j = 0; i < 3; i++) {
+		struct resource *res = &hose->mem_resources[i];
+
+		/* we only care about memory windows */
+		if (!(res->flags & IORESOURCE_MEM))
+			continue;
+		if (j > 1) {
+			printk(KERN_WARNING "%s: Too many ranges\n",
+			       port->node->full_name);
+			break;
+		}
+
+		/* Calculate register values */
+		lah = RES_TO_U32_HIGH(res->start);
+		lal = RES_TO_U32_LOW(res->start);
+		pciah = RES_TO_U32_HIGH(res->start - hose->pci_mem_offset);
+		pcial = RES_TO_U32_LOW(res->start - hose->pci_mem_offset);
+		sa = res->end + 1 - res->start;
+		if (!is_power_of_2(sa) || sa < 0x100000 ||
+		    sa > 0xffffffffu) {
+			printk(KERN_WARNING "%s: Resource out of range\n",
+			       port->node->full_name);
+			continue;
+		}
+		sa = (0xffffffffu << ilog2(sa)) | 0x1;
+
+		/* Program register values */
+		switch (j) {
+		case 0:
+			out_le32(mbase + PECFG_POM0LAH, pciah);
+			out_le32(mbase + PECFG_POM0LAL, pcial);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, sa | 3);
+			break;
+		case 1:
+			out_le32(mbase + PECFG_POM1LAH, pciah);
+			out_le32(mbase + PECFG_POM1LAL, pcial);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff);
+			dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, sa | 3);
+			break;
+		}
+		j++;
+	}
+
+	/* Configure IO, always 64K starting at 0 */
+	if (hose->io_resource.flags & IORESOURCE_IO) {
+		lah = RES_TO_U32_HIGH(hose->io_base_phys);
+		lal = RES_TO_U32_LOW(hose->io_base_phys);
+		out_le32(mbase + PECFG_POM2LAH, 0);
+		out_le32(mbase + PECFG_POM2LAL, 0);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff);
+		dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0xffff0000 | 3);
+	}
+}
+
+static void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port,
+					       struct pci_controller *hose,
+					       void __iomem *mbase,
+					       struct resource *res)
+{
+	resource_size_t size = res->end - res->start + 1;
+	u64 sa;
+
+	/* Calculate window size */
+	sa = (0xffffffffffffffffull << ilog2(size));;
+	if (res->flags & IORESOURCE_PREFETCH)
+		sa |= 0x8;
+
+	out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa));
+	out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa));
+
+	/* The setup of the split looks weird to me ... let's see if it works */
+	out_le32(mbase + PECFG_PIM0LAL, 0x00000000);
+	out_le32(mbase + PECFG_PIM0LAH, 0x00000000);
+	out_le32(mbase + PECFG_PIM1LAL, 0x00000000);
+	out_le32(mbase + PECFG_PIM1LAH, 0x00000000);
+	out_le32(mbase + PECFG_PIM01SAH, 0xffff0000);
+	out_le32(mbase + PECFG_PIM01SAL, 0x00000000);
+
+	/* Enable inbound mapping */
+	out_le32(mbase + PECFG_PIMEN, 0x1);
+
+	out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start));
+	out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start));
+
+	/* Enable I/O, Mem, and Busmaster cycles */
+	out_le16(mbase + PCI_COMMAND,
+		 in_le16(mbase + PCI_COMMAND) |
+		 PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
+}
+
+static void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port)
+{
+	struct resource dma_window;
+	struct pci_controller *hose = NULL;
+	const int *bus_range;
+	int primary = 0, busses;
+	void __iomem *mbase = NULL, *cfg_data = NULL;
+
+	/* XXX FIXME: Handle endpoint mode properly */
+	if (port->endpoint)
+		return;
+
+	/* Check if primary bridge */
+	if (of_get_property(port->node, "primary", NULL))
+		primary = 1;
+
+	/* Get bus range if any */
+	bus_range = of_get_property(port->node, "bus-range", NULL);
+
+	/* Allocate the host controller data structure */
+	hose = pcibios_alloc_controller(port->node);
+	if (!hose)
+		goto fail;
+
+	/* We stick the port number in "indirect_type" so the config space
+	 * ops can retrieve the port data structure easily
+	 */
+	hose->indirect_type = port->index;
+
+	/* Get bus range */
+	hose->first_busno = bus_range ? bus_range[0] : 0x0;
+	hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+	/* Because of how big mapping the config space is (1M per bus), we
+	 * limit how many busses we support. In the long run, we could replace
+	 * that with something akin to kmap_atomic instead. We set aside 1 bus
+	 * for the host itself too.
+	 */
+	busses = hose->last_busno - hose->first_busno; /* This is off by 1 */
+	if (busses > MAX_PCIE_BUS_MAPPED) {
+		busses = MAX_PCIE_BUS_MAPPED;
+		hose->last_busno = hose->first_busno + busses;
+	}
+
+	/* We map the external config space in cfg_data and the host config
+	 * space in cfg_addr. External space is 1M per bus, internal space
+	 * is 4K
+	 */
+	cfg_data = ioremap(port->cfg_space.start +
+				 (hose->first_busno + 1) * 0x100000,
+				 busses * 0x100000);
+	mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000);
+	if (cfg_data == NULL || mbase == NULL) {
+		printk(KERN_ERR "%s: Can't map config space !",
+		       port->node->full_name);
+		goto fail;
+	}
+
+	hose->cfg_data = cfg_data;
+	hose->cfg_addr = mbase;
+
+	pr_debug("PCIE %s, bus %d..%d\n", port->node->full_name,
+		 hose->first_busno, hose->last_busno);
+	pr_debug("     config space mapped at: root @0x%p, other @0x%p\n",
+		 hose->cfg_addr, hose->cfg_data);
+
+	/* Setup config space */
+	hose->ops = &ppc4xx_pciex_pci_ops;
+	port->hose = hose;
+	mbase = (void __iomem *)hose->cfg_addr;
+
+	/*
+	 * Set bus numbers on our root port
+	 */
+	out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno);
+	out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1);
+	out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno);
+
+	/*
+	 * OMRs are already reset, also disable PIMs
+	 */
+	out_le32(mbase + PECFG_PIMEN, 0);
+
+	/* Parse outbound mapping resources */
+	pci_process_bridge_OF_ranges(hose, port->node, primary);
+
+	/* Parse inbound mapping resources */
+	if (ppc4xx_parse_dma_ranges(hose, mbase, &dma_window) != 0)
+		goto fail;
+
+	/* Configure outbound ranges POMs */
+	ppc4xx_configure_pciex_POMs(port, hose, mbase);
+
+	/* Configure inbound ranges PIMs */
+	ppc4xx_configure_pciex_PIMs(port, hose, mbase, &dma_window);
+
+	/* The root complex doesn't show up if we don't set some vendor
+	 * and device IDs into it. Those are the same bogus one that the
+	 * initial code in arch/ppc add. We might want to change that.
+	 */
+	out_le16(mbase + 0x200, 0xaaa0 + port->index);
+	out_le16(mbase + 0x202, 0xbed0 + port->index);
+
+	/* Set Class Code to PCI-PCI bridge and Revision Id to 1 */
+	out_le32(mbase + 0x208, 0x06040001);
+
+	printk(KERN_INFO "PCIE%d: successfully set as root-complex\n",
+	       port->index);
+	return;
+ fail:
+	if (hose)
+		pcibios_free_controller(hose);
+	if (cfg_data)
+		iounmap(cfg_data);
+	if (mbase)
+		iounmap(mbase);
+}
+
 static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
 {
-	/* NYI */
+	struct ppc4xx_pciex_port *port;
+	const u32 *pval;
+	int portno;
+	unsigned int dcrs;
+
+	/* First, proceed to core initialization as we assume there's
+	 * only one PCIe core in the system
+	 */
+	if (ppc4xx_pciex_check_core_init(np))
+		return;
+
+	/* Get the port number from the device-tree */
+	pval = of_get_property(np, "port", NULL);
+	if (pval == NULL) {
+		printk(KERN_ERR "PCIE: Can't find port number for %s\n",
+		       np->full_name);
+		return;
+	}
+	portno = *pval;
+	if (portno >= ppc4xx_pciex_port_count) {
+		printk(KERN_ERR "PCIE: port number out of range for %s\n",
+		       np->full_name);
+		return;
+	}
+	port = &ppc4xx_pciex_ports[portno];
+	port->index = portno;
+	port->node = of_node_get(np);
+	pval = of_get_property(np, "sdr-base", NULL);
+	if (pval == NULL) {
+		printk(KERN_ERR "PCIE: missing sdr-base for %s\n",
+		       np->full_name);
+		return;
+	}
+	port->sdr_base = *pval;
+
+	/* Fetch config space registers address */
+	if (of_address_to_resource(np, 0, &port->cfg_space)) {
+		printk(KERN_ERR "%s: Can't get PCI-E config space !",
+		       np->full_name);
+		return;
+	}
+	/* Fetch host bridge internal registers address */
+	if (of_address_to_resource(np, 1, &port->utl_regs)) {
+		printk(KERN_ERR "%s: Can't get UTL register base !",
+		       np->full_name);
+		return;
+	}
+
+	/* Map DCRs */
+	dcrs = dcr_resource_start(np, 0);
+	if (dcrs == 0) {
+		printk(KERN_ERR "%s: Can't get DCR register base !",
+		       np->full_name);
+		return;
+	}
+	port->dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0));
+
+	/* Initialize the port specific registers */
+	if (ppc4xx_pciex_port_init(port))
+		return;
+
+	/* Setup the linux hose data structure */
+	ppc4xx_pciex_port_setup_hose(port);
 }
 
+#endif /* CONFIG_PPC4xx_PCI_EXPRESS */
+
 static int __init ppc4xx_pci_find_bridges(void)
 {
 	struct device_node *np;
 
+#ifdef CONFIG_PPC4xx_PCI_EXPRESS
 	for_each_compatible_node(np, NULL, "ibm,plb-pciex")
 		ppc4xx_probe_pciex_bridge(np);
+#endif
 	for_each_compatible_node(np, NULL, "ibm,plb-pcix")
 		ppc4xx_probe_pcix_bridge(np);
 	for_each_compatible_node(np, NULL, "ibm,plb-pci")
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h
===================================================================
--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-14 15:49:43.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-14 15:49:43.000000000 +1100
@@ -121,5 +121,247 @@
 #define PCIL0_PTM2MS		0x38
 #define PCIL0_PTM2LA		0x3c
 
+/*
+ * 4xx PCIe bridge register definitions
+ */
+
+/* DCR offsets */
+#define DCRO_PEGPL_CFGBAH		0x00
+#define DCRO_PEGPL_CFGBAL		0x01
+#define DCRO_PEGPL_CFGMSK		0x02
+#define DCRO_PEGPL_MSGBAH		0x03
+#define DCRO_PEGPL_MSGBAL		0x04
+#define DCRO_PEGPL_MSGMSK		0x05
+#define DCRO_PEGPL_OMR1BAH		0x06
+#define DCRO_PEGPL_OMR1BAL		0x07
+#define DCRO_PEGPL_OMR1MSKH		0x08
+#define DCRO_PEGPL_OMR1MSKL		0x09
+#define DCRO_PEGPL_OMR2BAH		0x0a
+#define DCRO_PEGPL_OMR2BAL		0x0b
+#define DCRO_PEGPL_OMR2MSKH		0x0c
+#define DCRO_PEGPL_OMR2MSKL		0x0d
+#define DCRO_PEGPL_OMR3BAH		0x0e
+#define DCRO_PEGPL_OMR3BAL		0x0f
+#define DCRO_PEGPL_OMR3MSKH		0x10
+#define DCRO_PEGPL_OMR3MSKL		0x11
+#define DCRO_PEGPL_REGBAH		0x12
+#define DCRO_PEGPL_REGBAL		0x13
+#define DCRO_PEGPL_REGMSK		0x14
+#define DCRO_PEGPL_SPECIAL		0x15
+#define DCRO_PEGPL_CFG			0x16
+#define DCRO_PEGPL_ESR			0x17
+#define DCRO_PEGPL_EARH			0x18
+#define DCRO_PEGPL_EARL			0x19
+#define DCRO_PEGPL_EATR			0x1a
+
+/* DMER mask */
+#define GPL_DMER_MASK_DISA	0x02000000
+
+/*
+ * System DCRs (SDRs)
+ */
+#define PESDR0_PLLLCT1			0x03a0
+#define PESDR0_PLLLCT2			0x03a1
+#define PESDR0_PLLLCT3			0x03a2
+
+/*
+ * 440SPe additional DCRs
+ */
+#define PESDR0_440SPE_UTLSET1		0x0300
+#define PESDR0_440SPE_UTLSET2		0x0301
+#define PESDR0_440SPE_DLPSET		0x0302
+#define PESDR0_440SPE_LOOP		0x0303
+#define PESDR0_440SPE_RCSSET		0x0304
+#define PESDR0_440SPE_RCSSTS		0x0305
+#define PESDR0_440SPE_HSSL0SET1		0x0306
+#define PESDR0_440SPE_HSSL0SET2		0x0307
+#define PESDR0_440SPE_HSSL0STS		0x0308
+#define PESDR0_440SPE_HSSL1SET1		0x0309
+#define PESDR0_440SPE_HSSL1SET2		0x030a
+#define PESDR0_440SPE_HSSL1STS		0x030b
+#define PESDR0_440SPE_HSSL2SET1		0x030c
+#define PESDR0_440SPE_HSSL2SET2		0x030d
+#define PESDR0_440SPE_HSSL2STS		0x030e
+#define PESDR0_440SPE_HSSL3SET1		0x030f
+#define PESDR0_440SPE_HSSL3SET2		0x0310
+#define PESDR0_440SPE_HSSL3STS		0x0311
+#define PESDR0_440SPE_HSSL4SET1		0x0312
+#define PESDR0_440SPE_HSSL4SET2		0x0313
+#define PESDR0_440SPE_HSSL4STS	       	0x0314
+#define PESDR0_440SPE_HSSL5SET1		0x0315
+#define PESDR0_440SPE_HSSL5SET2		0x0316
+#define PESDR0_440SPE_HSSL5STS		0x0317
+#define PESDR0_440SPE_HSSL6SET1		0x0318
+#define PESDR0_440SPE_HSSL6SET2		0x0319
+#define PESDR0_440SPE_HSSL6STS		0x031a
+#define PESDR0_440SPE_HSSL7SET1		0x031b
+#define PESDR0_440SPE_HSSL7SET2		0x031c
+#define PESDR0_440SPE_HSSL7STS		0x031d
+#define PESDR0_440SPE_HSSCTLSET		0x031e
+#define PESDR0_440SPE_LANE_ABCD		0x031f
+#define PESDR0_440SPE_LANE_EFGH		0x0320
+
+#define PESDR1_440SPE_UTLSET1		0x0340
+#define PESDR1_440SPE_UTLSET2		0x0341
+#define PESDR1_440SPE_DLPSET		0x0342
+#define PESDR1_440SPE_LOOP		0x0343
+#define PESDR1_440SPE_RCSSET		0x0344
+#define PESDR1_440SPE_RCSSTS		0x0345
+#define PESDR1_440SPE_HSSL0SET1		0x0346
+#define PESDR1_440SPE_HSSL0SET2		0x0347
+#define PESDR1_440SPE_HSSL0STS		0x0348
+#define PESDR1_440SPE_HSSL1SET1		0x0349
+#define PESDR1_440SPE_HSSL1SET2		0x034a
+#define PESDR1_440SPE_HSSL1STS		0x034b
+#define PESDR1_440SPE_HSSL2SET1		0x034c
+#define PESDR1_440SPE_HSSL2SET2		0x034d
+#define PESDR1_440SPE_HSSL2STS		0x034e
+#define PESDR1_440SPE_HSSL3SET1		0x034f
+#define PESDR1_440SPE_HSSL3SET2		0x0350
+#define PESDR1_440SPE_HSSL3STS		0x0351
+#define PESDR1_440SPE_HSSCTLSET		0x0352
+#define PESDR1_440SPE_LANE_ABCD		0x0353
+
+#define PESDR2_440SPE_UTLSET1		0x0370
+#define PESDR2_440SPE_UTLSET2		0x0371
+#define PESDR2_440SPE_DLPSET		0x0372
+#define PESDR2_440SPE_LOOP		0x0373
+#define PESDR2_440SPE_RCSSET		0x0374
+#define PESDR2_440SPE_RCSSTS		0x0375
+#define PESDR2_440SPE_HSSL0SET1		0x0376
+#define PESDR2_440SPE_HSSL0SET2		0x0377
+#define PESDR2_440SPE_HSSL0STS		0x0378
+#define PESDR2_440SPE_HSSL1SET1		0x0379
+#define PESDR2_440SPE_HSSL1SET2		0x037a
+#define PESDR2_440SPE_HSSL1STS		0x037b
+#define PESDR2_440SPE_HSSL2SET1		0x037c
+#define PESDR2_440SPE_HSSL2SET2		0x037d
+#define PESDR2_440SPE_HSSL2STS		0x037e
+#define PESDR2_440SPE_HSSL3SET1		0x037f
+#define PESDR2_440SPE_HSSL3SET2		0x0380
+#define PESDR2_440SPE_HSSL3STS		0x0381
+#define PESDR2_440SPE_HSSCTLSET		0x0382
+#define PESDR2_440SPE_LANE_ABCD		0x0383
+
+/*
+ * 405EX additional DCRs
+ */
+#define PESDR0_405EX_UTLSET1		0x0400
+#define PESDR0_405EX_UTLSET2		0x0401
+#define PESDR0_405EX_DLPSET		0x0402
+#define PESDR0_405EX_LOOP		0x0403
+#define PESDR0_405EX_RCSSET		0x0404
+#define PESDR0_405EX_RCSSTS		0x0405
+#define PESDR0_405EX_PHYSET1		0x0406
+#define PESDR0_405EX_PHYSET2		0x0407
+#define PESDR0_405EX_BIST		0x0408
+#define PESDR0_405EX_LPB		0x040B
+#define PESDR0_405EX_PHYSTA		0x040C
+
+#define PESDR1_405EX_UTLSET1		0x0440
+#define PESDR1_405EX_UTLSET2		0x0441
+#define PESDR1_405EX_DLPSET		0x0442
+#define PESDR1_405EX_LOOP		0x0443
+#define PESDR1_405EX_RCSSET		0x0444
+#define PESDR1_405EX_RCSSTS		0x0445
+#define PESDR1_405EX_PHYSET1		0x0446
+#define PESDR1_405EX_PHYSET2		0x0447
+#define PESDR1_405EX_BIST		0x0448
+#define PESDR1_405EX_LPB		0x044B
+#define PESDR1_405EX_PHYSTA		0x044C
+
+/*
+ * Of the above, some are common offsets from the base
+ */
+#define PESDRn_UTLSET1			0x00
+#define PESDRn_UTLSET2			0x01
+#define PESDRn_DLPSET			0x02
+#define PESDRn_LOOP			0x03
+#define PESDRn_RCSSET			0x04
+#define PESDRn_RCSSTS			0x05
+
+/* 440spe only */
+#define PESDRn_440SPE_HSSL0SET1		0x06
+#define PESDRn_440SPE_HSSL0SET2		0x07
+#define PESDRn_440SPE_HSSL0STS		0x08
+#define PESDRn_440SPE_HSSL1SET1		0x09
+#define PESDRn_440SPE_HSSL1SET2		0x0a
+#define PESDRn_440SPE_HSSL1STS		0x0b
+#define PESDRn_440SPE_HSSL2SET1		0x0c
+#define PESDRn_440SPE_HSSL2SET2		0x0d
+#define PESDRn_440SPE_HSSL2STS		0x0e
+#define PESDRn_440SPE_HSSL3SET1		0x0f
+#define PESDRn_440SPE_HSSL3SET2		0x10
+#define PESDRn_440SPE_HSSL3STS		0x11
+
+/* 440spe port 0 only */
+#define PESDRn_440SPE_HSSL4SET1		0x12
+#define PESDRn_440SPE_HSSL4SET2		0x13
+#define PESDRn_440SPE_HSSL4STS	       	0x14
+#define PESDRn_440SPE_HSSL5SET1		0x15
+#define PESDRn_440SPE_HSSL5SET2		0x16
+#define PESDRn_440SPE_HSSL5STS		0x17
+#define PESDRn_440SPE_HSSL6SET1		0x18
+#define PESDRn_440SPE_HSSL6SET2		0x19
+#define PESDRn_440SPE_HSSL6STS		0x1a
+#define PESDRn_440SPE_HSSL7SET1		0x1b
+#define PESDRn_440SPE_HSSL7SET2		0x1c
+#define PESDRn_440SPE_HSSL7STS		0x1d
+
+/* 405ex only */
+#define PESDRn_405EX_PHYSET1		0x06
+#define PESDRn_405EX_PHYSET2		0x07
+#define PESDRn_405EX_PHYSTA		0x0c
+
+/*
+ * UTL register offsets
+ */
+#define PEUTL_PBCTL		0x00
+#define PEUTL_PBBSZ		0x20
+#define PEUTL_OPDBSZ		0x68
+#define PEUTL_IPHBSZ		0x70
+#define PEUTL_IPDBSZ		0x78
+#define PEUTL_OUTTR		0x90
+#define PEUTL_INTR		0x98
+#define PEUTL_PCTL		0xa0
+#define PEUTL_RCSTA		0xB0
+#define PEUTL_RCIRQEN		0xb8
+
+/*
+ * Config space register offsets
+ */
+#define PECFG_BAR0LMPA		0x210
+#define PECFG_BAR0HMPA		0x214
+#define PECFG_BAR1MPA		0x218
+#define PECFG_BAR2LMPA		0x220
+#define PECFG_BAR2HMPA		0x224
+
+#define PECFG_PIMEN		0x33c
+#define PECFG_PIM0LAL		0x340
+#define PECFG_PIM0LAH		0x344
+#define PECFG_PIM1LAL		0x348
+#define PECFG_PIM1LAH		0x34c
+#define PECFG_PIM01SAL		0x350
+#define PECFG_PIM01SAH		0x354
+
+#define PECFG_POM0LAL		0x380
+#define PECFG_POM0LAH		0x384
+#define PECFG_POM1LAL		0x388
+#define PECFG_POM1LAH		0x38c
+#define PECFG_POM2LAL		0x390
+#define PECFG_POM2LAH		0x394
+
+
+enum
+{
+	PTYPE_ENDPOINT		= 0x0,
+	PTYPE_LEGACY_ENDPOINT	= 0x1,
+	PTYPE_ROOT_PORT		= 0x4,
+
+	LNKW_X1			= 0x1,
+	LNKW_X4			= 0x4,
+	LNKW_X8			= 0x8
+};
+
 
 #endif /* __PPC4XX_PCI_H__ */
Index: linux-merge/arch/powerpc/Kconfig
===================================================================
--- linux-merge.orig/arch/powerpc/Kconfig	2007-12-14 15:48:56.000000000 +1100
+++ linux-merge/arch/powerpc/Kconfig	2007-12-14 15:49:43.000000000 +1100
@@ -165,6 +165,7 @@ config PPC_OF_PLATFORM_PCI
 
 source "init/Kconfig"
 
+source "arch/powerpc/sysdev/Kconfig"
 source "arch/powerpc/platforms/Kconfig"
 
 menu "Kernel options"
Index: linux-merge/arch/powerpc/sysdev/Kconfig
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-merge/arch/powerpc/sysdev/Kconfig	2007-12-14 15:49:43.000000000 +1100
@@ -0,0 +1,8 @@
+# For a description of the syntax of this configuration file,
+# see Documentation/kbuild/kconfig-language.txt.
+#
+
+config PPC4xx_PCI_EXPRESS
+	bool
+	depends on PCI && 4xx
+	default n

^ permalink raw reply

* [PATCH 4/21] [POWERPC] 4xx PLB to PCI 2.x support
From: Benjamin Herrenschmidt @ 2007-12-21  4:39 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds to the previous patch the support for the 4xx PCI 2.x
bridges.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

This version implement the basic support for the 405GP bridge,
I haven't yet looked at differences that other implementations
may have for the PCI 2.x part.

 arch/powerpc/sysdev/ppc4xx_pci.c |  180 ++++++++++++++++++++++++++++++++++++++-
 arch/powerpc/sysdev/ppc4xx_pci.h |   19 ++++
 2 files changed, 198 insertions(+), 1 deletion(-)

--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-14 15:49:42.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-14 15:49:43.000000000 +1100
@@ -21,6 +21,36 @@ static int dma_offset_set;
 /* Move that to a useable header */
 extern unsigned long total_memory;
 
+static void fixup_ppc4xx_pci_bridge(struct pci_dev *dev)
+{
+	struct pci_controller *hose;
+	int i;
+
+	if (dev->devfn != 0 || dev->bus->self != NULL)
+		return;
+
+	hose = pci_bus_to_host(dev->bus);
+	if (hose == NULL)
+		return;
+
+	if (!of_device_is_compatible(hose->dn, "ibm,plb-pciex") &&
+	    !of_device_is_compatible(hose->dn, "ibm,plb-pcix") &&
+	    !of_device_is_compatible(hose->dn, "ibm,plb-pci"))
+		return;
+
+	/* Hide the PCI host BARs from the kernel as their content doesn't
+	 * fit well in the resource management
+	 */
+	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+		dev->resource[i].start = dev->resource[i].end = 0;
+		dev->resource[i].flags = 0;
+	}
+
+	printk(KERN_INFO "PCI: Hiding 4xx host bridge resources %s\n",
+	       pci_name(dev));
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, fixup_ppc4xx_pci_bridge);
+
 static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
 					  void __iomem *reg,
 					  struct resource *res)
@@ -126,9 +156,157 @@ static int __init ppc4xx_parse_dma_range
 /*
  * 4xx PCI 2.x part
  */
+
+static void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose,
+					     void __iomem *reg)
+{
+	u32 la, ma, pcila, pciha;
+	int i, j;
+
+	/* Setup outbound memory windows */
+	for (i = j = 0; i < 3; i++) {
+		struct resource *res = &hose->mem_resources[i];
+
+		/* we only care about memory windows */
+		if (!(res->flags & IORESOURCE_MEM))
+			continue;
+		if (j > 2) {
+			printk(KERN_WARNING "%s: Too many ranges\n",
+			       hose->dn->full_name);
+			break;
+		}
+
+		/* Calculate register values */
+		la = res->start;
+#ifdef CONFIG_RESOURCES_64BIT
+		pciha = (res->start - hose->pci_mem_offset) >> 32;
+		pcila = (res->start - hose->pci_mem_offset) & 0xffffffffu;
+#else
+		pciha = 0;
+		pcila = res->start - hose->pci_mem_offset;
+#endif
+
+		ma = res->end + 1 - res->start;
+		if (!is_power_of_2(ma) || ma < 0x1000 || ma > 0xffffffffu) {
+			printk(KERN_WARNING "%s: Resource out of range\n",
+			       hose->dn->full_name);
+			continue;
+		}
+		ma = (0xffffffffu << ilog2(ma)) | 0x1;
+		if (res->flags & IORESOURCE_PREFETCH)
+			ma |= 0x2;
+
+		/* Program register values */
+		writel(la, reg + PCIL0_PMM0LA + (0x10 * j));
+		writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * j));
+		writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * j));
+		writel(ma, reg + PCIL0_PMM0MA + (0x10 * j));
+		j++;
+	}
+}
+
+static void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose,
+					     void __iomem *reg,
+					     const struct resource *res)
+{
+	resource_size_t size = res->end - res->start + 1;
+	u32 sa;
+
+	/* Calculate window size */
+	sa = (0xffffffffu << ilog2(size)) | 1;
+	sa |= 0x1;
+
+	/* RAM is always at 0 local for now */
+	writel(0, reg + PCIL0_PTM1LA);
+	writel(sa, reg + PCIL0_PTM1MS);
+
+	/* Map on PCI side */
+	early_write_config_dword(hose, hose->first_busno, 0,
+				 PCI_BASE_ADDRESS_1, res->start);
+	early_write_config_dword(hose, hose->first_busno, 0,
+				 PCI_BASE_ADDRESS_2, 0x00000000);
+	early_write_config_word(hose, hose->first_busno, 0,
+				PCI_COMMAND, 0x0006);
+}
+
 static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
 {
 	/* NYI */
+	struct resource rsrc_cfg;
+	struct resource rsrc_reg;
+	struct resource dma_window;
+	struct pci_controller *hose = NULL;
+	void __iomem *reg = NULL;
+	const int *bus_range;
+	int primary = 0;
+
+	/* Fetch config space registers address */
+	if (of_address_to_resource(np, 0, &rsrc_cfg)) {
+		printk(KERN_ERR "%s:Can't get PCI config register base !",
+		       np->full_name);
+		return;
+	}
+	/* Fetch host bridge internal registers address */
+	if (of_address_to_resource(np, 3, &rsrc_reg)) {
+		printk(KERN_ERR "%s: Can't get PCI internal register base !",
+		       np->full_name);
+		return;
+	}
+
+	/* Check if primary bridge */
+	if (of_get_property(np, "primary", NULL))
+		primary = 1;
+
+	/* Get bus range if any */
+	bus_range = of_get_property(np, "bus-range", NULL);
+
+	/* Map registers */
+	reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start);
+	if (reg == NULL) {
+		printk(KERN_ERR "%s: Can't map registers !", np->full_name);
+		goto fail;
+	}
+
+	/* Allocate the host controller data structure */
+	hose = pcibios_alloc_controller(np);
+	if (!hose)
+		goto fail;
+
+	hose->first_busno = bus_range ? bus_range[0] : 0x0;
+	hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+	/* Setup config space */
+	setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0);
+
+	/* Disable all windows */
+	writel(0, reg + PCIL0_PMM0MA);
+	writel(0, reg + PCIL0_PMM1MA);
+	writel(0, reg + PCIL0_PMM2MA);
+	writel(0, reg + PCIL0_PTM1MS);
+	writel(0, reg + PCIL0_PTM2MS);
+
+	/* Parse outbound mapping resources */
+	pci_process_bridge_OF_ranges(hose, np, primary);
+
+	/* Parse inbound mapping resources */
+	if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0)
+		goto fail;
+
+	/* Configure outbound ranges POMs */
+	ppc4xx_configure_pci_PMMs(hose, reg);
+
+	/* Configure inbound ranges PIMs */
+	ppc4xx_configure_pci_PTMs(hose, reg, &dma_window);
+
+	/* We don't need the registers anymore */
+	iounmap(reg);
+	return;
+
+ fail:
+	if (hose)
+		pcibios_free_controller(hose);
+	if (reg)
+		iounmap(reg);
 }
 
 /*
@@ -155,7 +333,7 @@ static void __init ppc4xx_configure_pcix
 		}
 
 		/* Calculate register values */
-#ifdef CONFIG_PTE_64BIT
+#ifdef CONFIG_RESOURCES_64BIT
 		lah = res->start >> 32;
 		lal = res->start & 0xffffffffu;
 		pciah = (res->start - hose->pci_mem_offset) >> 32;
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h
===================================================================
--- linux-merge.orig/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-14 15:49:42.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-14 15:49:43.000000000 +1100
@@ -101,6 +101,25 @@
 #define PCIX0_MSGOH		0x10c
 #define PCIX0_IM		0x1f8
 
+/*
+ * 4xx PCI bridge register definitions
+ */
+#define PCIL0_PMM0LA		0x00
+#define PCIL0_PMM0MA		0x04
+#define PCIL0_PMM0PCILA		0x08
+#define PCIL0_PMM0PCIHA		0x0c
+#define PCIL0_PMM1LA		0x10
+#define PCIL0_PMM1MA		0x14
+#define PCIL0_PMM1PCILA		0x18
+#define PCIL0_PMM1PCIHA		0x1c
+#define PCIL0_PMM2LA		0x20
+#define PCIL0_PMM2MA		0x24
+#define PCIL0_PMM2PCILA		0x28
+#define PCIL0_PMM2PCIHA		0x2c
+#define PCIL0_PTM1MS		0x30
+#define PCIL0_PTM1LA		0x34
+#define PCIL0_PTM2MS		0x38
+#define PCIL0_PTM2LA		0x3c
 
 
 #endif /* __PPC4XX_PCI_H__ */

^ permalink raw reply

* [PATCH 3/21] [POWERPC] 4xx PLB to PCI-X support
From: Benjamin Herrenschmidt @ 2007-12-21  4:39 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds base support code for the 4xx PCI-X bridge. It also provides
placeholders for the PCI and PCI-E version but they aren't supported
with this patch.

The bridges are configured based on device-tree properties.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

Tested on 440GP only so far.

 arch/powerpc/sysdev/Makefile     |    3 
 arch/powerpc/sysdev/ppc4xx_pci.c |  339 +++++++++++++++++++++++++++++++++++++++
 arch/powerpc/sysdev/ppc4xx_pci.h |  106 ++++++++++++
 3 files changed, 448 insertions(+)

--- linux-merge.orig/arch/powerpc/sysdev/Makefile	2007-12-14 15:48:56.000000000 +1100
+++ linux-merge/arch/powerpc/sysdev/Makefile	2007-12-14 15:49:42.000000000 +1100
@@ -27,6 +27,9 @@ obj-$(CONFIG_PPC_I8259)		+= i8259.o
 obj-$(CONFIG_PPC_83xx)		+= ipic.o
 obj-$(CONFIG_4xx)		+= uic.o
 obj-$(CONFIG_XILINX_VIRTEX)	+= xilinx_intc.o
+ifeq ($(CONFIG_PCI),y)
+obj-$(CONFIG_4xx)		+= ppc4xx_pci.o
+endif
 endif
 
 # Temporary hack until we have migrated to asm-powerpc
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.c	2007-12-14 15:49:42.000000000 +1100
@@ -0,0 +1,339 @@
+/*
+ * PCI / PCI-X / PCI-Express support for 4xx parts
+ *
+ * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/of.h>
+
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+
+#include "ppc4xx_pci.h"
+
+static int dma_offset_set;
+
+/* Move that to a useable header */
+extern unsigned long total_memory;
+
+static int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose,
+					  void __iomem *reg,
+					  struct resource *res)
+{
+	u64 size;
+	const u32 *ranges;
+	int rlen;
+	int pna = of_n_addr_cells(hose->dn);
+	int np = pna + 5;
+
+	/* Default */
+	res->start = 0;
+	res->end = size = 0x80000000;
+	res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
+
+	/* Get dma-ranges property */
+	ranges = of_get_property(hose->dn, "dma-ranges", &rlen);
+	if (ranges == NULL)
+		goto out;
+
+	/* Walk it */
+	while ((rlen -= np * 4) >= 0) {
+		u32 pci_space = ranges[0];
+		u64 pci_addr = of_read_number(ranges + 1, 2);
+		u64 cpu_addr = of_translate_dma_address(hose->dn, ranges + 3);
+		size = of_read_number(ranges + pna + 3, 2);
+		ranges += np;
+		if (cpu_addr == OF_BAD_ADDR || size == 0)
+			continue;
+
+		/* We only care about memory */
+		if ((pci_space & 0x03000000) != 0x02000000)
+			continue;
+
+		/* We currently only support memory at 0, and pci_addr
+		 * within 32 bits space
+		 */
+		if (cpu_addr != 0 || pci_addr > 0xffffffff) {
+			printk(KERN_WARNING "%s: Ignored unsupported dma range"
+			       " 0x%016llx...0x%016llx -> 0x%016llx\n",
+			       hose->dn->full_name,
+			       pci_addr, pci_addr + size - 1, cpu_addr);
+			continue;
+		}
+
+		/* Check if not prefetchable */
+		if (!(pci_space & 0x40000000))
+			res->flags &= ~IORESOURCE_PREFETCH;
+
+
+		/* Use that */
+		res->start = pci_addr;
+#ifndef CONFIG_RESOURCES_64BIT
+		/* Beware of 32 bits resources */
+		if ((pci_addr + size) > 0x100000000ull)
+			res->end = 0xffffffff;
+		else
+#endif
+			res->end = res->start + size - 1;
+		break;
+	}
+
+	/* We only support one global DMA offset */
+	if (dma_offset_set && pci_dram_offset != res->start) {
+		printk(KERN_ERR "%s: dma-ranges(s) mismatch\n",
+		       hose->dn->full_name);
+		return -ENXIO;
+	}
+
+	/* Check that we can fit all of memory as we don't support
+	 * DMA bounce buffers
+	 */
+	if (size < total_memory) {
+		printk(KERN_ERR "%s: dma-ranges too small "
+		       "(size=%llx total_memory=%lx)\n",
+		       hose->dn->full_name, size, total_memory);
+		return -ENXIO;
+	}
+
+	/* Check we are a power of 2 size and that base is a multiple of size*/
+	if (!is_power_of_2(size) ||
+	    (res->start & (size - 1)) != 0) {
+		printk(KERN_ERR "%s: dma-ranges unaligned\n",
+		       hose->dn->full_name);
+		return -ENXIO;
+	}
+
+	/* Check that we are fully contained within 32 bits space */
+	if (res->end > 0xffffffff) {
+		printk(KERN_ERR "%s: dma-ranges outside of 32 bits space\n",
+		       hose->dn->full_name);
+		return -ENXIO;
+	}
+ out:
+	dma_offset_set = 1;
+	pci_dram_offset = res->start;
+
+	printk(KERN_INFO "4xx PCI DMA offset set to 0x%08lx\n",
+	       pci_dram_offset);
+	return 0;
+}
+
+/*
+ * 4xx PCI 2.x part
+ */
+static void __init ppc4xx_probe_pci_bridge(struct device_node *np)
+{
+	/* NYI */
+}
+
+/*
+ * 4xx PCI-X part
+ */
+
+static void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose,
+					      void __iomem *reg)
+{
+	u32 lah, lal, pciah, pcial, sa;
+	int i, j;
+
+	/* Setup outbound memory windows */
+	for (i = j = 0; i < 3; i++) {
+		struct resource *res = &hose->mem_resources[i];
+
+		/* we only care about memory windows */
+		if (!(res->flags & IORESOURCE_MEM))
+			continue;
+		if (j > 1) {
+			printk(KERN_WARNING "%s: Too many ranges\n",
+			       hose->dn->full_name);
+			break;
+		}
+
+		/* Calculate register values */
+#ifdef CONFIG_PTE_64BIT
+		lah = res->start >> 32;
+		lal = res->start & 0xffffffffu;
+		pciah = (res->start - hose->pci_mem_offset) >> 32;
+		pcial = (res->start - hose->pci_mem_offset) & 0xffffffffu;
+#else
+		lah = pciah = 0;
+		lal = res->start;
+		pcial = res->start - hose->pci_mem_offset;
+#endif
+		sa = res->end + 1 - res->start;
+		if (!is_power_of_2(sa) || sa < 0x100000 ||
+		    sa > 0xffffffffu) {
+			printk(KERN_WARNING "%s: Resource out of range\n",
+			       hose->dn->full_name);
+			continue;
+		}
+		sa = (0xffffffffu << ilog2(sa)) | 0x1;
+
+		/* Program register values */
+		if (j == 0) {
+			writel(lah, reg + PCIX0_POM0LAH);
+			writel(lal, reg + PCIX0_POM0LAL);
+			writel(pciah, reg + PCIX0_POM0PCIAH);
+			writel(pcial, reg + PCIX0_POM0PCIAL);
+			writel(sa, reg + PCIX0_POM0SA);
+		} else {
+			writel(lah, reg + PCIX0_POM1LAH);
+			writel(lal, reg + PCIX0_POM1LAL);
+			writel(pciah, reg + PCIX0_POM1PCIAH);
+			writel(pcial, reg + PCIX0_POM1PCIAL);
+			writel(sa, reg + PCIX0_POM1SA);
+		}
+		j++;
+	}
+}
+
+static void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose,
+					      void __iomem *reg,
+					      const struct resource *res,
+					      int big_pim,
+					      int enable_msi_hole)
+{
+	resource_size_t size = res->end - res->start + 1;
+	u32 sa;
+
+	/* RAM is always at 0 */
+	writel(0x00000000, reg + PCIX0_PIM0LAH);
+	writel(0x00000000, reg + PCIX0_PIM0LAL);
+
+	/* Calculate window size */
+	sa = (0xffffffffu << ilog2(size)) | 1;
+	sa |= 0x1;
+	if (res->flags & IORESOURCE_PREFETCH)
+		sa |= 0x2;
+	if (enable_msi_hole)
+		sa |= 0x4;
+	writel(sa, reg + PCIX0_PIM0SA);
+	if (big_pim)
+		writel(0xffffffff, reg + PCIX0_PIM0SAH);
+
+	/* Map on PCI side */
+	writel(0x00000000, reg + PCIX0_BAR0H);
+	writel(res->start, reg + PCIX0_BAR0L);
+	writew(0x0006, reg + PCIX0_COMMAND);
+}
+
+static void __init ppc4xx_probe_pcix_bridge(struct device_node *np)
+{
+	struct resource rsrc_cfg;
+	struct resource rsrc_reg;
+	struct resource dma_window;
+	struct pci_controller *hose = NULL;
+	void __iomem *reg = NULL;
+	const int *bus_range;
+	int big_pim = 0, msi = 0, primary = 0;
+
+	/* Fetch config space registers address */
+	if (of_address_to_resource(np, 0, &rsrc_cfg)) {
+		printk(KERN_ERR "%s:Can't get PCI-X config register base !",
+		       np->full_name);
+		return;
+	}
+	/* Fetch host bridge internal registers address */
+	if (of_address_to_resource(np, 3, &rsrc_reg)) {
+		printk(KERN_ERR "%s: Can't get PCI-X internal register base !",
+		       np->full_name);
+		return;
+	}
+
+	/* Check if it supports large PIMs (440GX) */
+	if (of_get_property(np, "large-inbound-windows", NULL))
+		big_pim = 1;
+
+	/* Check if we should enable MSIs inbound hole */
+	if (of_get_property(np, "enable-msi-hole", NULL))
+		msi = 1;
+
+	/* Check if primary bridge */
+	if (of_get_property(np, "primary", NULL))
+		primary = 1;
+
+	/* Get bus range if any */
+	bus_range = of_get_property(np, "bus-range", NULL);
+
+	/* Map registers */
+	reg = ioremap(rsrc_reg.start, rsrc_reg.end + 1 - rsrc_reg.start);
+	if (reg == NULL) {
+		printk(KERN_ERR "%s: Can't map registers !", np->full_name);
+		goto fail;
+	}
+
+	/* Allocate the host controller data structure */
+	hose = pcibios_alloc_controller(np);
+	if (!hose)
+		goto fail;
+
+	hose->first_busno = bus_range ? bus_range[0] : 0x0;
+	hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+	/* Setup config space */
+	setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0);
+
+	/* Disable all windows */
+	writel(0, reg + PCIX0_POM0SA);
+	writel(0, reg + PCIX0_POM1SA);
+	writel(0, reg + PCIX0_POM2SA);
+	writel(0, reg + PCIX0_PIM0SA);
+	writel(0, reg + PCIX0_PIM1SA);
+	writel(0, reg + PCIX0_PIM2SA);
+	if (big_pim) {
+		writel(0, reg + PCIX0_PIM0SAH);
+		writel(0, reg + PCIX0_PIM2SAH);
+	}
+
+	/* Parse outbound mapping resources */
+	pci_process_bridge_OF_ranges(hose, np, primary);
+
+	/* Parse inbound mapping resources */
+	if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0)
+		goto fail;
+
+	/* Configure outbound ranges POMs */
+	ppc4xx_configure_pcix_POMs(hose, reg);
+
+	/* Configure inbound ranges PIMs */
+	ppc4xx_configure_pcix_PIMs(hose, reg, &dma_window, big_pim, msi);
+
+	/* We don't need the registers anymore */
+	iounmap(reg);
+	return;
+
+ fail:
+	if (hose)
+		pcibios_free_controller(hose);
+	if (reg)
+		iounmap(reg);
+}
+
+/*
+ * 4xx PCI-Express part
+ */
+static void __init ppc4xx_probe_pciex_bridge(struct device_node *np)
+{
+	/* NYI */
+}
+
+static int __init ppc4xx_pci_find_bridges(void)
+{
+	struct device_node *np;
+
+	for_each_compatible_node(np, NULL, "ibm,plb-pciex")
+		ppc4xx_probe_pciex_bridge(np);
+	for_each_compatible_node(np, NULL, "ibm,plb-pcix")
+		ppc4xx_probe_pcix_bridge(np);
+	for_each_compatible_node(np, NULL, "ibm,plb-pci")
+		ppc4xx_probe_pci_bridge(np);
+
+	return 0;
+}
+arch_initcall(ppc4xx_pci_find_bridges);
+
Index: linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-merge/arch/powerpc/sysdev/ppc4xx_pci.h	2007-12-14 15:49:42.000000000 +1100
@@ -0,0 +1,106 @@
+/*
+ * PCI / PCI-X / PCI-Express support for 4xx parts
+ *
+ * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp.
+ *
+ * Bits and pieces extracted from arch/ppc support by
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2002-2005 MontaVista Software Inc.
+ */
+#ifndef __PPC4XX_PCI_H__
+#define __PPC4XX_PCI_H__
+
+/*
+ * 4xx PCI-X bridge register definitions
+ */
+#define PCIX0_VENDID		0x000
+#define PCIX0_DEVID		0x002
+#define PCIX0_COMMAND		0x004
+#define PCIX0_STATUS		0x006
+#define PCIX0_REVID		0x008
+#define PCIX0_CLS		0x009
+#define PCIX0_CACHELS		0x00c
+#define PCIX0_LATTIM		0x00d
+#define PCIX0_HDTYPE		0x00e
+#define PCIX0_BIST		0x00f
+#define PCIX0_BAR0L		0x010
+#define PCIX0_BAR0H		0x014
+#define PCIX0_BAR1		0x018
+#define PCIX0_BAR2L		0x01c
+#define PCIX0_BAR2H		0x020
+#define PCIX0_BAR3		0x024
+#define PCIX0_CISPTR		0x028
+#define PCIX0_SBSYSVID		0x02c
+#define PCIX0_SBSYSID		0x02e
+#define PCIX0_EROMBA		0x030
+#define PCIX0_CAP		0x034
+#define PCIX0_RES0		0x035
+#define PCIX0_RES1		0x036
+#define PCIX0_RES2		0x038
+#define PCIX0_INTLN		0x03c
+#define PCIX0_INTPN		0x03d
+#define PCIX0_MINGNT		0x03e
+#define PCIX0_MAXLTNCY		0x03f
+#define PCIX0_BRDGOPT1		0x040
+#define PCIX0_BRDGOPT2		0x044
+#define PCIX0_ERREN		0x050
+#define PCIX0_ERRSTS		0x054
+#define PCIX0_PLBBESR		0x058
+#define PCIX0_PLBBEARL		0x05c
+#define PCIX0_PLBBEARH		0x060
+#define PCIX0_POM0LAL		0x068
+#define PCIX0_POM0LAH		0x06c
+#define PCIX0_POM0SA		0x070
+#define PCIX0_POM0PCIAL		0x074
+#define PCIX0_POM0PCIAH		0x078
+#define PCIX0_POM1LAL		0x07c
+#define PCIX0_POM1LAH		0x080
+#define PCIX0_POM1SA		0x084
+#define PCIX0_POM1PCIAL		0x088
+#define PCIX0_POM1PCIAH		0x08c
+#define PCIX0_POM2SA		0x090
+#define PCIX0_PIM0SAL		0x098
+#define PCIX0_PIM0SA		PCIX0_PIM0SAL
+#define PCIX0_PIM0LAL		0x09c
+#define PCIX0_PIM0LAH		0x0a0
+#define PCIX0_PIM1SA		0x0a4
+#define PCIX0_PIM1LAL		0x0a8
+#define PCIX0_PIM1LAH		0x0ac
+#define PCIX0_PIM2SAL		0x0b0
+#define PCIX0_PIM2SA		PCIX0_PIM2SAL
+#define PCIX0_PIM2LAL		0x0b4
+#define PCIX0_PIM2LAH		0x0b8
+#define PCIX0_OMCAPID		0x0c0
+#define PCIX0_OMNIPTR		0x0c1
+#define PCIX0_OMMC		0x0c2
+#define PCIX0_OMMA		0x0c4
+#define PCIX0_OMMUA		0x0c8
+#define PCIX0_OMMDATA		0x0cc
+#define PCIX0_OMMEOI		0x0ce
+#define PCIX0_PMCAPID		0x0d0
+#define PCIX0_PMNIPTR		0x0d1
+#define PCIX0_PMC		0x0d2
+#define PCIX0_PMCSR		0x0d4
+#define PCIX0_PMCSRBSE		0x0d6
+#define PCIX0_PMDATA		0x0d7
+#define PCIX0_PMSCRR		0x0d8
+#define PCIX0_CAPID		0x0dc
+#define PCIX0_NIPTR		0x0dd
+#define PCIX0_CMD		0x0de
+#define PCIX0_STS		0x0e0
+#define PCIX0_IDR		0x0e4
+#define PCIX0_CID		0x0e8
+#define PCIX0_RID		0x0ec
+#define PCIX0_PIM0SAH		0x0f8
+#define PCIX0_PIM2SAH		0x0fc
+#define PCIX0_MSGIL		0x100
+#define PCIX0_MSGIH		0x104
+#define PCIX0_MSGOL		0x108
+#define PCIX0_MSGOH		0x10c
+#define PCIX0_IM		0x1f8
+
+
+
+#endif /* __PPC4XX_PCI_H__ */

^ permalink raw reply

* [PATCH 2/21] [POWERPC] Improve support for 4xx indirect DCRs
From: Benjamin Herrenschmidt @ 2007-12-21  4:39 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

Accessing indirect DCRs is done via a pair of address/data DCRs.

Such accesses are thus inherently racy, vs. interrupts, preemption
and possibly SMP if 4xx SMP cores are ever used.

This updates the mfdcri/mtdcri macros in dcr-native.h (which were
so far unused) to use a spinlock.

In addition, add some common definitions to a new dcr-regs.h file.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 include/asm-powerpc/dcr-native.h |   30 ++++++++++------
 include/asm-powerpc/dcr-regs.h   |   71 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 91 insertions(+), 10 deletions(-)

--- linux-merge.orig/include/asm-powerpc/dcr-native.h	2007-12-14 15:48:56.000000000 +1100
+++ linux-merge/include/asm-powerpc/dcr-native.h	2007-12-14 15:49:41.000000000 +1100
@@ -22,6 +22,8 @@
 #ifdef __KERNEL__
 #ifndef __ASSEMBLY__
 
+#include <linux/spinlock.h>
+
 typedef struct {
 	unsigned int base;
 } dcr_host_t;
@@ -55,20 +57,28 @@ do {								\
 } while (0)
 
 /* R/W of indirect DCRs make use of standard naming conventions for DCRs */
-#define mfdcri(base, reg)			\
-({						\
-	mtdcr(base ## _CFGADDR, base ## _ ## reg);	\
-	mfdcr(base ## _CFGDATA);			\
+extern spinlock_t dcr_ind_lock;
+
+#define mfdcri(base, reg)				\
+({							\
+	unsigned long flags; 				\
+	unsigned int val;				\
+	spin_lock_irqsave(&dcr_ind_lock, flags);	\
+	mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg);	\
+	val = mfdcr(DCRN_ ## base ## _CONFIG_DATA);	\
+	spin_unlock_irqrestore(&dcr_ind_lock, flags);	\
+	val;						\
 })
 
-#define mtdcri(base, reg, data)			\
-do {						\
-	mtdcr(base ## _CFGADDR, base ## _ ## reg);	\
-	mtdcr(base ## _CFGDATA, data);		\
+#define mtdcri(base, reg, data)				\
+do {							\
+	unsigned long flags; 				\
+	spin_lock_irqsave(&dcr_ind_lock, flags);	\
+	mtdcr(DCRN_ ## base ## _CONFIG_ADDR, reg);	\
+	mtdcr(DCRN_ ## base ## _CONFIG_DATA, data);	\
+	spin_unlock_irqrestore(&dcr_ind_lock, flags);	\
 } while (0)
 
 #endif /* __ASSEMBLY__ */
 #endif /* __KERNEL__ */
 #endif /* _ASM_POWERPC_DCR_NATIVE_H */
-
-
Index: linux-merge/include/asm-powerpc/dcr-regs.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-merge/include/asm-powerpc/dcr-regs.h	2007-12-14 15:49:41.000000000 +1100
@@ -0,0 +1,71 @@
+/*
+ * Common DCR / SDR / CPR register definitions used on various IBM/AMCC
+ * 4xx processors
+ *
+ *    Copyright 2007 Benjamin Herrenschmidt, IBM Corp
+ *                   <benh@kernel.crashing.org>
+ *
+ * Mostly lifted from asm-ppc/ibm4xx.h by
+ *
+ *    Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
+ *
+ */
+
+#ifndef __DCR_REGS_H__
+#define __DCR_REGS_H__
+
+/*
+ * Most DCRs used for controlling devices such as the MAL, DMA engine,
+ * etc... are obtained for the device tree.
+ *
+ * The definitions in this files are fixed DCRs and indirect DCRs that
+ * are commonly used outside of specific drivers or refer to core
+ * common registers that may occasionally have to be tweaked outside
+ * of the driver main register set
+ */
+
+/* CPRs (440GX and 440SP/440SPe) */
+#define DCRN_CPR0_CONFIG_ADDR	0xc
+#define DCRN_CPR0_CONFIG_DATA	0xd
+
+/* SDRs (440GX and 440SP/440SPe) */
+#define DCRN_SDR0_CONFIG_ADDR 	0xe
+#define DCRN_SDR0_CONFIG_DATA	0xf
+
+#define SDR0_PFC0		0x4100
+#define SDR0_PFC1		0x4101
+#define SDR0_PFC1_EPS		0x1c00000
+#define SDR0_PFC1_EPS_SHIFT	22
+#define SDR0_PFC1_RMII		0x02000000
+#define SDR0_MFR		0x4300
+#define SDR0_MFR_TAH0 		0x80000000  	/* TAHOE0 Enable */
+#define SDR0_MFR_TAH1 		0x40000000  	/* TAHOE1 Enable */
+#define SDR0_MFR_PCM  		0x10000000  	/* PPC440GP irq compat mode */
+#define SDR0_MFR_ECS  		0x08000000  	/* EMAC int clk */
+#define SDR0_MFR_T0TXFL		0x00080000
+#define SDR0_MFR_T0TXFH		0x00040000
+#define SDR0_MFR_T1TXFL		0x00020000
+#define SDR0_MFR_T1TXFH		0x00010000
+#define SDR0_MFR_E0TXFL		0x00008000
+#define SDR0_MFR_E0TXFH		0x00004000
+#define SDR0_MFR_E0RXFL		0x00002000
+#define SDR0_MFR_E0RXFH		0x00001000
+#define SDR0_MFR_E1TXFL		0x00000800
+#define SDR0_MFR_E1TXFH		0x00000400
+#define SDR0_MFR_E1RXFL		0x00000200
+#define SDR0_MFR_E1RXFH		0x00000100
+#define SDR0_MFR_E2TXFL		0x00000080
+#define SDR0_MFR_E2TXFH		0x00000040
+#define SDR0_MFR_E2RXFL		0x00000020
+#define SDR0_MFR_E2RXFH		0x00000010
+#define SDR0_MFR_E3TXFL		0x00000008
+#define SDR0_MFR_E3TXFH		0x00000004
+#define SDR0_MFR_E3RXFL		0x00000002
+#define SDR0_MFR_E3RXFH		0x00000001
+#define SDR0_UART0		0x0120
+#define SDR0_UART1		0x0121
+#define SDR0_UART2		0x0122
+#define SDR0_UART3		0x0123
+#define SDR0_CUST0		0x4000
+
+#endif /* __DCR_REGS_H__ */

^ permalink raw reply

* [PATCH 1/21] [POWERPC] Reworking machine check handling and Fix 440/440A
From: Benjamin Herrenschmidt @ 2007-12-21  4:39 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev

This adds a cputable function pointer for the CPU-side machine
check handling. The semantic is still the same as the old one,
the one in ppc_md. overrides the one in cputable, though
ultimately we'll want to change that so the CPU gets first.

This removes CONFIG_440A which was a problem for multiplatform
kernels and instead fixes up the IVOR at runtime from a setup_cpu
function. The "A" version of the machine check also tweaks the
regs->trap value to differenciate the 2 versions at the C level.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 arch/powerpc/kernel/cpu_setup_44x.S |    9 +++
 arch/powerpc/kernel/cputable.c      |  105 ++++++++++++++++++++++++++++++++++++
 arch/powerpc/kernel/head_44x.S      |   14 +++-
 arch/powerpc/kernel/head_booke.h    |    2 
 arch/powerpc/kernel/traps.c         |   62 ++++++++++++++++-----
 arch/powerpc/platforms/44x/Kconfig  |    5 -
 arch/ppc/kernel/traps.c             |   98 ++++++++++++++++++++++-----------
 include/asm-powerpc/cputable.h      |   13 ++++
 include/asm-powerpc/ptrace.h        |    3 -
 include/asm-powerpc/reg_booke.h     |    3 -
 include/asm-ppc/reg_booke.h         |    2 
 11 files changed, 258 insertions(+), 58 deletions(-)

--- linux-merge.orig/arch/powerpc/kernel/cpu_setup_44x.S	2007-10-15 11:19:35.000000000 +1000
+++ linux-merge/arch/powerpc/kernel/cpu_setup_44x.S	2007-12-20 11:35:40.000000000 +1100
@@ -23,11 +23,20 @@ _GLOBAL(__setup_cpu_440epx)
 	mflr	r4
 	bl	__init_fpu_44x
 	bl	__plb_disable_wrp
+	bl	__fixup_440A_mcheck
 	mtlr	r4
 	blr
 _GLOBAL(__setup_cpu_440grx)
 	b	__plb_disable_wrp
+_GLOBAL(__setup_cpu_440gx)
+_GLOBAL(__setup_cpu_440spe)
+	b	__fixup_440A_mcheck
 
+ /* Temporary fixup for arch/ppc until we kill the whole thing */
+#ifndef CONFIG_PPC_MERGE
+_GLOBAL(__fixup_440A_mcheck)
+	blr
+#endif
 
 /* enable APU between CPU and FPU */
 _GLOBAL(__init_fpu_44x)
Index: linux-merge/arch/powerpc/kernel/cputable.c
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/cputable.c	2007-12-20 11:34:43.000000000 +1100
+++ linux-merge/arch/powerpc/kernel/cputable.c	2007-12-20 11:35:40.000000000 +1100
@@ -33,7 +33,9 @@ EXPORT_SYMBOL(cur_cpu_spec);
 #ifdef CONFIG_PPC32
 extern void __setup_cpu_440ep(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_440epx(unsigned long offset, struct cpu_spec* spec);
+extern void __setup_cpu_440gx(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_440grx(unsigned long offset, struct cpu_spec* spec);
+extern void __setup_cpu_440spe(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_603(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_604(unsigned long offset, struct cpu_spec* spec);
 extern void __setup_cpu_750(unsigned long offset, struct cpu_spec* spec);
@@ -85,6 +87,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power3",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "power3",
 	},
 	{	/* Power3+ */
@@ -99,6 +102,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power3",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "power3",
 	},
 	{	/* Northstar */
@@ -113,6 +117,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* Pulsar */
@@ -127,6 +132,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* I-star */
@@ -141,6 +147,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* S-star */
@@ -155,6 +162,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/rs64",
 		.oprofile_type		= PPC_OPROFILE_RS64,
+		.machine_check		= machine_check_generic,
 		.platform		= "rs64",
 	},
 	{	/* Power4 */
@@ -169,6 +177,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power4",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "power4",
 	},
 	{	/* Power4+ */
@@ -183,6 +192,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/power4",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "power4",
 	},
 	{	/* PPC970 */
@@ -200,6 +210,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970FX */
@@ -217,6 +228,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970MP DD1.0 - no DEEPNAP, use regular 970 init */
@@ -234,6 +246,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970MP",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970MP */
@@ -251,6 +264,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970MP",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* PPC970GX */
@@ -267,6 +281,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_ppc970,
 		.oprofile_cpu_type	= "ppc64/970",
 		.oprofile_type		= PPC_OPROFILE_POWER4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc970",
 	},
 	{	/* Power5 GR */
@@ -286,6 +301,7 @@ static struct cpu_spec __initdata cpu_sp
 		 */
 		.oprofile_mmcra_sihv	= MMCRA_SIHV,
 		.oprofile_mmcra_sipr	= MMCRA_SIPR,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5",
 	},
 	{	/* Power5++ */
@@ -301,6 +317,7 @@ static struct cpu_spec __initdata cpu_sp
 		.oprofile_type		= PPC_OPROFILE_POWER4,
 		.oprofile_mmcra_sihv	= MMCRA_SIHV,
 		.oprofile_mmcra_sipr	= MMCRA_SIPR,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5+",
 	},
 	{	/* Power5 GS */
@@ -317,6 +334,7 @@ static struct cpu_spec __initdata cpu_sp
 		.oprofile_type		= PPC_OPROFILE_POWER4,
 		.oprofile_mmcra_sihv	= MMCRA_SIHV,
 		.oprofile_mmcra_sipr	= MMCRA_SIPR,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5+",
 	},
 	{	/* POWER6 in P5+ mode; 2.04-compliant processor */
@@ -327,6 +345,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_POWER5_PLUS,
 		.icache_bsize		= 128,
 		.dcache_bsize		= 128,
+		.machine_check		= machine_check_generic,
 		.platform		= "power5+",
 	},
 	{	/* Power6 */
@@ -346,6 +365,7 @@ static struct cpu_spec __initdata cpu_sp
 		.oprofile_mmcra_sipr	= POWER6_MMCRA_SIPR,
 		.oprofile_mmcra_clear	= POWER6_MMCRA_THRM |
 			POWER6_MMCRA_OTHER,
+		.machine_check		= machine_check_generic,
 		.platform		= "power6x",
 	},
 	{	/* 2.05-compliant processor, i.e. Power6 "architected" mode */
@@ -356,6 +376,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_POWER6,
 		.icache_bsize		= 128,
 		.dcache_bsize		= 128,
+		.machine_check		= machine_check_generic,
 		.platform		= "power6",
 	},
 	{	/* Cell Broadband Engine */
@@ -372,6 +393,7 @@ static struct cpu_spec __initdata cpu_sp
 		.pmc_type		= PPC_PMC_IBM,
 		.oprofile_cpu_type	= "ppc64/cell-be",
 		.oprofile_type		= PPC_OPROFILE_CELL,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc-cell-be",
 	},
 	{	/* PA Semi PA6T */
@@ -388,6 +410,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_restore		= __restore_cpu_pa6t,
 		.oprofile_cpu_type	= "ppc64/pa6t",
 		.oprofile_type		= PPC_OPROFILE_PA6T,
+		.machine_check		= machine_check_generic,
 		.platform		= "pa6t",
 	},
 	{	/* default match */
@@ -400,6 +423,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 128,
 		.num_pmcs		= 6,
 		.pmc_type		= PPC_PMC_IBM,
+		.machine_check		= machine_check_generic,
 		.platform		= "power4",
 	}
 #endif	/* CONFIG_PPC64 */
@@ -414,6 +438,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_UNIFIED_CACHE | PPC_FEATURE_NO_TB,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc601",
 	},
 	{	/* 603 */
@@ -425,6 +450,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* 603e */
@@ -436,6 +462,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* 603ev */
@@ -447,6 +474,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* 604 */
@@ -459,6 +487,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 2,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 604e */
@@ -471,6 +500,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 604r */
@@ -483,6 +513,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 604ev */
@@ -495,6 +526,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_604,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc604",
 	},
 	{	/* 740/750 (0x4202, don't support TAU ?) */
@@ -507,6 +539,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CX (80100 and 8010x?) */
@@ -519,6 +552,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CX (82201 and 82202) */
@@ -531,6 +565,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CXe (82214) */
@@ -543,6 +578,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CXe "Gekko" (83214) */
@@ -555,6 +591,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750cx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750CL */
@@ -567,6 +604,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 745/755 */
@@ -579,6 +617,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750FX rev 1.x */
@@ -591,6 +630,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750FX rev 2.0 must disable HID0[DPM] */
@@ -603,6 +643,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750FX (All revs except 2.0) */
@@ -615,6 +656,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750fx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 750GX */
@@ -627,6 +669,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750fx,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 740/750 (L2CR bit need fixup for 740) */
@@ -639,6 +682,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_750,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc750",
 	},
 	{	/* 7400 rev 1.1 ? (no TAU) */
@@ -652,6 +696,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_7400,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7400",
 	},
 	{	/* 7400 */
@@ -665,6 +710,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_7400,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7400",
 	},
 	{	/* 7410 */
@@ -678,6 +724,7 @@ static struct cpu_spec __initdata cpu_sp
 		.dcache_bsize		= 32,
 		.num_pmcs		= 4,
 		.cpu_setup		= __setup_cpu_7410,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7400",
 	},
 	{	/* 7450 2.0 - no doze/nap */
@@ -693,6 +740,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7450 2.1 */
@@ -708,6 +756,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7450 2.3 and newer */
@@ -723,6 +772,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7455 rev 1.x */
@@ -738,6 +788,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7455 rev 2.0 */
@@ -753,6 +804,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7455 others */
@@ -768,6 +820,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447/7457 Rev 1.0 */
@@ -783,6 +836,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447/7457 Rev 1.1 */
@@ -798,6 +852,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447/7457 Rev 1.2 and later */
@@ -812,6 +867,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7447A */
@@ -827,6 +883,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 7448 */
@@ -842,6 +899,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_setup		= __setup_cpu_745x,
 		.oprofile_cpu_type      = "ppc/7450",
 		.oprofile_type		= PPC_OPROFILE_G4,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc7450",
 	},
 	{	/* 82xx (8240, 8245, 8260 are all 603e cores) */
@@ -853,6 +911,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* All G2_LE (603e core, plus some) have the same pvr */
@@ -864,6 +923,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* e300c1 (a 603e core, plus some) on 83xx */
@@ -875,6 +935,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* e300c2 (an e300c1 core, plus some, minus FPU) on 83xx */
@@ -886,6 +947,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* e300c3 (e300c1, plus one IU, half cache size) on 83xx */
@@ -908,6 +970,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_603,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 	{	/* default match, we assume split I/D cache & TB (non-601)... */
@@ -918,6 +981,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_generic,
 		.platform		= "ppc603",
 	},
 #endif /* CLASSIC_PPC */
@@ -944,6 +1008,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
 		.icache_bsize		= 16,
 		.dcache_bsize		= 16,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc403",
 	},
 	{	/* 403GCX */
@@ -955,6 +1020,7 @@ static struct cpu_spec __initdata cpu_sp
 		 	PPC_FEATURE_HAS_MMU | PPC_FEATURE_NO_TB,
 		.icache_bsize		= 16,
 		.dcache_bsize		= 16,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc403",
 	},
 	{	/* 403G ?? */
@@ -965,6 +1031,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
 		.icache_bsize		= 16,
 		.dcache_bsize		= 16,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc403",
 	},
 	{	/* 405GP */
@@ -976,6 +1043,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* STB 03xxx */
@@ -987,6 +1055,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* STB 04xxx */
@@ -998,6 +1067,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* NP405L */
@@ -1009,6 +1079,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* NP4GS3 */
@@ -1020,6 +1091,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{   /* NP405H */
@@ -1031,6 +1103,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405GPr */
@@ -1042,6 +1115,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{   /* STBx25xx */
@@ -1053,6 +1127,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405LP */
@@ -1063,6 +1138,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= PPC_FEATURE_32 | PPC_FEATURE_HAS_MMU,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* Xilinx Virtex-II Pro  */
@@ -1074,6 +1150,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* Xilinx Virtex-4 FX */
@@ -1085,6 +1162,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405EP */
@@ -1096,6 +1174,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 	{	/* 405EX */
@@ -1107,6 +1186,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_MMU | PPC_FEATURE_HAS_4xxMAC,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc405",
 	},
 
@@ -1120,6 +1200,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* Use logical PVR for 440EP (logical pvr = pvr | 0x8) */
@@ -1131,6 +1212,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440ep,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{
@@ -1141,6 +1223,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE | PPC_FEATURE_HAS_FPU,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* Use logical PVR for 440EP (logical pvr = pvr | 0x8) */
@@ -1152,6 +1235,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440ep,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* 440GRX */
@@ -1163,6 +1247,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440grx,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* Use logical PVR for 440EPx (logical pvr = pvr | 0x8) */
@@ -1174,6 +1259,7 @@ static struct cpu_spec __initdata cpu_sp
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
 		.cpu_setup		= __setup_cpu_440epx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{	/* 440GP Rev. B */
@@ -1184,6 +1270,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440gp",
 	},
 	{	/* 440GP Rev. C */
@@ -1194,6 +1281,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440gp",
 	},
 	{ /* 440GX Rev. A */
@@ -1204,6 +1292,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440GX Rev. B */
@@ -1214,6 +1304,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440GX Rev. C */
@@ -1224,6 +1316,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440GX Rev. F */
@@ -1234,6 +1328,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440gx,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 	{ /* 440SP Rev. A */
@@ -1244,6 +1340,7 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_4xx,
 		.platform		= "ppc440",
 	},
 	{ /* 440SPe Rev. A */
@@ -1254,6 +1351,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features      = COMMON_USER_BOOKE,
 		.icache_bsize           = 32,
 		.dcache_bsize           = 32,
+		.cpu_setup		= __setup_cpu_440spe,
+		.machine_check		= machine_check_440A,
 		.platform               = "ppc440",
 	},
 	{ /* 440SPe Rev. B */
@@ -1264,6 +1363,8 @@ static struct cpu_spec __initdata cpu_sp
 		.cpu_user_features	= COMMON_USER_BOOKE,
 		.icache_bsize		= 32,
 		.dcache_bsize		= 32,
+		.cpu_setup		= __setup_cpu_440spe,
+		.machine_check		= machine_check_440A,
 		.platform		= "ppc440",
 	},
 #endif /* CONFIG_44x */
@@ -1278,6 +1379,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_EFP_SINGLE |
 			PPC_FEATURE_UNIFIED_CACHE,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_e200,
 		.platform		= "ppc5554",
 	},
 	{	/* e200z6 */
@@ -1291,6 +1393,7 @@ static struct cpu_spec __initdata cpu_sp
 			PPC_FEATURE_HAS_EFP_SINGLE_COMP |
 			PPC_FEATURE_UNIFIED_CACHE,
 		.dcache_bsize		= 32,
+		.machine_check		= machine_check_e200,
 		.platform		= "ppc5554",
 	},
 	{	/* e500 */
@@ -1307,6 +1410,7 @@ static struct cpu_spec __initdata cpu_sp
 		.num_pmcs		= 4,
 		.oprofile_cpu_type	= "ppc/e500",
 		.oprofile_type		= PPC_OPROFILE_BOOKE,
+		.machine_check		= machine_check_e500,
 		.platform		= "ppc8540",
 	},
 	{	/* e500v2 */
@@ -1324,6 +1428,7 @@ static struct cpu_spec __initdata cpu_sp
 		.num_pmcs		= 4,
 		.oprofile_cpu_type	= "ppc/e500",
 		.oprofile_type		= PPC_OPROFILE_BOOKE,
+		.machine_check		= machine_check_e500,
 		.platform		= "ppc8548",
 	},
 #endif
Index: linux-merge/arch/powerpc/kernel/head_44x.S
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/head_44x.S	2007-12-11 15:07:22.000000000 +1100
+++ linux-merge/arch/powerpc/kernel/head_44x.S	2007-12-20 11:35:40.000000000 +1100
@@ -289,11 +289,8 @@ interrupt_base:
 	CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception)
 
 	/* Machine Check Interrupt */
-#ifdef CONFIG_440A
-	MCHECK_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
-#else
 	CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception)
-#endif
+	MCHECK_EXCEPTION(0x0210, MachineCheckA, machine_check_exception)
 
 	/* Data Storage Interrupt */
 	START_EXCEPTION(DataStorage)
@@ -674,6 +671,15 @@ finish_tlb_load:
  */
 
 /*
+ * Adjust the machine check IVOR on 440A cores
+ */
+_GLOBAL(__fixup_440A_mcheck)
+	li	r3,MachineCheckA@l
+	mtspr	SPRN_IVOR1,r3
+	sync
+	blr
+
+/*
  * extern void giveup_altivec(struct task_struct *prev)
  *
  * The 44x core does not have an AltiVec unit.
Index: linux-merge/arch/powerpc/kernel/traps.c
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/traps.c	2007-12-20 11:34:43.000000000 +1100
+++ linux-merge/arch/powerpc/kernel/traps.c	2007-12-20 11:35:40.000000000 +1100
@@ -334,18 +334,25 @@ static inline int check_io_access(struct
 #define clear_single_step(regs)	((regs)->msr &= ~MSR_SE)
 #endif
 
-static int generic_machine_check_exception(struct pt_regs *regs)
+#if defined(CONFIG_4xx)
+int machine_check_4xx(struct pt_regs *regs)
 {
 	unsigned long reason = get_mc_reason(regs);
 
-#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
 	if (reason & ESR_IMCP) {
 		printk("Instruction");
 		mtspr(SPRN_ESR, reason & ~ESR_IMCP);
 	} else
 		printk("Data");
 	printk(" machine check in kernel mode.\n");
-#elif defined(CONFIG_440A)
+
+	return 0;
+}
+
+int machine_check_440A(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	if (reason & ESR_IMCP){
 		printk("Instruction Synchronous Machine Check exception\n");
@@ -375,7 +382,13 @@ static int generic_machine_check_excepti
 		/* Clear MCSR */
 		mtspr(SPRN_MCSR, mcsr);
 	}
-#elif defined (CONFIG_E500)
+	return 0;
+}
+#elif defined(CONFIG_E500)
+int machine_check_e500(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -403,7 +416,14 @@ static int generic_machine_check_excepti
 		printk("Bus - Instruction Parity Error\n");
 	if (reason & MCSR_BUS_RPERR)
 		printk("Bus - Read Parity Error\n");
-#elif defined (CONFIG_E200)
+
+	return 0;
+}
+#elif defined(CONFIG_E200)
+int machine_check_e200(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -421,7 +441,14 @@ static int generic_machine_check_excepti
 		printk("Bus - Read Bus Error on data load\n");
 	if (reason & MCSR_BUS_WRERR)
 		printk("Bus - Write Bus Error on buffered store or cache line push\n");
-#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
+
+	return 0;
+}
+#else
+int machine_check_generic(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from SRR1=%lx): ", reason);
 	switch (reason & 0x601F0000) {
@@ -451,22 +478,26 @@ static int generic_machine_check_excepti
 	default:
 		printk("Unknown values in msr\n");
 	}
-#endif /* CONFIG_4xx */
-
 	return 0;
 }
+#endif /* everything else */
 
 void machine_check_exception(struct pt_regs *regs)
 {
 	int recover = 0;
 
-	/* See if any machine dependent calls */
+	/* See if any machine dependent calls. In theory, we would want
+	 * to call the CPU first, and call the ppc_md. one if the CPU
+	 * one returns a positive number. However there is existing code
+	 * that assumes the board gets a first chance, so let's keep it
+	 * that way for now and fix things later. --BenH.
+	 */
 	if (ppc_md.machine_check_exception)
 		recover = ppc_md.machine_check_exception(regs);
-	else
-		recover = generic_machine_check_exception(regs);
+	else if (cur_cpu_spec->machine_check)
+		recover = cur_cpu_spec->machine_check(regs);
 
-	if (recover)
+	if (recover > 0)
 		return;
 
 	if (user_mode(regs)) {
@@ -476,7 +507,12 @@ void machine_check_exception(struct pt_r
 	}
 
 #if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-	/* the qspan pci read routines can cause machine checks -- Cort */
+	/* the qspan pci read routines can cause machine checks -- Cort
+	 *
+	 * yuck !!! that totally needs to go away ! There are better ways
+	 * to deal with that than having a wart in the mcheck handler.
+	 * -- BenH
+	 */
 	bad_page_fault(regs, regs->dar, SIGBUS);
 	return;
 #endif
Index: linux-merge/arch/powerpc/platforms/44x/Kconfig
===================================================================
--- linux-merge.orig/arch/powerpc/platforms/44x/Kconfig	2007-10-25 13:15:47.000000000 +1000
+++ linux-merge/arch/powerpc/platforms/44x/Kconfig	2007-12-20 11:36:04.000000000 +1100
@@ -62,11 +62,6 @@ config 440GX
 config 440SP
 	bool
 
-config 440A
-	bool
-	depends on 440GX || 440EPX
-	default y
-
 # 44x errata/workaround config symbols, selected by the CPU models above
 config IBM440EP_ERR42
 	bool
Index: linux-merge/arch/powerpc/kernel/head_booke.h
===================================================================
--- linux-merge.orig/arch/powerpc/kernel/head_booke.h	2007-07-27 13:44:42.000000000 +1000
+++ linux-merge/arch/powerpc/kernel/head_booke.h	2007-12-20 11:35:40.000000000 +1100
@@ -166,7 +166,7 @@ label:
 	mfspr	r5,SPRN_ESR;					\
 	stw	r5,_ESR(r11);					\
 	addi	r3,r1,STACK_FRAME_OVERHEAD;			\
-	EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
+	EXC_XFER_TEMPLATE(hdlr, n+4, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \
 			  NOCOPY, mcheck_transfer_to_handler,   \
 			  ret_from_mcheck_exc)
 
Index: linux-merge/include/asm-powerpc/ptrace.h
===================================================================
--- linux-merge.orig/include/asm-powerpc/ptrace.h	2007-09-28 11:42:10.000000000 +1000
+++ linux-merge/include/asm-powerpc/ptrace.h	2007-12-20 11:35:40.000000000 +1100
@@ -106,7 +106,8 @@ extern int ptrace_put_reg(struct task_st
  */
 #define FULL_REGS(regs)		(((regs)->trap & 1) == 0)
 #ifndef __powerpc64__
-#define IS_CRITICAL_EXC(regs)	(((regs)->trap & 2) == 0)
+#define IS_CRITICAL_EXC(regs)	(((regs)->trap & 2) != 0)
+#define IS_MCHECK_EXC(regs)	(((regs)->trap & 4) != 0)
 #endif /* ! __powerpc64__ */
 #define TRAP(regs)		((regs)->trap & ~0xF)
 #ifdef __powerpc64__
Index: linux-merge/include/asm-powerpc/reg_booke.h
===================================================================
--- linux-merge.orig/include/asm-powerpc/reg_booke.h	2007-12-20 11:34:43.000000000 +1100
+++ linux-merge/include/asm-powerpc/reg_booke.h	2007-12-20 11:35:40.000000000 +1100
@@ -218,7 +218,6 @@
 #define	CCR1_TCS	0x00000080 /* Timer Clock Select */
 
 /* Bit definitions for the MCSR. */
-#ifdef CONFIG_440A
 #define MCSR_MCS	0x80000000 /* Machine Check Summary */
 #define MCSR_IB		0x40000000 /* Instruction PLB Error */
 #define MCSR_DRB	0x20000000 /* Data Read PLB Error */
@@ -228,7 +227,7 @@
 #define MCSR_DCSP	0x02000000 /* D-Cache Search Parity Error */
 #define MCSR_DCFP	0x01000000 /* D-Cache Flush Parity Error */
 #define MCSR_IMPE	0x00800000 /* Imprecise Machine Check Exception */
-#endif
+
 #ifdef CONFIG_E500
 #define MCSR_MCP 	0x80000000UL /* Machine Check Input Pin */
 #define MCSR_ICPERR 	0x40000000UL /* I-Cache Parity Error */
Index: linux-merge/include/asm-powerpc/cputable.h
===================================================================
--- linux-merge.orig/include/asm-powerpc/cputable.h	2007-11-27 18:25:29.000000000 +1100
+++ linux-merge/include/asm-powerpc/cputable.h	2007-12-20 11:35:40.000000000 +1100
@@ -57,6 +57,14 @@ enum powerpc_pmc_type {
 	PPC_PMC_PA6T = 2,
 };
 
+struct pt_regs;
+
+extern int machine_check_generic(struct pt_regs *regs);
+extern int machine_check_4xx(struct pt_regs *regs);
+extern int machine_check_440A(struct pt_regs *regs);
+extern int machine_check_e500(struct pt_regs *regs);
+extern int machine_check_e200(struct pt_regs *regs);
+
 /* NOTE WELL: Update identify_cpu() if fields are added or removed! */
 struct cpu_spec {
 	/* CPU is matched via (PVR & pvr_mask) == pvr_value */
@@ -97,6 +105,11 @@ struct cpu_spec {
 
 	/* Name of processor class, for the ELF AT_PLATFORM entry */
 	char		*platform;
+
+	/* Processor specific machine check handling. Return negative
+	 * if the error is fatal, 1 if it was fully recovered and 0 to
+	 * pass up (not CPU originated) */
+	int		(*machine_check)(struct pt_regs *regs);
 };
 
 extern struct cpu_spec		*cur_cpu_spec;
Index: linux-merge/arch/ppc/kernel/traps.c
===================================================================
--- linux-merge.orig/arch/ppc/kernel/traps.c	2007-10-25 13:15:47.000000000 +1000
+++ linux-merge/arch/ppc/kernel/traps.c	2007-12-20 11:35:40.000000000 +1100
@@ -231,39 +231,25 @@ platform_machine_check(struct pt_regs *r
 {
 }
 
-void machine_check_exception(struct pt_regs *regs)
+#if defined(CONFIG_4xx)
+int machine_check_4xx(struct pt_regs *regs)
 {
 	unsigned long reason = get_mc_reason(regs);
 
-	if (user_mode(regs)) {
-		regs->msr |= MSR_RI;
-		_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
-		return;
-	}
-
-#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
-	/* the qspan pci read routines can cause machine checks -- Cort */
-	bad_page_fault(regs, regs->dar, SIGBUS);
-	return;
-#endif
-
-	if (debugger_fault_handler) {
-		debugger_fault_handler(regs);
-		regs->msr |= MSR_RI;
-		return;
-	}
-
-	if (check_io_access(regs))
-		return;
-
-#if defined(CONFIG_4xx) && !defined(CONFIG_440A)
 	if (reason & ESR_IMCP) {
 		printk("Instruction");
 		mtspr(SPRN_ESR, reason & ~ESR_IMCP);
 	} else
 		printk("Data");
 	printk(" machine check in kernel mode.\n");
-#elif defined(CONFIG_440A)
+
+	return 0;
+}
+
+int machine_check_440A(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	if (reason & ESR_IMCP){
 		printk("Instruction Synchronous Machine Check exception\n");
@@ -293,7 +279,13 @@ void machine_check_exception(struct pt_r
 		/* Clear MCSR */
 		mtspr(SPRN_MCSR, mcsr);
 	}
-#elif defined (CONFIG_E500)
+	return 0;
+}
+#elif defined(CONFIG_E500)
+int machine_check_e500(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -305,8 +297,6 @@ void machine_check_exception(struct pt_r
 		printk("Data Cache Push Parity Error\n");
 	if (reason & MCSR_DCPERR)
 		printk("Data Cache Parity Error\n");
-	if (reason & MCSR_GL_CI)
-		printk("Guarded Load or Cache-Inhibited stwcx.\n");
 	if (reason & MCSR_BUS_IAERR)
 		printk("Bus - Instruction Address Error\n");
 	if (reason & MCSR_BUS_RAERR)
@@ -318,12 +308,19 @@ void machine_check_exception(struct pt_r
 	if (reason & MCSR_BUS_RBERR)
 		printk("Bus - Read Data Bus Error\n");
 	if (reason & MCSR_BUS_WBERR)
-		printk("Bus - Write Data Bus Error\n");
+		printk("Bus - Read Data Bus Error\n");
 	if (reason & MCSR_BUS_IPERR)
 		printk("Bus - Instruction Parity Error\n");
 	if (reason & MCSR_BUS_RPERR)
 		printk("Bus - Read Parity Error\n");
-#elif defined (CONFIG_E200)
+
+	return 0;
+}
+#elif defined(CONFIG_E200)
+int machine_check_e200(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from MCSR=%lx): ", reason);
 
@@ -341,7 +338,14 @@ void machine_check_exception(struct pt_r
 		printk("Bus - Read Bus Error on data load\n");
 	if (reason & MCSR_BUS_WRERR)
 		printk("Bus - Write Bus Error on buffered store or cache line push\n");
-#else /* !CONFIG_4xx && !CONFIG_E500 && !CONFIG_E200 */
+
+	return 0;
+}
+#else
+int machine_check_generic(struct pt_regs *regs)
+{
+	unsigned long reason = get_mc_reason(regs);
+
 	printk("Machine check in kernel mode.\n");
 	printk("Caused by (from SRR1=%lx): ", reason);
 	switch (reason & 0x601F0000) {
@@ -371,7 +375,39 @@ void machine_check_exception(struct pt_r
 	default:
 		printk("Unknown values in msr\n");
 	}
-#endif /* CONFIG_4xx */
+	return 0;
+}
+#endif /* everything else */
+
+void machine_check_exception(struct pt_regs *regs)
+{
+	int recover = 0;
+
+	if (cur_cpu_spec->machine_check)
+		recover = cur_cpu_spec->machine_check(regs);
+	if (recover > 0)
+		return;
+
+	if (user_mode(regs)) {
+		regs->msr |= MSR_RI;
+		_exception(SIGBUS, regs, BUS_ADRERR, regs->nip);
+		return;
+	}
+
+#if defined(CONFIG_8xx) && defined(CONFIG_PCI)
+	/* the qspan pci read routines can cause machine checks -- Cort */
+	bad_page_fault(regs, regs->dar, SIGBUS);
+	return;
+#endif
+
+	if (debugger_fault_handler) {
+		debugger_fault_handler(regs);
+		regs->msr |= MSR_RI;
+		return;
+	}
+
+	if (check_io_access(regs))
+		return;
 
 	/*
 	 * Optional platform-provided routine to print out
Index: linux-merge/include/asm-ppc/reg_booke.h
===================================================================
--- linux-merge.orig/include/asm-ppc/reg_booke.h	2007-09-28 11:42:10.000000000 +1000
+++ linux-merge/include/asm-ppc/reg_booke.h	2007-12-20 11:35:40.000000000 +1100
@@ -207,7 +207,7 @@
 #define	CCR1_TCS	0x00000080 /* Timer Clock Select */
 
 /* Bit definitions for the MCSR. */
-#ifdef CONFIG_440A
+#ifdef CONFIG_44x
 #define MCSR_MCS	0x80000000 /* Machine Check Summary */
 #define MCSR_IB		0x40000000 /* Instruction PLB Error */
 #define MCSR_DRB	0x20000000 /* Data Read PLB Error */

^ permalink raw reply

* [PATCH] [POWERPC] Make non-PCI build work again
From: Stephen Rothwell @ 2007-12-21  4:37 UTC (permalink / raw)
  To: paulus; +Cc: ppc-dev

Maple and pasemi both require PCI as does CONFIG_OF_PLATFORM_PCI.
The default setting of CONFIG_ISA_DMA_API is set to match the protection
around the relevant routines in asm/dma.h.

I also had to remove the PMAC platform from the combined build.  The
precis is that to build a 64 bit kernel with no PCI, you can only include
pSeries and iSeries.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 arch/powerpc/Kconfig                  |    3 ++-
 arch/powerpc/platforms/maple/Kconfig  |    1 +
 arch/powerpc/platforms/pasemi/Kconfig |    1 +
 3 files changed, 4 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index 232c298..d40844f 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -160,6 +160,7 @@ config PPC_DCR
 
 config PPC_OF_PLATFORM_PCI
 	bool
+	depends on PCI
 	depends on PPC64 # not supported on 32 bits yet
 	default n
 
@@ -417,7 +418,7 @@ endmenu
 
 config ISA_DMA_API
 	bool
-	default y
+	default !PPC_ISERIES || PCI
 
 menu "Bus options"
 
diff --git a/arch/powerpc/platforms/maple/Kconfig b/arch/powerpc/platforms/maple/Kconfig
index f7c95eb..a6467a5 100644
--- a/arch/powerpc/platforms/maple/Kconfig
+++ b/arch/powerpc/platforms/maple/Kconfig
@@ -1,6 +1,7 @@
 config PPC_MAPLE
 	depends on PPC_MULTIPLATFORM && PPC64
 	bool "Maple 970FX Evaluation Board"
+	select PCI
 	select MPIC
 	select U3_DART
 	select MPIC_U3_HT_IRQS
diff --git a/arch/powerpc/platforms/pasemi/Kconfig b/arch/powerpc/platforms/pasemi/Kconfig
index 2f4dd6e..b3458a1 100644
--- a/arch/powerpc/platforms/pasemi/Kconfig
+++ b/arch/powerpc/platforms/pasemi/Kconfig
@@ -3,6 +3,7 @@ config PPC_PASEMI
 	bool "PA Semi SoC-based platforms"
 	default n
 	select MPIC
+	select PCI
 	select PPC_UDBG_16550
 	select PPC_NATIVE
 	select MPIC_BROKEN_REGREAD
-- 
1.5.3.7

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

^ permalink raw reply related

* [PATCH] [POWERPC] pointers marked as __iomem do not need to be volatile
From: Stephen Rothwell @ 2007-12-21  4:23 UTC (permalink / raw)
  To: paulus; +Cc: ppc-dev

Fixes this warning:

arch/powerpc/platforms/powermac/pci.c: In function 'u3_ht_cfg_access':
arch/powerpc/platforms/powermac/pci.c:354: warning: return discards qualifiers from pointer target type
arch/powerpc/platforms/powermac/pci.c:358: warning: return discards qualifiers from pointer target type

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 include/asm-powerpc/pci-bridge.h |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/asm-powerpc/pci-bridge.h b/include/asm-powerpc/pci-bridge.h
index f7ed2b6..a6ea49e 100644
--- a/include/asm-powerpc/pci-bridge.h
+++ b/include/asm-powerpc/pci-bridge.h
@@ -75,8 +75,8 @@ struct pci_controller {
 #endif
 
 	struct pci_ops *ops;
-	volatile unsigned int __iomem *cfg_addr;
-	volatile void __iomem *cfg_data;
+	unsigned int __iomem *cfg_addr;
+	void __iomem *cfg_data;
 
 #ifndef CONFIG_PPC64
 	/*
-- 
1.5.3.7

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au

^ permalink raw reply related

* [PATCH] [POWERPC] constify the of_device_id passed to of_platform_bus_probe
From: Stephen Rothwell @ 2007-12-21  4:21 UTC (permalink / raw)
  To: paulus; +Cc: ppc-dev

This will allow us to declare const all the statically declared arrrays
of these.

Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 arch/powerpc/kernel/of_platform.c |    8 ++++----
 include/asm-powerpc/of_platform.h |    2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/powerpc/kernel/of_platform.c b/arch/powerpc/kernel/of_platform.c
index 79c04d1..de36e23 100644
--- a/arch/powerpc/kernel/of_platform.c
+++ b/arch/powerpc/kernel/of_platform.c
@@ -40,7 +40,7 @@
  * a bus type in the list
  */
 
-static struct of_device_id of_default_bus_ids[] = {
+static const struct of_device_id of_default_bus_ids[] = {
 	{ .type = "soc", },
 	{ .compatible = "soc", },
 	{ .type = "spider", },
@@ -100,8 +100,8 @@ EXPORT_SYMBOL(of_platform_device_create);
  * @matches: match table, NULL to use the default, OF_NO_DEEP_PROBE to
  * disallow recursive creation of child busses
  */
-static int of_platform_bus_create(struct device_node *bus,
-				  struct of_device_id *matches,
+static int of_platform_bus_create(const struct device_node *bus,
+				  const struct of_device_id *matches,
 				  struct device *parent)
 {
 	struct device_node *child;
@@ -137,7 +137,7 @@ static int of_platform_bus_create(struct device_node *bus,
  */
 
 int of_platform_bus_probe(struct device_node *root,
-			  struct of_device_id *matches,
+			  const struct of_device_id *matches,
 			  struct device *parent)
 {
 	struct device_node *child;
diff --git a/include/asm-powerpc/of_platform.h b/include/asm-powerpc/of_platform.h
index d20a4cd..1bce5ef 100644
--- a/include/asm-powerpc/of_platform.h
+++ b/include/asm-powerpc/of_platform.h
@@ -32,7 +32,7 @@ extern struct of_device *of_platform_device_create(struct device_node *np,
 #define OF_NO_DEEP_PROBE ((struct of_device_id *)-1)
 
 extern int of_platform_bus_probe(struct device_node *root,
-				 struct of_device_id *matches,
+				 const struct of_device_id *matches,
 				 struct device *parent);
 
 extern struct of_device *of_find_device_by_phandle(phandle ph);
-- 
1.5.3.7

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

^ permalink raw reply related

* [PATCH] [POWERPC] The builtin matches for ibmebus.c can be __initdata
From: Stephen Rothwell @ 2007-12-21  4:19 UTC (permalink / raw)
  To: paulus; +Cc: ppc-dev


Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 arch/powerpc/kernel/ibmebus.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/powerpc/kernel/ibmebus.c b/arch/powerpc/kernel/ibmebus.c
index 72fd871..caae49f 100644
--- a/arch/powerpc/kernel/ibmebus.c
+++ b/arch/powerpc/kernel/ibmebus.c
@@ -52,7 +52,7 @@ static struct device ibmebus_bus_device = { /* fake "parent" device */
 struct bus_type ibmebus_bus_type;
 
 /* These devices will automatically be added to the bus during init */
-static struct of_device_id builtin_matches[] = {
+static struct of_device_id __initdata builtin_matches[] = {
 	{ .compatible = "IBM,lhca" },
 	{ .compatible = "IBM,lhea" },
 	{},
-- 
1.5.3.7

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au

^ permalink raw reply related

* Re: [PATCH 0/4] arch/powerpc support for SBC8560 board
From: David Gibson @ 2007-12-21  3:59 UTC (permalink / raw)
  To: Paul Gortmaker; +Cc: linuxppc-dev
In-Reply-To: <20071221033815.GA13921@windriver.com>

On Thu, Dec 20, 2007 at 10:38:15PM -0500, Paul Gortmaker wrote:
> In message: Re: [PATCH 0/4] arch/powerpc support for SBC8560 board
> on 20/12/2007 Kumar Gala wrote:
> 
> >> 3) Add device tree source for Wind River SBC8560 board
> >>
> >> This is probably the most interesting part of the group, given that the
> >> board doesn't use the CPM2 to provide the serial console.  I've made a
> >> duart dts entry that is kind of similar to what is done for the tsi108
> >> on the mpc7448/hpc-ii board, and made sure that the serial had their
> >> parent marked as "soc" (found out the hard way that UARTs were ignored
> >> as possible consoles unless they were soc or tsi108 children...)
> >>
> >> b/arch/powerpc/boot/dts/sbc8560.dts |  203 
> >> +++++++++++++++++++++++++++++++++++-
> >
> > we need to figure out to fix this so we don't need the parent marked as 
> > 'soc'.
> 
> Here is my interpretation of what is happening here -- we come in via
> find_legacy_serial_ports() to pick a console port.  It grabs "chosen"
> to get np stdout, and then checks the parent of the 16550 compat ports
> against the following, requiring at least one of them to match:
> 
> 	parent->type == "soc" ? add_legacy_soc_port()
> 
> 	parent->type == "isa" ? add_legacy_isa_port()
> 
> 	parent->type == "tsi-bridge" ? add_legacy_soc_port()
> 
> 	parent->type == "opb" ? add_legacy_soc_port()
> 
> No match means no serial console, it seems.

Not necessarily.  If the port is picked up by the of_serial driver it
should work.  The drawback is that the console will be initialized
rather late by this method.

>  I figured that parent == "soc"
> was the lesser lie to choose from, but I'm open to an alternate approach 
> that is less apt to make David go "ewww" (an understandable
> reaction...).

Lying in the device tree like this is just bad.  Adding another case
to legacy_serial for your specific case would be ugly but infinitely
preferable to mislabelling the node with type "doc".

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply

* [PATCH] [POWERPC] Add EHEA and EHCA as modules in the ppc64_defconfig
From: Stephen Rothwell @ 2007-12-21  3:54 UTC (permalink / raw)
  To: paulus; +Cc: ppc-dev


Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
---
 arch/powerpc/configs/ppc64_defconfig |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/arch/powerpc/configs/ppc64_defconfig b/arch/powerpc/configs/ppc64_defconfig
index 5760b9f..7695a4c 100644
--- a/arch/powerpc/configs/ppc64_defconfig
+++ b/arch/powerpc/configs/ppc64_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.24-rc4
-# Thu Dec  6 16:49:07 2007
+# Fri Dec 21 14:47:29 2007
 #
 CONFIG_PPC64=y
 
@@ -211,7 +211,7 @@ CONFIG_MMIO_NVRAM=y
 CONFIG_MPIC_U3_HT_IRQS=y
 CONFIG_MPIC_BROKEN_REGREAD=y
 CONFIG_IBMVIO=y
-# CONFIG_IBMEBUS is not set
+CONFIG_IBMEBUS=y
 # CONFIG_PPC_MPC106 is not set
 CONFIG_PPC_970_NAP=y
 CONFIG_PPC_INDIRECT_IO=y
@@ -375,7 +375,7 @@ CONFIG_INET_TUNNEL=y
 CONFIG_INET_XFRM_MODE_TRANSPORT=y
 CONFIG_INET_XFRM_MODE_TUNNEL=y
 CONFIG_INET_XFRM_MODE_BEET=y
-# CONFIG_INET_LRO is not set
+CONFIG_INET_LRO=m
 CONFIG_INET_DIAG=y
 CONFIG_INET_TCP_DIAG=y
 # CONFIG_TCP_CONG_ADVANCED is not set
@@ -929,6 +929,7 @@ CONFIG_SPIDER_NET=m
 CONFIG_NETDEV_10000=y
 # CONFIG_CHELSIO_T1 is not set
 # CONFIG_CHELSIO_T3 is not set
+CONFIG_EHEA=m
 # CONFIG_IXGBE is not set
 CONFIG_IXGB=m
 # CONFIG_IXGB_NAPI is not set
@@ -1558,6 +1559,7 @@ CONFIG_INFINIBAND_ADDR_TRANS=y
 CONFIG_INFINIBAND_MTHCA=m
 CONFIG_INFINIBAND_MTHCA_DEBUG=y
 # CONFIG_INFINIBAND_IPATH is not set
+CONFIG_INFINIBAND_EHCA=m
 # CONFIG_INFINIBAND_AMSO1100 is not set
 # CONFIG_MLX4_INFINIBAND is not set
 CONFIG_INFINIBAND_IPOIB=m
-- 
1.5.3.7

-- 
Cheers,
Stephen Rothwell                    sfr@canb.auug.org.au
http://www.canb.auug.org.au/~sfr/

^ permalink raw reply related

* [PATCH] [POWERPC] Fix possible NULL deref in ppc32 PCI
From: Benjamin Herrenschmidt @ 2007-12-21  3:53 UTC (permalink / raw)
  To: Paul Mackerras; +Cc: linuxppc-dev

The 32-bit PCI code tests if "bus" is non-NULL after calling
pci_scan_bus_parented() in one place but not another before
derefencing it.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
---

 arch/powerpc/kernel/pci_32.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

--- linux-merge.orig/arch/powerpc/kernel/pci_32.c	2007-12-21 14:50:24.000000000 +1100
+++ linux-merge/arch/powerpc/kernel/pci_32.c	2007-12-21 14:51:20.000000000 +1100
@@ -442,9 +442,10 @@ static int __init pcibios_init(void)
 		hose->last_busno = 0xff;
 		bus = pci_scan_bus_parented(hose->parent, hose->first_busno,
 					    hose->ops, hose);
-		if (bus)
+		if (bus) {
 			pci_bus_add_devices(bus);
-		hose->last_busno = bus->subordinate;
+			hose->last_busno = bus->subordinate;
+		}
 		if (pci_assign_all_buses || next_busno <= hose->last_busno)
 			next_busno = hose->last_busno + pcibios_assign_bus_offset;
 	}

^ permalink raw reply

* Re: [PATCH 0/4] arch/powerpc support for SBC8560 board
From: Paul Gortmaker @ 2007-12-21  3:38 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev, david
In-Reply-To: <AD8BE998-401D-4109-8DD0-F26760C0E980@kernel.crashing.org>

In message: Re: [PATCH 0/4] arch/powerpc support for SBC8560 board
on 20/12/2007 Kumar Gala wrote:

>> 3) Add device tree source for Wind River SBC8560 board
>>
>> This is probably the most interesting part of the group, given that the
>> board doesn't use the CPM2 to provide the serial console.  I've made a
>> duart dts entry that is kind of similar to what is done for the tsi108
>> on the mpc7448/hpc-ii board, and made sure that the serial had their
>> parent marked as "soc" (found out the hard way that UARTs were ignored
>> as possible consoles unless they were soc or tsi108 children...)
>>
>> b/arch/powerpc/boot/dts/sbc8560.dts |  203 
>> +++++++++++++++++++++++++++++++++++-
>
> we need to figure out to fix this so we don't need the parent marked as 
> 'soc'.

Here is my interpretation of what is happening here -- we come in via
find_legacy_serial_ports() to pick a console port.  It grabs "chosen"
to get np stdout, and then checks the parent of the 16550 compat ports
against the following, requiring at least one of them to match:

	parent->type == "soc" ? add_legacy_soc_port()

	parent->type == "isa" ? add_legacy_isa_port()

	parent->type == "tsi-bridge" ? add_legacy_soc_port()

	parent->type == "opb" ? add_legacy_soc_port()

No match means no serial console, it seems.  I figured that parent == "soc"
was the lesser lie to choose from, but I'm open to an alternate approach 
that is less apt to make David go "ewww" (an understandable reaction...).

>
> Out of interest how exactly are the duart's wired on the 8560.  Are they 
> off localbus?

The board has a bunch of stuff hanging off of CS5 -- an RTC, a 7 segment
display, an EEPROM, some BCSR-like registers, and of course the two
UARTs which are supposed to be 16C2550.  According to TFM, an EPM7128
PLD is responsible for mashing/sub-decoding this all onto/off of CS5.
CS3 and CS4 are the LB-SDRAM.

>
>> I'd quickly spun together a u-boot 1.2.0 for testing this -- since that 
>> was
>> the quickest route to getting a powerpc capable version.  I'll see what
>> can be done for getting a u-boot 1.3.1 patchset out so the 
>> local-mac-address
>> vs address thing in the dtb isn't an issue.
>
> Probably should add a boot wrapper to work with old non-device tree aware 
> u-boots.

Something to consider, sure -- but I figured working towards a more up
to date u-boot is something people would see more of a future for and
more of an interest in.

Paul.

^ permalink raw reply

* [DTC PATCH 2/2] Add support for binary includes.
From: Scott Wood @ 2007-12-20 16:48 UTC (permalink / raw)
  To: jdl; +Cc: linuxppc-dev, u-boot-users

A property's data can be populated with a file's contents
as follows:

node {
	prop = /bin-include/ "path/to/data";
};

Search paths are not yet implemented; non-absolute lookups are relative to
the directory from which dtc was invoked.

Signed-off-by: Scott Wood <scottwood@freescale.com>
---
 dtc-lexer.l  |    6 ++++++
 dtc-parser.y |   26 ++++++++++++++++++++++++++
 dtc.h        |    1 +
 3 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/dtc-lexer.l b/dtc-lexer.l
index c811b22..1f3e6d6 100644
--- a/dtc-lexer.l
+++ b/dtc-lexer.l
@@ -190,6 +190,12 @@ static int dts_version; /* = 0 */
 			return DT_PROPNODENAME;
 		}
 
+"/bin-include/"	{
+			yylloc.filenum = srcpos_filenum;
+			yylloc.first_line = yylineno;
+			DPRINT("Binary Include\n");
+			return DT_BININCLUDE;
+		}
 
 <*>[[:space:]]+	/* eat whitespace */
 
diff --git a/dtc-parser.y b/dtc-parser.y
index 4a0181d..c7ed715 100644
--- a/dtc-parser.y
+++ b/dtc-parser.y
@@ -21,6 +21,8 @@
 %locations
 
 %{
+#include <sys/stat.h>
+
 #include "dtc.h"
 #include "srcpos.h"
 
@@ -58,6 +60,7 @@ extern struct boot_info *the_boot_info;
 %token <data> DT_STRING
 %token <labelref> DT_LABEL
 %token <labelref> DT_REF
+%token DT_BININCLUDE
 
 %type <data> propdata
 %type <data> propdataprefix
@@ -196,6 +199,29 @@ propdata:
 		{
 			$$ = data_add_marker($1, REF_PATH, $2);
 		}
+	| propdataprefix DT_BININCLUDE DT_STRING
+		{
+			struct stat st;
+			FILE *f;
+			int fd;
+			
+			f = fopen($3.val, "rb");
+			if (!f) {
+				yyerrorf("Cannot open file \"%s\": %s",
+				         $3.val, strerror(errno));
+				YYERROR;
+			}
+
+			fd = fileno(f);
+			if (fstat(fd, &st) < 0) {
+				yyerrorf("Cannot stat file \"%s\": %s",
+				         $3.val, strerror(errno));
+				YYERROR;
+			}
+
+			$$ = data_merge($1, data_copy_file(f, st.st_size));
+			fclose(f);
+		}
 	| propdata DT_LABEL
 		{
 			$$ = data_add_marker($1, LABEL, $2);
diff --git a/dtc.h b/dtc.h
index 9b89689..87b5bb1 100644
--- a/dtc.h
+++ b/dtc.h
@@ -138,6 +138,7 @@ struct data data_grow_for(struct data d, int xlen);
 struct data data_copy_mem(const char *mem, int len);
 struct data data_copy_escape_string(const char *s, int len);
 struct data data_copy_file(FILE *f, size_t len);
+struct data data_bin_include(const char *filename);
 
 struct data data_append_data(struct data d, const void *p, int len);
 struct data data_insert_at_marker(struct data d, struct marker *m,
-- 
1.5.3

^ permalink raw reply related

* Enable RTC for Ebony and Walnut
From: David Gibson @ 2007-12-21  2:21 UTC (permalink / raw)
  To: Josh Boyer; +Cc: linuxppc-dev, Paul Mackerras

This patch extends the Ebony and Walnut platform code to instantiate
the existing ds1742 RTC class driver for the DS1743 RTC/NVRAM chip
found on both those boards.  The patch uses a helper function to scan
the device tree and instantiate the appropriate platform_device based
on it, so it should be easy to extend for other boards which have mmio
mapped RTC chips.

Along with this, the device tree binding for the ds1743 chips is
tweaked, based on the existing DS1385 OF binding found at:
	http://playground.sun.com/1275/proposals/Closed/Remanded/Accepted/346-it.txt
Although that document covers the NVRAM portion of the chip, whereas
here we're interested in the RTC portion, so it's not entirely clear
if that's a good model.

This implements only RTC class driver support - that is /dev/rtc0, not
/dev/rtc, and the low-level get/set time callbacks remain
unimplemented.  That means in order to get at the clock you will
either need a modified version of hwclock which will look at
/dev/rtc0, or you'll need to configure udev to symlink rtc0 to rtc.

Signed-off-by: David Gibson <david@gibson.dropbear.id.au>

Index: working-2.6/arch/powerpc/boot/dts/ebony.dts
===================================================================
--- working-2.6.orig/arch/powerpc/boot/dts/ebony.dts	2007-10-22 13:55:49.000000000 +1000
+++ working-2.6/arch/powerpc/boot/dts/ebony.dts	2007-12-21 13:11:34.000000000 +1100
@@ -150,9 +150,10 @@
 					};
 				};
 
-				ds1743@1,0 {
+				nvram@1,0 {
 					/* NVRAM & RTC */
-					compatible = "ds1743";
+					compatible = "ds1743-nvram";
+					#bytes = <2000>;
 					reg = <1 0 2000>;
 				};
 
Index: working-2.6/arch/powerpc/platforms/44x/ebony.c
===================================================================
--- working-2.6.orig/arch/powerpc/platforms/44x/ebony.c	2007-12-10 10:52:44.000000000 +1100
+++ working-2.6/arch/powerpc/platforms/44x/ebony.c	2007-12-21 13:11:34.000000000 +1100
@@ -18,6 +18,7 @@
 
 #include <linux/init.h>
 #include <linux/of_platform.h>
+#include <linux/rtc.h>
 
 #include <asm/machdep.h>
 #include <asm/prom.h>
@@ -40,6 +41,7 @@ static int __init ebony_device_probe(voi
 		return 0;
 
 	of_platform_bus_probe(NULL, ebony_of_bus, NULL);
+	of_instantiate_rtc();
 
 	return 0;
 }
Index: working-2.6/arch/powerpc/platforms/Kconfig
===================================================================
--- working-2.6.orig/arch/powerpc/platforms/Kconfig	2007-12-10 10:52:44.000000000 +1100
+++ working-2.6/arch/powerpc/platforms/Kconfig	2007-12-21 13:11:34.000000000 +1100
@@ -315,6 +315,9 @@ config FSL_ULI1575
 config CPM
 	bool
 
+config OF_RTC
+	bool
+
 source "arch/powerpc/sysdev/bestcomm/Kconfig"
 
 endmenu
Index: working-2.6/arch/powerpc/sysdev/Makefile
===================================================================
--- working-2.6.orig/arch/powerpc/sysdev/Makefile	2007-12-21 13:11:32.000000000 +1100
+++ working-2.6/arch/powerpc/sysdev/Makefile	2007-12-21 13:11:34.000000000 +1100
@@ -27,6 +27,7 @@ obj-$(CONFIG_PPC_I8259)		+= i8259.o
 obj-$(CONFIG_PPC_83xx)		+= ipic.o
 obj-$(CONFIG_4xx)		+= uic.o
 obj-$(CONFIG_XILINX_VIRTEX)	+= xilinx_intc.o
+obj-$(CONFIG_OF_RTC)		+= of_rtc.o
 endif
 
 # Temporary hack until we have migrated to asm-powerpc
Index: working-2.6/arch/powerpc/sysdev/of_rtc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ working-2.6/arch/powerpc/sysdev/of_rtc.c	2007-12-21 13:11:34.000000000 +1100
@@ -0,0 +1,59 @@
+/*
+ * Instantiate mmio-mapped RTC chips based on device tree information
+ *
+ * Copyright 2007 David Gibson <dwg@au1.ibm.com>, IBM Corporation.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <asm/of_platform.h>
+
+static struct {
+	char *compatible;
+	char *plat_name;
+} of_rtc_table[] = {
+	{ "ds1743-nvram", "rtc-ds1742" },
+};
+
+void __init of_instantiate_rtc(void)
+{
+	struct device_node *node;
+	int err;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(of_rtc_table); i++) {
+		char *compatible = of_rtc_table[i].compatible;
+		char *plat_name = of_rtc_table[i].plat_name;
+
+		for_each_compatible_node(node, NULL, compatible) {
+			struct resource *res;
+
+			res = kmalloc(sizeof(*res), GFP_KERNEL);
+			if (!res) {
+				printk(KERN_ERR "OF RTC: Out of memory "
+				       "allocating resource structure for %s\n",
+				       node->full_name);
+				continue;
+			}
+
+			err = of_address_to_resource(node, 0, res);
+			if (err) {
+				printk(KERN_ERR "OF RTC: Error "
+				       "translating resources for %s\n",
+				       node->full_name);
+				continue;
+			}
+
+			printk(KERN_INFO "OF_RTC: %s is a %s @ 0x%llx-0x%llx\n",
+			       node->full_name, plat_name,
+			       (unsigned long long)res->start,
+			       (unsigned long long)res->end);
+			platform_device_register_simple(plat_name, -1, res, 1);
+		}
+	}
+}
Index: working-2.6/arch/powerpc/platforms/44x/Kconfig
===================================================================
--- working-2.6.orig/arch/powerpc/platforms/44x/Kconfig	2007-12-10 10:52:44.000000000 +1100
+++ working-2.6/arch/powerpc/platforms/44x/Kconfig	2007-12-21 13:11:34.000000000 +1100
@@ -11,6 +11,7 @@ config EBONY
 	depends on 44x
 	default y
 	select 440GP
+	select OF_RTC
 	help
 	  This option enables support for the IBM PPC440GP evaluation board.
 
Index: working-2.6/include/asm-powerpc/of_platform.h
===================================================================
--- working-2.6.orig/include/asm-powerpc/of_platform.h	2007-12-21 13:11:32.000000000 +1100
+++ working-2.6/include/asm-powerpc/of_platform.h	2007-12-21 13:11:34.000000000 +1100
@@ -37,4 +37,6 @@ extern int of_platform_bus_probe(struct 
 
 extern struct of_device *of_find_device_by_phandle(phandle ph);
 
+extern void of_instantiate_rtc(void);
+
 #endif	/* _ASM_POWERPC_OF_PLATFORM_H */
Index: working-2.6/arch/powerpc/boot/dts/walnut.dts
===================================================================
--- working-2.6.orig/arch/powerpc/boot/dts/walnut.dts	2007-12-10 10:52:43.000000000 +1100
+++ working-2.6/arch/powerpc/boot/dts/walnut.dts	2007-12-21 13:11:34.000000000 +1100
@@ -168,9 +168,10 @@
 				};
 			};
 
-			ds1743@1,0 {
+			nvram@1,0 {
 				/* NVRAM and RTC */
-				compatible = "ds1743";
+				compatible = "ds1743-nvram";
+				#bytes = <2000>;
 				reg = <1 0 2000>;
 			};
 
Index: working-2.6/arch/powerpc/platforms/40x/walnut.c
===================================================================
--- working-2.6.orig/arch/powerpc/platforms/40x/walnut.c	2007-12-21 13:14:53.000000000 +1100
+++ working-2.6/arch/powerpc/platforms/40x/walnut.c	2007-12-21 13:15:05.000000000 +1100
@@ -39,6 +39,7 @@ static int __init walnut_device_probe(vo
 
 	/* FIXME: do bus probe here */
 	of_platform_bus_probe(NULL, walnut_of_bus, NULL);
+	of_instantiate_rtc();
 
 	return 0;
 }

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply

* Re: [PATCH v3 2/2] mpc82xx: Embedded Planet EP8248E support
From: David Gibson @ 2007-12-21  0:31 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-dev
In-Reply-To: <20071220174524.GB21048@loki.buserror.net>

On Thu, Dec 20, 2007 at 11:45:24AM -0600, Scott Wood wrote:
> This board is also resold by Freescale under the names
> "QUICCStart MPC8248 Evaluation System" and "CWH-PPC-8248N-VE".
> 
> Signed-off-by: Scott Wood <scottwood@freescale.com>
[snip]
> +		bcsr@1,0 {
> +			#address-cells = <2>;
> +			#size-cells = <1>;
> +			reg = <1 0 0x10>;
> +			compatible = "fsl,ep8248e-bcsr";
> +			ranges;
> +
> +			mdio {
> +				device_type = "mdio";

Shouldn't have this device_type.

> +				compatible = "fsl,ep8248e-mdio-bitbang";
> +				#address-cells = <1>;
> +				#size-cells = <0>;
> +				reg = <1 8 1>;
> +
> +				PHY0: ethernet-phy@0 {
> +					interrupt-parent = <&PIC>;
> +					reg = <0>;
> +					device_type = "ethernet-phy";
> +				};
> +
> +				PHY1: ethernet-phy@1 {
> +					interrupt-parent = <&PIC>;
> +					reg = <1>;
> +					device_type = "ethernet-phy";
> +				};
> +			};
> +		};
> +	};

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply

* Re: [RESEND DTC PATCH 2/2] Add support for binary includes.
From: David Gibson @ 2007-12-21  0:29 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-dev, jdl, u-boot-users
In-Reply-To: <20071220195259.GA1238@ld0162-tx32.am.freescale.net>

On Thu, Dec 20, 2007 at 01:52:59PM -0600, Scott Wood wrote:
> A property's data can be populated with a file's contents
> as follows:
> 
> node {
> 	prop = /bin-include/ "path/to/data";
> };

I'd be inclined to use /incbin/ rather than /bin-include/.  It's only
slightly less obvious, but it's then the same as the gas pseudo-op as
well as being a little briefer.

> Search paths are not yet implemented; non-absolute lookups are relative to
> the directory from which dtc was invoked.

Hrm.  I think that's a bit too bogus.  Although it's rather more work
to implement, I think we have to make relative paths relative to the
location of the dts file until search paths are implemented.

> Signed-off-by: Scott Wood <scottwood@freescale.com>
> ---
> Apologies if you get this twice, but AFAICT the original got eaten by our
> mail server.
> 
>  dtc-lexer.l  |    6 ++++++
>  dtc-parser.y |   26 ++++++++++++++++++++++++++
>  dtc.h        |    1 +
>  3 files changed, 33 insertions(+), 0 deletions(-)
> 
> diff --git a/dtc-lexer.l b/dtc-lexer.l
> index c811b22..1f3e6d6 100644
> --- a/dtc-lexer.l
> +++ b/dtc-lexer.l
> @@ -190,6 +190,12 @@ static int dts_version; /* = 0 */
>  			return DT_PROPNODENAME;
>  		}
>  
> +"/bin-include/"	{
> +			yylloc.filenum = srcpos_filenum;
> +			yylloc.first_line = yylineno;
> +			DPRINT("Binary Include\n");
> +			return DT_BININCLUDE;
> +		}
>  
>  <*>[[:space:]]+	/* eat whitespace */
>  
> diff --git a/dtc-parser.y b/dtc-parser.y
> index 4a0181d..c7ed715 100644
> --- a/dtc-parser.y
> +++ b/dtc-parser.y
> @@ -21,6 +21,8 @@
>  %locations
>  
>  %{
> +#include <sys/stat.h>
> +
>  #include "dtc.h"
>  #include "srcpos.h"
>  
> @@ -58,6 +60,7 @@ extern struct boot_info *the_boot_info;
>  %token <data> DT_STRING
>  %token <labelref> DT_LABEL
>  %token <labelref> DT_REF
> +%token DT_BININCLUDE
>  
>  %type <data> propdata
>  %type <data> propdataprefix
> @@ -196,6 +199,29 @@ propdata:
>  		{
>  			$$ = data_add_marker($1, REF_PATH, $2);
>  		}
> +	| propdataprefix DT_BININCLUDE DT_STRING
> +		{
> +			struct stat st;
> +			FILE *f;
> +			int fd;
> +			
> +			f = fopen($3.val, "rb");
> +			if (!f) {
> +				yyerrorf("Cannot open file \"%s\": %s",
> +				         $3.val, strerror(errno));
> +				YYERROR;

Hrm.  I'm not sure that being unable to open the file should cause a
*parse* error which is what YYERROR will do.  Probably better to print
an error message, but let the parsing continue, with the property
value being as though the file were empty.

> +			}
> +
> +			fd = fileno(f);
> +			if (fstat(fd, &st) < 0) {
> +				yyerrorf("Cannot stat file \"%s\": %s",
> +				         $3.val, strerror(errno));
> +				YYERROR;
> +			}

I'm also not sure that stat()ing the file is a good way to get the
size.  This requires that the included file be a regular file with a
sane st_size value, and I can imagine cases where it might be useful
to incbin from a /dev node or other special file.  Obviosuly
implementing that will require work to data_copy_file().

Actually, I think the way to go here would be to have two variants of
the incbin directive:  one which takes just a filename and includes
the whole file contents, another which takes a filename and a number
and includes just the first N bytes of the file.

> +			$$ = data_merge($1, data_copy_file(f, st.st_size));
> +			fclose(f);
> +		}
>  	| propdata DT_LABEL
>  		{
>  			$$ = data_add_marker($1, LABEL, $2);
> diff --git a/dtc.h b/dtc.h
> index 9b89689..87b5bb1 100644
> --- a/dtc.h
> +++ b/dtc.h
> @@ -138,6 +138,7 @@ struct data data_grow_for(struct data d, int xlen);
>  struct data data_copy_mem(const char *mem, int len);
>  struct data data_copy_escape_string(const char *s, int len);
>  struct data data_copy_file(FILE *f, size_t len);
> +struct data data_bin_include(const char *filename);

This looks like a hangover from an earlier version.

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply

* Re: patch pci-fix-bus-resource-assignment-on-32-bits-with-64b-resources.patch added to gregkh-2.6 tree
From: Greg KH @ 2007-12-21  0:14 UTC (permalink / raw)
  To: Benjamin Herrenschmidt; +Cc: greg, linux-kernel, linuxppc-dev
In-Reply-To: <1198194973.6779.35.camel@pasglop>

On Fri, Dec 21, 2007 at 10:56:13AM +1100, Benjamin Herrenschmidt wrote:
> 
> > The current pci_assign_unassigned_resources() code doesn't work properly
> > on 32 bits platforms with 64 bits resources. The main reason is the use
> > of unsigned long in various places instead of resource_size_t.
> > 
> > This fixes it, along with some tricks to avoid casting to 64 bits on
> > platforms that don't need it in every printk around.
> 
> Can you still edit the changelog ?

Yes I can.

> If yes, please remove that sentence
> since the latest version (which you put in your tree) doesn't actually
> have those tricks anymore as per discussion on the list...

Ok, now done, thanks.

greg k-h

^ permalink raw reply

* Re: [DTC PATCH 1/2] Add yyerrorf() for formatted error messages.
From: David Gibson @ 2007-12-21  0:04 UTC (permalink / raw)
  To: Scott Wood; +Cc: linuxppc-dev, jdl, u-boot-users
In-Reply-To: <20071220164823.GA32193@ld0162-tx32.am.freescale.net>

On Thu, Dec 20, 2007 at 10:48:23AM -0600, Scott Wood wrote:
> Signed-off-by: Scott Wood <scottwood@freescale.com>

No need for a new function.  If yyerror() is defined as a varargs
function it's still compatible with bison's built-in usage.

Oh, and while you're at it, you can kill off the bogus prototype for
yyerror() in treesource.c

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply

* Re: [PATCH 0/4] arch/powerpc support for SBC8560 board
From: Paul Gortmaker @ 2007-12-21  0:00 UTC (permalink / raw)
  To: Kumar Gala; +Cc: linuxppc-dev
In-Reply-To: <DAA17734-F900-42D2-9BA6-3522D7D59D2B@kernel.crashing.org>

Kumar Gala wrote:
> Can we get ride of sbc85xx in arch/ppc with these patches?
>
> - k

That was the plan, yes.

P.

^ permalink raw reply

* Re: [PATCH] ASoC drivers for the Freescale MPC8610 SoC
From: David Gibson @ 2007-12-21  0:00 UTC (permalink / raw)
  To: Jon Smirl; +Cc: Scott Wood, linuxppc-dev, alsa-devel, Timur Tabi
In-Reply-To: <9e4733910712201513t329e2a49j420cb3b4ece5057@mail.gmail.com>

On Thu, Dec 20, 2007 at 06:13:31PM -0500, Jon Smirl wrote:
> On 12/20/07, Scott Wood <scottwood@freescale.com> wrote:
> > Timur Tabi wrote:
> > > Jon Smirl wrote:
> > >
> > >> mpc5200 does it like this:
> > >> of_platform_bus_probe(NULL, NULL, NULL);
> > >
> > > I think that tells the OF base code to probe everything in the device tree,
> > > which is probably overkill.  I think fsl_soc.c covers most of the device tree,
> > > but the SSI is not defined in fsl_soc.c.
> >
> > Not quite; it tells it to use a built-in list of bus matches.  Most of
> > which are device_type-based, FWIW.
> 
> Here's the default. Using NULL would work.

It might work, but using the default list is discouraged.  Pass an
explicit list of match ids for the buses you need to scan instead (and
use compatible to match them, not device_type).

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ permalink raw reply

* ppc4xx_dma
From: Ronnie Hedlund @ 2007-12-20 22:41 UTC (permalink / raw)
  To: linuxppc-embedded

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

Hi, 

DMA code (for the EBC) is broken for ppc4xx as it is (for all CPUs) in the linux kernel.
This (or some of it) should be implemented in the ppc kernel (since the same broken code has been provided for many years).

This works, and is thread safe. It can use more than one channel in parallel, without causing the "one in a million"-type of errors that WILL happen using the current code.


The old post regarding this matter.
http://ozlabs.org/pipermail/linuxppc-embedded/2005-December/021225.html

It is true that this code does handle the dma-memory in a new way, memory for the sg list is allocated dynamically, which takes a little more CPU time at allocation, but it can still operate on any DMA transfer size (as opposed to the hard coded value (<600Kb) used in current code). Reallocation can be prevented by keeping an allocation for many concurrent transfers.


Hope to help someone, but I'm somewhat shocked that the DMA code in the current 2.6 kernel is in the same state as it was many years ago in the 2.4 kernel, even though patches exists...
Seems everyone that uses the DMA->PLB3 (EBC) needs to patch the kernel code them selves (I'm probably the only one using it/has the hardware to run it, since not much has happened).

[Maybe patches like this are implemented already, then this post was not necessary]

Which you a nice DMA.
/Ronnie Hedlund
 


[-- Attachment #2: ppc4xx_dma.h --]
[-- Type: text/plain, Size: 19535 bytes --]

/*
 * include/asm-ppc/ppc4xx_dma.h
 *
 * IBM PPC4xx DMA engine library
 *
 * Copyright 2000-2004 MontaVista Software Inc.
 *
 * Cleaned up a bit more, Matt Porter <mporter@kernel.crashing.org>
 *
 * Original code by Armin Kuster <akuster@mvista.com>
 * and Pete Popov <ppopov@mvista.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.
 *
 * 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.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef __KERNEL__
#ifndef __ASMPPC_PPC4xx_DMA_H
#define __ASMPPC_PPC4xx_DMA_H

#include <linux/types.h>
#include <asm/mmu.h>
#include <asm/ibm4xx.h>

#undef DEBUG_4xxDMA

#define MAX_PPC4xx_DMA_CHANNELS		4

/*
 * Function return status codes
 * These values are used to indicate whether or not the function
 * call was successful, or a bad/invalid parameter was passed.
 */
#define DMA_STATUS_GOOD			0
#define DMA_STATUS_BAD_CHANNEL		1
#define DMA_STATUS_BAD_HANDLE		2
#define DMA_STATUS_BAD_MODE		3
#define DMA_STATUS_NULL_POINTER		4
#define DMA_STATUS_OUT_OF_MEMORY	5
#define DMA_STATUS_SGL_LIST_EMPTY	6
#define DMA_STATUS_GENERAL_ERROR	7
#define DMA_STATUS_CHANNEL_NOTFREE	8

#define DMA_CHANNEL_BUSY		0x80000000

/*
 * These indicate status as returned from the DMA Status Register.
 */
#define DMA_STATUS_NO_ERROR	0
#define DMA_STATUS_CS		1	/* Count Status        */
#define DMA_STATUS_TS		2	/* Transfer Status     */
#define DMA_STATUS_DMA_ERROR	3	/* DMA Error Occurred  */
#define DMA_STATUS_DMA_BUSY	4	/* The channel is busy */


/*
 * DMA Channel Control Registers
 */

#if defined(CONFIG_44x) && !defined(CONFIG_440EP)
#define	PPC4xx_DMA_64BIT
#define DMA_CR_OFFSET 1
#else
#define DMA_CR_OFFSET 0
#endif

#define DMA_CE_ENABLE        (1<<31)	/* DMA Channel Enable */
#define SET_DMA_CE_ENABLE(x) (((x)&0x1)<<31)
#define GET_DMA_CE_ENABLE(x) (((x)&DMA_CE_ENABLE)>>31)

#define DMA_CIE_ENABLE        (1<<30)	/* DMA Channel Interrupt Enable */
#define SET_DMA_CIE_ENABLE(x) (((x)&0x1)<<30)
#define GET_DMA_CIE_ENABLE(x) (((x)&DMA_CIE_ENABLE)>>30)

#define DMA_TD                (1<<29)
#define SET_DMA_TD(x)         (((x)&0x1)<<29)
#define GET_DMA_TD(x)         (((x)&DMA_TD)>>29)

#define DMA_PL                (1<<28)	/* Peripheral Location */
#define SET_DMA_PL(x)         (((x)&0x1)<<28)
#define GET_DMA_PL(x)         (((x)&DMA_PL)>>28)

#define EXTERNAL_PERIPHERAL    0
#define INTERNAL_PERIPHERAL    1

#define SET_DMA_PW(x)     (((x)&0x3)<<(26-DMA_CR_OFFSET))	/* Peripheral Width */
#define DMA_PW_MASK       SET_DMA_PW(3)
#define   PW_8                 0
#define   PW_16                1
#define   PW_32                2
#define   PW_64                3
/* FIXME: Add PW_128 support for 440GP DMA block */
#define GET_DMA_PW(x)     (((x)&DMA_PW_MASK)>>(26-DMA_CR_OFFSET))

#define DMA_DAI           (1<<(25-DMA_CR_OFFSET))	/* Destination Address Increment */
#define SET_DMA_DAI(x)    (((x)&0x1)<<(25-DMA_CR_OFFSET))

#define DMA_SAI           (1<<(24-DMA_CR_OFFSET))	/* Source Address Increment */
#define SET_DMA_SAI(x)    (((x)&0x1)<<(24-DMA_CR_OFFSET))

#define DMA_BEN           (1<<(23-DMA_CR_OFFSET))	/* Buffer Enable */
#define SET_DMA_BEN(x)    (((x)&0x1)<<(23-DMA_CR_OFFSET))

#define SET_DMA_TM(x)     (((x)&0x3)<<(21-DMA_CR_OFFSET))	/* Transfer Mode */
#define DMA_TM_MASK       SET_DMA_TM(3)
#define   TM_PERIPHERAL        0	/* Peripheral */
#define   TM_RESERVED          1	/* Reserved */
#define   TM_S_MM              2	/* Memory to Memory */
#define   TM_D_MM              3	/* Device Paced Memory to Memory */
#define GET_DMA_TM(x)     (((x)&DMA_TM_MASK)>>(21-DMA_CR_OFFSET))

#define SET_DMA_PSC(x)    (((x)&0x3)<<(19-DMA_CR_OFFSET))	/* Peripheral Setup Cycles */
#define DMA_PSC_MASK      SET_DMA_PSC(3)
#define GET_DMA_PSC(x)    (((x)&DMA_PSC_MASK)>>(19-DMA_CR_OFFSET))

#define SET_DMA_PWC(x)    (((x)&0x3F)<<(13-DMA_CR_OFFSET))	/* Peripheral Wait Cycles */
#define DMA_PWC_MASK      SET_DMA_PWC(0x3F)
#define GET_DMA_PWC(x)    (((x)&DMA_PWC_MASK)>>(13-DMA_CR_OFFSET))

#define SET_DMA_PHC(x)    (((x)&0x7)<<(10-DMA_CR_OFFSET))	/* Peripheral Hold Cycles */
#define DMA_PHC_MASK      SET_DMA_PHC(0x7)
#define GET_DMA_PHC(x)    (((x)&DMA_PHC_MASK)>>(10-DMA_CR_OFFSET))

#define DMA_ETD_OUTPUT     (1<<(9-DMA_CR_OFFSET))	/* EOT pin is a TC output */
#define SET_DMA_ETD(x)     (((x)&0x1)<<(9-DMA_CR_OFFSET))

#define DMA_TCE_ENABLE     (1<<(8-DMA_CR_OFFSET))
#define SET_DMA_TCE(x)     (((x)&0x1)<<(8-DMA_CR_OFFSET))

#define DMA_DEC            (1<<(2))	/* Address Decrement */
#define SET_DMA_DEC(x)     (((x)&0x1)<<2)
#define GET_DMA_DEC(x)     (((x)&DMA_DEC)>>2)


/*
 * Transfer Modes
 * These modes are defined in a way that makes it possible to
 * simply "or" in the value in the control register.
 */

#define DMA_MODE_MM		(SET_DMA_TM(TM_S_MM))	/* memory to memory */

				/* Device-paced memory to memory, */
				/* device is at source address    */
#define DMA_MODE_MM_DEVATSRC	(DMA_TD | SET_DMA_TM(TM_D_MM))

				/* Device-paced memory to memory,      */
				/* device is at destination address    */
#define DMA_MODE_MM_DEVATDST	(SET_DMA_TM(TM_D_MM))

/* 405gp/440gp */
#define SET_DMA_PREFETCH(x)   (((x)&0x3)<<(4-DMA_CR_OFFSET))	/* Memory Read Prefetch */
#define DMA_PREFETCH_MASK      SET_DMA_PREFETCH(3)
#define   PREFETCH_1           0	/* Prefetch 1 Double Word */
#define   PREFETCH_2           1
#define   PREFETCH_4           2
#define GET_DMA_PREFETCH(x) (((x)&DMA_PREFETCH_MASK)>>(4-DMA_CR_OFFSET))

#define DMA_PCE            (1<<(3-DMA_CR_OFFSET))	/* Parity Check Enable */
#define SET_DMA_PCE(x)     (((x)&0x1)<<(3-DMA_CR_OFFSET))
#define GET_DMA_PCE(x)     (((x)&DMA_PCE)>>(3-DMA_CR_OFFSET))

/* stb3x */

#define DMA_ECE_ENABLE (1<<5)
#define SET_DMA_ECE(x) (((x)&0x1)<<5)
#define GET_DMA_ECE(x) (((x)&DMA_ECE_ENABLE)>>5)

#define DMA_TCD_DISABLE	(1<<4)
#define SET_DMA_TCD(x) (((x)&0x1)<<4)
#define GET_DMA_TCD(x) (((x)&DMA_TCD_DISABLE)>>4)

typedef uint32_t sgl_handle_t;

#ifdef CONFIG_PPC4xx_EDMA

#ifdef CONFIG_VDR
#define SGL_LIST_SIZE (8*4096)
#else
#define SGL_LIST_SIZE 4096
#endif
#define DMA_PPC4xx_SIZE SGL_LIST_SIZE

#define SET_DMA_PRIORITY(x)   (((x)&0x3)<<(6-DMA_CR_OFFSET))	/* DMA Channel Priority */
#define DMA_PRIORITY_MASK SET_DMA_PRIORITY(3)
#define PRIORITY_LOW           0
#define PRIORITY_MID_LOW       1
#define PRIORITY_MID_HIGH      2
#define PRIORITY_HIGH          3
#define GET_DMA_PRIORITY(x) (((x)&DMA_PRIORITY_MASK)>>(6-DMA_CR_OFFSET))

/*
 * DMA Polarity Configuration Register
 */
#define DMAReq_ActiveLow(chan) (1<<(31-(chan*3)))
#define DMAAck_ActiveLow(chan) (1<<(30-(chan*3)))
#define EOT_ActiveLow(chan)    (1<<(29-(chan*3)))	/* End of Transfer */

/*
 * DMA Sleep Mode Register
 */
#define SLEEP_MODE_ENABLE (1<<21)

/*
 * DMA Status Register
 */
#define DMA_CS0           (1<<31)	/* Terminal Count has been reached */
#define DMA_CS1           (1<<30)
#define DMA_CS2           (1<<29)
#define DMA_CS3           (1<<28)

#define DMA_TS0           (1<<27)	/* End of Transfer has been requested */
#define DMA_TS1           (1<<26)
#define DMA_TS2           (1<<25)
#define DMA_TS3           (1<<24)

#define DMA_CH0_ERR       (1<<23)	/* DMA Chanel 0 Error */
#define DMA_CH1_ERR       (1<<22)
#define DMA_CH2_ERR       (1<<21)
#define DMA_CH3_ERR       (1<<20)

#define DMA_IN_DMA_REQ0   (1<<19)	/* Internal DMA Request is pending */
#define DMA_IN_DMA_REQ1   (1<<18)
#define DMA_IN_DMA_REQ2   (1<<17)
#define DMA_IN_DMA_REQ3   (1<<16)

#define DMA_EXT_DMA_REQ0  (1<<15)	/* External DMA Request is pending */
#define DMA_EXT_DMA_REQ1  (1<<14)
#define DMA_EXT_DMA_REQ2  (1<<13)
#define DMA_EXT_DMA_REQ3  (1<<12)

#define DMA_CH0_BUSY      (1<<11)	/* DMA Channel 0 Busy */
#define DMA_CH1_BUSY      (1<<10)
#define DMA_CH2_BUSY       (1<<9)
#define DMA_CH3_BUSY       (1<<8)

#define DMA_SG0            (1<<7)	/* DMA Channel 0 Scatter/Gather in progress */
#define DMA_SG1            (1<<6)
#define DMA_SG2            (1<<5)
#define DMA_SG3            (1<<4)

/* DMA Channel Count Register */
#define DMA_CTC_BTEN     (1<<23)    /* Burst Enable/Disable bit */
#define DMA_CTC_BSIZ_MSK (3<<21)    /* Mask of the Burst size bits */
#define DMA_CTC_BSIZ_2   (0)
#define DMA_CTC_BSIZ_4   (1<<21)
#define DMA_CTC_BSIZ_8   (2<<21)
#define DMA_CTC_BSIZ_16  (3<<21)

/*
 * DMA SG Command Register
 */
#define SSG_ENABLE(chan)   	(1<<(31-chan))	/* Start Scatter Gather */
#define SSG_MASK_ENABLE(chan)	(1<<(15-chan))	/* Enable writing to SSG0 bit */

/*
 * DMA Scatter/Gather Descriptor Bit fields
 */
#define SG_LINK            (1<<31)	/* Link */
#define SG_TCI_ENABLE      (1<<29)	/* Enable Terminal Count Interrupt */
#define SG_ETI_ENABLE      (1<<28)	/* Enable End of Transfer Interrupt */
#define SG_ERI_ENABLE      (1<<27)	/* Enable Error Interrupt */
#define SG_COUNT_MASK       0xFFFF	/* Count Field */

#define SET_DMA_CONTROL \
 		(SET_DMA_CIE_ENABLE(p_init->int_enable) | /* interrupt enable         */ \
 		SET_DMA_BEN(p_init->buffer_enable)     | /* buffer enable            */\
		SET_DMA_ETD(p_init->etd_output)        | /* end of transfer pin      */ \
	       	SET_DMA_TCE(p_init->tce_enable)        | /* terminal count enable    */ \
                SET_DMA_PL(p_init->pl)                 | /* peripheral location      */ \
                SET_DMA_DAI(p_init->dai)               | /* dest addr increment      */ \
                SET_DMA_SAI(p_init->sai)               | /* src addr increment       */ \
                SET_DMA_PRIORITY(p_init->cp)           |  /* channel priority        */ \
                SET_DMA_PW(p_init->pwidth)             |  /* peripheral/bus width    */ \
                SET_DMA_PSC(p_init->psc)               |  /* peripheral setup cycles */ \
                SET_DMA_PWC(p_init->pwc)               |  /* peripheral wait cycles  */ \
                SET_DMA_PHC(p_init->phc)               |  /* peripheral hold cycles  */ \
                SET_DMA_PREFETCH(p_init->pf)              /* read prefetch           */)

#define GET_DMA_POLARITY(chan) (DMAReq_ActiveLow(chan) | DMAAck_ActiveLow(chan) | EOT_ActiveLow(chan))

#elif defined(CONFIG_STB03xxx)		/* stb03xxx */

#define DMA_PPC4xx_SIZE	4096

/*
 * DMA Status Register
 */

#define SET_DMA_PRIORITY(x)   (((x)&0x00800001))	/* DMA Channel Priority */
#define DMA_PRIORITY_MASK	0x00800001
#define   PRIORITY_LOW         	0x00000000
#define   PRIORITY_MID_LOW     	0x00000001
#define   PRIORITY_MID_HIGH    	0x00800000
#define   PRIORITY_HIGH        	0x00800001
#define GET_DMA_PRIORITY(x) (((((x)&DMA_PRIORITY_MASK) &0x00800000) >> 22 ) | (((x)&DMA_PRIORITY_MASK) &0x00000001))

#define DMA_CS0           (1<<31)	/* Terminal Count has been reached */
#define DMA_CS1           (1<<30)
#define DMA_CS2           (1<<29)
#define DMA_CS3           (1<<28)

#define DMA_TS0           (1<<27)	/* End of Transfer has been requested */
#define DMA_TS1           (1<<26)
#define DMA_TS2           (1<<25)
#define DMA_TS3           (1<<24)

#define DMA_CH0_ERR       (1<<23)	/* DMA Chanel 0 Error */
#define DMA_CH1_ERR       (1<<22)
#define DMA_CH2_ERR       (1<<21)
#define DMA_CH3_ERR       (1<<20)

#define DMA_CT0		  (1<<19)	/* Chained transfere */

#define DMA_IN_DMA_REQ0   (1<<18)	/* Internal DMA Request is pending */
#define DMA_IN_DMA_REQ1   (1<<17)
#define DMA_IN_DMA_REQ2   (1<<16)
#define DMA_IN_DMA_REQ3   (1<<15)

#define DMA_EXT_DMA_REQ0  (1<<14)	/* External DMA Request is pending */
#define DMA_EXT_DMA_REQ1  (1<<13)
#define DMA_EXT_DMA_REQ2  (1<<12)
#define DMA_EXT_DMA_REQ3  (1<<11)

#define DMA_CH0_BUSY      (1<<10)	/* DMA Channel 0 Busy */
#define DMA_CH1_BUSY      (1<<9)
#define DMA_CH2_BUSY       (1<<8)
#define DMA_CH3_BUSY       (1<<7)

#define DMA_CT1            (1<<6)	/* Chained transfere */
#define DMA_CT2            (1<<5)
#define DMA_CT3            (1<<4)

#define DMA_CH_ENABLE (1<<7)
#define SET_DMA_CH(x) (((x)&0x1)<<7)
#define GET_DMA_CH(x) (((x)&DMA_CH_ENABLE)>>7)

/* STBx25xxx dma unique */
/* enable device port on a dma channel
 * example ext 0 on dma 1
 */

#define	SSP0_RECV	15
#define	SSP0_XMIT	14
#define EXT_DMA_0	12
#define	SC1_XMIT	11
#define SC1_RECV	10
#define EXT_DMA_2	9
#define	EXT_DMA_3	8
#define SERIAL2_XMIT	7
#define SERIAL2_RECV	6
#define SC0_XMIT 	5
#define	SC0_RECV	4
#define	SERIAL1_XMIT	3
#define SERIAL1_RECV	2
#define	SERIAL0_XMIT	1
#define SERIAL0_RECV	0

#define DMA_CHAN_0	1
#define DMA_CHAN_1	2
#define DMA_CHAN_2	3
#define DMA_CHAN_3	4

/* end STBx25xx */

/*
 * Bit 30 must be one for Redwoods, otherwise transfers may receive errors.
 */
#define DMA_CR_MB0 0x2

#define SET_DMA_CONTROL \
       		(SET_DMA_CIE_ENABLE(p_init->int_enable) |  /* interrupt enable         */ \
		SET_DMA_ETD(p_init->etd_output)        |  /* end of transfer pin      */ \
		SET_DMA_TCE(p_init->tce_enable)        |  /* terminal count enable    */ \
		SET_DMA_PL(p_init->pl)                 |  /* peripheral location      */ \
		SET_DMA_DAI(p_init->dai)               |  /* dest addr increment      */ \
		SET_DMA_SAI(p_init->sai)               |  /* src addr increment       */ \
		SET_DMA_PRIORITY(p_init->cp)           |  /* channel priority        */  \
		SET_DMA_PW(p_init->pwidth)             |  /* peripheral/bus width    */ \
		SET_DMA_PSC(p_init->psc)               |  /* peripheral setup cycles */ \
		SET_DMA_PWC(p_init->pwc)               |  /* peripheral wait cycles  */ \
		SET_DMA_PHC(p_init->phc)               |  /* peripheral hold cycles  */ \
		SET_DMA_TCD(p_init->tcd_disable)	  |  /* TC chain mode disable   */ \
		SET_DMA_ECE(p_init->ece_enable)	  |  /* ECE chanin mode enable  */ \
		SET_DMA_CH(p_init->ch_enable)	|    /* Chain enable 	        */ \
		DMA_CR_MB0				/* must be one */)

#define GET_DMA_POLARITY(chan) chan

#endif

typedef struct {
	unsigned short in_use;	/* set when channel is being used, clr when
				 * available.
				 */
	/*
	 * Valid polarity settings:
	 *   DMAReq_ActiveLow(n)
	 *   DMAAck_ActiveLow(n)
	 *   EOT_ActiveLow(n)
	 *
	 *   n is 0 to max dma chans
	 */
	unsigned int polarity;

	char buffer_enable;	/* Boolean: buffer enable            */
	char tce_enable;	/* Boolean: terminal count enable    */
	char etd_output;	/* Boolean: eot pin is a tc output   */
	char pce;		/* Boolean: parity check enable      */

	/*
	 * Peripheral location:
	 * INTERNAL_PERIPHERAL (UART0 on the 405GP)
	 * EXTERNAL_PERIPHERAL
	 */
	char pl;		/* internal/external peripheral      */

	/*
	 * Valid pwidth settings:
	 *   PW_8
	 *   PW_16
	 *   PW_32
	 *   PW_64
	 */
	unsigned int pwidth;

	char dai;		/* Boolean: dst address increment   */
	char sai;		/* Boolean: src address increment   */

	/*
	 * Valid psc settings: 0-3
	 */
	unsigned int psc;	/* Peripheral Setup Cycles         */

	/*
	 * Valid pwc settings:
	 * 0-63
	 */
	unsigned int pwc;	/* Peripheral Wait Cycles          */

	/*
	 * Valid phc settings:
	 * 0-7
	 */
	unsigned int phc;	/* Peripheral Hold Cycles          */

	/*
	 * Valid cp (channel priority) settings:
	 *   PRIORITY_LOW
	 *   PRIORITY_MID_LOW
	 *   PRIORITY_MID_HIGH
	 *   PRIORITY_HIGH
	 */
	unsigned int cp;	/* channel priority                */

	/*
	 * Valid pf (memory read prefetch) settings:
	 *
	 *   PREFETCH_1
	 *   PREFETCH_2
	 *   PREFETCH_4
	 */
	unsigned int pf;	/* memory read prefetch            */

	/*
	 * Boolean: channel interrupt enable
	 * NOTE: for sgl transfers, only the last descriptor will be setup to
	 * interrupt.
	 */
	char int_enable;

	char shift;		/* easy access to byte_count shift, based on */
	/* the width of the channel                  */

	uint32_t control;	/* channel control word                      */

	/* These variabled are used ONLY in single dma transfers              */
	unsigned int mode;	/* transfer mode                     */
	phys_addr_t addr;
	char ce;		/* channel enable */
#ifdef CONFIG_STB03xxx
	char ch_enable;
	char tcd_disable;
	char ece_enable;
	char td;		/* transfer direction */
#endif

	char int_on_final_sg;/* for scatter/gather - only interrupt on last sg */
} ppc_dma_ch_t;

/*
 * PPC44x DMA implementations have a slightly different
 * descriptor layout.  Probably moved about due to the
 * change to 64-bit addresses and link pointer. I don't
 * know why they didn't just leave control_count after
 * the dst_addr.
 */
#ifdef PPC4xx_DMA_64BIT
typedef struct {
	uint32_t control;
	uint32_t control_count;
	phys_addr_t src_addr;
	phys_addr_t dst_addr;
	phys_addr_t next;
} ppc_sgl_t;
#else
typedef struct {
	uint32_t control;
	uint32_t src_addr;
	uint32_t dst_addr;
	uint32_t control_count;
	uint32_t next;
} ppc_sgl_t;
#endif

typedef struct {
	unsigned int dmanr;
	uint32_t control;	/* channel ctrl word; loaded from each descrptr */
	uint32_t sgl_control;	/* LK, TCI, ETI, and ERI bits in sgl descriptor */
	dma_addr_t dma_addr;	/* dma (physical) address of this list          */
	ppc_sgl_t *phead;
	dma_addr_t phead_dma;
	ppc_sgl_t *ptail;
	dma_addr_t ptail_dma;
} sgl_list_info_t;

typedef struct {
	phys_addr_t *src_addr;
	phys_addr_t *dst_addr;
	phys_addr_t dma_src_addr;
	phys_addr_t dma_dst_addr;
} pci_alloc_desc_t;

extern ppc_dma_ch_t dma_channels[];

/*
 * The DMA API are in ppc4xx_dma.c and ppc4xx_sgdma.c
 */
extern int ppc4xx_init_dma_channel(unsigned int, ppc_dma_ch_t *);
extern int ppc4xx_get_channel_config(unsigned int, ppc_dma_ch_t *);
extern int ppc4xx_set_channel_priority(unsigned int, unsigned int);
extern unsigned int ppc4xx_get_peripheral_width(unsigned int);
extern void ppc4xx_set_sg_addr(int, phys_addr_t);
extern int ppc4xx_add_dma_sgl(sgl_handle_t, phys_addr_t, phys_addr_t, unsigned int);
extern void ppc4xx_enable_dma_sgl(sgl_handle_t);
extern void ppc4xx_disable_dma_sgl(sgl_handle_t);
extern void ppc4xx_disable_dma_sgl_nr(unsigned int dmanr);
extern int ppc4xx_get_dma_sgl_residue(sgl_handle_t, phys_addr_t *, phys_addr_t *);
extern int ppc4xx_delete_dma_sgl_element(sgl_handle_t, phys_addr_t *, phys_addr_t *);
extern int ppc4xx_alloc_dma_handle(sgl_handle_t *, unsigned int, unsigned int);
extern void ppc4xx_free_dma_handle(sgl_handle_t);
extern int ppc4xx_get_dma_status(void);
extern int ppc4xx_enable_burst(unsigned int);
extern int ppc4xx_disable_burst(unsigned int);
extern int ppc4xx_set_burst_size(unsigned int, unsigned int);
extern void ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr);
extern void ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr);
extern void ppc4xx_enable_dma(unsigned int dmanr);
extern void ppc4xx_disable_dma(unsigned int dmanr);
extern void ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count);
extern int ppc4xx_get_dma_residue(unsigned int dmanr);
extern void ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
				 phys_addr_t dst_dma_addr);
extern int ppc4xx_enable_dma_interrupt(unsigned int dmanr);
extern int ppc4xx_disable_dma_interrupt(unsigned int dmanr);
extern int ppc4xx_clr_dma_status(unsigned int dmanr);
extern int ppc4xx_map_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan);
extern int ppc4xx_disable_dma_port(unsigned int dmanr, unsigned int ocp_dma,short dma_chan);
extern int ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode);

/* These are in kernel/dma.c: */

/* reserve a DMA channel */
extern int request_dma(unsigned int dmanr, const char *device_id);
/* release it again */
extern void free_dma(unsigned int dmanr);
#endif
#endif				/* __KERNEL__ */

[-- Attachment #3: ppc4xx_dma.c --]
[-- Type: text/plain, Size: 18928 bytes --]

/*
 * IBM PPC4xx DMA engine core library
 *
 * Copyright 2000-2004 MontaVista Software Inc.
 *
 * Cleaned up and converted to new DCR access
 * Matt Porter <mporter@kernel.crashing.org>
 *
 * Original code by Armin Kuster <akuster@mvista.com>
 * and Pete Popov <ppopov@mvista.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.
 *
 * 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.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
#include <linux/module.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/ppc4xx_dma.h>

ppc_dma_ch_t dma_channels[MAX_PPC4xx_DMA_CHANNELS];

int
ppc4xx_get_dma_status(void)
{
	return (mfdcr(DCRN_DMASR));
}

void
ppc4xx_set_src_addr(int dmanr, phys_addr_t src_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_src_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef PPC4xx_DMA_64BIT
	mtdcr(DCRN_DMASAH0 + dmanr*2, (u32)(src_addr >> 32));
#else
	mtdcr(DCRN_DMASA0 + dmanr*2, (u32)src_addr);
#endif
}

void
ppc4xx_set_dst_addr(int dmanr, phys_addr_t dst_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_dst_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef PPC4xx_DMA_64BIT
	mtdcr(DCRN_DMADAH0 + dmanr*2, (u32)(dst_addr >> 32));
#else
	mtdcr(DCRN_DMADA0 + dmanr*2, (u32)dst_addr);
#endif
}

void
ppc4xx_enable_dma(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
	unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
				       DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
				       DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
				       DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};

	if (p_dma_ch->in_use) {
		printk("enable_dma: channel %d in use\n", dmanr);
		return;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("enable_dma: bad channel: %d\n", dmanr);
		return;
	}

	if (p_dma_ch->mode == DMA_MODE_READ) {
		/* peripheral to memory */
		ppc4xx_set_src_addr(dmanr, 0);
		ppc4xx_set_dst_addr(dmanr, p_dma_ch->addr);
	} else if (p_dma_ch->mode == DMA_MODE_WRITE) {
		/* memory to peripheral */
		ppc4xx_set_src_addr(dmanr, p_dma_ch->addr);
		ppc4xx_set_dst_addr(dmanr, 0);
	}

	/* for other xfer modes, the addresses are already set */
	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	control &= ~(DMA_TM_MASK | DMA_TD);	/* clear all mode bits */
	if (p_dma_ch->mode == DMA_MODE_MM) {
		/* software initiated memory to memory */
		control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;
	}

	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	/*
	 * Clear the CS, TS, RI bits for the channel from DMASR.  This
	 * has been observed to happen correctly only after the mode and
	 * ETD/DCE bits in DMACRx are set above.  Must do this before
	 * enabling the channel.
	 */

	mtdcr(DCRN_DMASR, status_bits[dmanr]);

	/*
	 * For device-paced transfers, Terminal Count Enable apparently
	 * must be on, and this must be turned on after the mode, etc.
	 * bits are cleared above (at least on Redwood-6).
	 */

	if ((p_dma_ch->mode == DMA_MODE_MM_DEVATDST) ||
	    (p_dma_ch->mode == DMA_MODE_MM_DEVATSRC))
		control |= DMA_TCE_ENABLE;

	/*
	 * Now enable the channel.
	 */

	control |= (p_dma_ch->mode | DMA_CE_ENABLE);

	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	p_dma_ch->in_use = 1;
}

void
ppc4xx_disable_dma(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (!p_dma_ch->in_use) {
	  //		printk("disable_dma: channel %d not in use\n", dmanr);
		return;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("disable_dma: bad channel: %d\n", dmanr);
		return;
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control &= ~DMA_CE_ENABLE;
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	p_dma_ch->in_use = 0;
}

/*
 * Sets the dma mode for single DMA transfers only.
 * For scatter/gather transfers, the mode is passed to the
 * alloc_dma_handle() function as one of the parameters.
 *
 * The mode is simply saved and used later.  This allows
 * the driver to call set_dma_mode() and set_dma_addr() in
 * any order.
 *
 * Valid mode values are:
 *
 * DMA_MODE_READ          peripheral to memory
 * DMA_MODE_WRITE         memory to peripheral
 * DMA_MODE_MM            memory to memory
 * DMA_MODE_MM_DEVATSRC   device-paced memory to memory, device at src
 * DMA_MODE_MM_DEVATDST   device-paced memory to memory, device at dst
 */
int
ppc4xx_set_dma_mode(unsigned int dmanr, unsigned int mode)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("set_dma_mode: bad channel 0x%x\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->mode = mode;

	return DMA_STATUS_GOOD;
}

/*
 * Sets the DMA Count register. Note that 'count' is in bytes.
 * However, the DMA Count register counts the number of "transfers",
 * where each transfer is equal to the bus width.  Thus, count
 * MUST be a multiple of the bus width.
 */
void
ppc4xx_set_dma_count(unsigned int dmanr, unsigned int count)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

#ifdef DEBUG_4xxDMA
	{
		int error = 0;
		switch (p_dma_ch->pwidth) {
		case PW_8:
			break;
		case PW_16:
			if (count & 0x1)
				error = 1;
			break;
		case PW_32:
			if (count & 0x3)
				error = 1;
			break;
		case PW_64:
			if (count & 0x7)
				error = 1;
			break;
		default:
			printk("set_dma_count: invalid bus width: 0x%x\n",
			       p_dma_ch->pwidth);
			return;
		}
		if (error)
			printk
			    ("Warning: set_dma_count count 0x%x bus width %d\n",
			     count, p_dma_ch->pwidth);
	}
#endif

	count = count >> p_dma_ch->shift;

	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), count);
}

/*
 *   Returns the number of bytes left to be transfered.
 *   After a DMA transfer, this should return zero.
 *   Reading this while a DMA transfer is still in progress will return
 *   unpredictable results.
 */
int
ppc4xx_get_dma_residue(unsigned int dmanr)
{
	unsigned int count;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_dma_residue: bad channel 0x%x\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	count = mfdcr(DCRN_DMACT0 + (dmanr * 0x8));

	return (count << p_dma_ch->shift);
}

/*
 * Sets the DMA address for a memory to peripheral or peripheral
 * to memory transfer.  The address is just saved in the channel
 * structure for now and used later in enable_dma().
 */
void
ppc4xx_set_dma_addr(unsigned int dmanr, phys_addr_t addr)
{
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_dma_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef DEBUG_4xxDMA
	{
		int error = 0;
		switch (p_dma_ch->pwidth) {
		case PW_8:
			break;
		case PW_16:
			if ((unsigned) addr & 0x1)
				error = 1;
			break;
		case PW_32:
			if ((unsigned) addr & 0x3)
				error = 1;
			break;
		case PW_64:
			if ((unsigned) addr & 0x7)
				error = 1;
			break;
		default:
			printk("ppc4xx_set_dma_addr: invalid bus width: 0x%x\n",
			       p_dma_ch->pwidth);
			return;
		}
		if (error)
			printk("Warning: ppc4xx_set_dma_addr addr 0x%x bus width %d\n",
			       addr, p_dma_ch->pwidth);
	}
#endif

	/* save dma address and program it later after we know the xfer mode */
	p_dma_ch->addr = addr;
}

/*
 * Sets both DMA addresses for a memory to memory transfer.
 * For memory to peripheral or peripheral to memory transfers
 * the function set_dma_addr() should be used instead.
 */
void
ppc4xx_set_dma_addr2(unsigned int dmanr, phys_addr_t src_dma_addr,
		     phys_addr_t dst_dma_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_dma_addr2: bad channel: %d\n", dmanr);
		return;
	}

#ifdef DEBUG_4xxDMA
	{
		ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
		int error = 0;
		switch (p_dma_ch->pwidth) {
			case PW_8:
				break;
			case PW_16:
				if (((unsigned) src_dma_addr & 0x1) ||
						((unsigned) dst_dma_addr & 0x1)
				   )
					error = 1;
				break;
			case PW_32:
				if (((unsigned) src_dma_addr & 0x3) ||
						((unsigned) dst_dma_addr & 0x3)
				   )
					error = 1;
				break;
			case PW_64:
				if (((unsigned) src_dma_addr & 0x7) ||
						((unsigned) dst_dma_addr & 0x7)
				   )
					error = 1;
				break;
			default:
				printk("ppc4xx_set_dma_addr2: invalid bus width: 0x%x\n",
						p_dma_ch->pwidth);
				return;
		}
		if (error)
			printk
				("Warning: ppc4xx_set_dma_addr2 src 0x%x dst 0x%x bus width %d\n",
				 src_dma_addr, dst_dma_addr, p_dma_ch->pwidth);
	}
#endif

	ppc4xx_set_src_addr(dmanr, src_dma_addr);
	ppc4xx_set_dst_addr(dmanr, dst_dma_addr);
}

/*
 * Enables the channel interrupt.
 *
 * If performing a scatter/gatter transfer, this function
 * MUST be called before calling alloc_dma_handle() and building
 * the sgl list.  Otherwise, interrupts will not be enabled, if
 * they were previously disabled.
 */
int
ppc4xx_enable_dma_interrupt(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_enable_dma_interrupt: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->int_enable = 1;

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control |= DMA_CIE_ENABLE;	/* Channel Interrupt Enable */
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Disables the channel interrupt.
 *
 * If performing a scatter/gatter transfer, this function
 * MUST be called before calling alloc_dma_handle() and building
 * the sgl list.  Otherwise, interrupts will not be disabled, if
 * they were previously enabled.
 */
int
ppc4xx_disable_dma_interrupt(unsigned int dmanr)
{
	unsigned int control;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_disable_dma_interrupt: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch->int_enable = 0;

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control &= ~DMA_CIE_ENABLE;	/* Channel Interrupt Enable */
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Configures a DMA channel, including the peripheral bus width, if a
 * peripheral is attached to the channel, the polarity of the DMAReq and
 * DMAAck signals, etc.  This information should really be setup by the boot
 * code, since most likely the configuration won't change dynamically.
 * If the kernel has to call this function, it's recommended that it's
 * called from platform specific init code.  The driver should not need to
 * call this function.
 */
int
ppc4xx_init_dma_channel(unsigned int dmanr, ppc_dma_ch_t * p_init)
{
	unsigned int status_bits[] = { DMA_CS0 | DMA_TS0 | DMA_CH0_ERR,
				       DMA_CS1 | DMA_TS1 | DMA_CH1_ERR,
				       DMA_CS2 | DMA_TS2 | DMA_CH2_ERR,
				       DMA_CS3 | DMA_TS3 | DMA_CH3_ERR};

	unsigned int polarity;
	uint32_t control = 0;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];

	DMA_MODE_READ = (unsigned long) DMA_TD;	/* Peripheral to Memory */
	DMA_MODE_WRITE = 0;	/* Memory to Peripheral */

	if (!p_init) {
		printk("ppc4xx_init_dma_channel: NULL p_init\n");
		return DMA_STATUS_NULL_POINTER;
	}

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_init_dma_channel: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

#if DCRN_POL > 0
	polarity = mfdcr(DCRN_POL);
#else
	polarity = 0;
#endif

	/* Setup the control register based on the values passed to
	 * us in p_init.  Then, over-write the control register with this
	 * new value.
	 */
	control |= SET_DMA_CONTROL;

	/* clear all polarity signals and then "or" in new signal levels */
	polarity &= ~GET_DMA_POLARITY(dmanr);
	polarity |= p_init->polarity;
#if DCRN_POL > 0
	mtdcr(DCRN_POL, polarity);
#endif
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	/* save these values in our dma channel structure */
	memcpy(p_dma_ch, p_init, sizeof (ppc_dma_ch_t));

	/*
	 * The peripheral width values written in the control register are:
	 *   PW_8                 0
	 *   PW_16                1
	 *   PW_32                2
	 *   PW_64                3
	 *
	 *   Since the DMA count register takes the number of "transfers",
	 *   we need to divide the count sent to us in certain
	 *   functions by the appropriate number.  It so happens that our
	 *   right shift value is equal to the peripheral width value.
	 */
	p_dma_ch->shift = p_init->pwidth;

	/*
	 * Save the control word for easy access.
	 */
	p_dma_ch->control = control;

	/*
	 * clear status register for the channel
	 * only TS, CS and RI needs to be cleared.
	 */
	mtdcr(DCRN_DMASR, status_bits[dmanr]);
	
	return DMA_STATUS_GOOD;
}

/*
 * This function returns the channel configuration.
 */
int
ppc4xx_get_channel_config(unsigned int dmanr, ppc_dma_ch_t * p_dma_ch)
{
	unsigned int polarity;
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_channel_config: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	memcpy(p_dma_ch, &dma_channels[dmanr], sizeof (ppc_dma_ch_t));

#if DCRN_POL > 0
	polarity = mfdcr(DCRN_POL);
#else
	polarity = 0;
#endif

	p_dma_ch->polarity = polarity & GET_DMA_POLARITY(dmanr);
	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	p_dma_ch->cp = GET_DMA_PRIORITY(control);
	p_dma_ch->pwidth = GET_DMA_PW(control);
	p_dma_ch->psc = GET_DMA_PSC(control);
	p_dma_ch->pwc = GET_DMA_PWC(control);
	p_dma_ch->phc = GET_DMA_PHC(control);
	p_dma_ch->ce = GET_DMA_CE_ENABLE(control);
	p_dma_ch->int_enable = GET_DMA_CIE_ENABLE(control);
	p_dma_ch->shift = GET_DMA_PW(control);

#ifdef CONFIG_PPC4xx_EDMA
	p_dma_ch->pf = GET_DMA_PREFETCH(control);
#else
	p_dma_ch->ch_enable = GET_DMA_CH(control);
	p_dma_ch->ece_enable = GET_DMA_ECE(control);
	p_dma_ch->tcd_disable = GET_DMA_TCD(control);
#endif
	return DMA_STATUS_GOOD;
}

/*
 * Sets the priority for the DMA channel dmanr.
 * Since this is setup by the hardware init function, this function
 * can be used to dynamically change the priority of a channel.
 *
 * Acceptable priorities:
 *
 * PRIORITY_LOW
 * PRIORITY_MID_LOW
 * PRIORITY_MID_HIGH
 * PRIORITY_HIGH
 *
 */
int
ppc4xx_set_channel_priority(unsigned int dmanr, unsigned int priority)
{
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_channel_priority: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	if ((priority != PRIORITY_LOW) &&
	    (priority != PRIORITY_MID_LOW) &&
	    (priority != PRIORITY_MID_HIGH) && (priority != PRIORITY_HIGH)) {
		printk("ppc4xx_set_channel_priority: bad priority: 0x%x\n", priority);
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));
	control |= SET_DMA_PRIORITY(priority);
	mtdcr(DCRN_DMACR0 + (dmanr * 0x8), control);

	return DMA_STATUS_GOOD;
}

/*
 * Returns the width of the peripheral attached to this channel. This assumes
 * that someone who knows the hardware configuration, boot code or some other
 * init code, already set the width.
 *
 * The return value is one of:
 *   PW_8
 *   PW_16
 *   PW_32
 *   PW_64
 *
 *   The function returns 0 on error.
 */
unsigned int
ppc4xx_get_peripheral_width(unsigned int dmanr)
{
	unsigned int control;

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_get_peripheral_width: bad channel %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	control = mfdcr(DCRN_DMACR0 + (dmanr * 0x8));

	return (GET_DMA_PW(control));
}

/*
 * Clears the channel status bits
 */
int
ppc4xx_clr_dma_status(unsigned int dmanr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_clr_dma_status: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	mtdcr(DCRN_DMASR, ((u32)DMA_CH0_ERR | (u32)DMA_CS0 | (u32)DMA_TS0) >> dmanr);
	return DMA_STATUS_GOOD;
}

#ifdef CONFIG_PPC4xx_EDMA
/*
 * Enables the burst on the channel (BTEN bit in the control/count register)
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_enable_burst(unsigned int dmanr)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_enable_burst: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
        ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) | DMA_CTC_BTEN;
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}
/*
 * Disables the burst on the channel (BTEN bit in the control/count register)
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_disable_burst(unsigned int dmanr)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_disable_burst: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BTEN;
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}
/*
 * Sets the burst size (number of peripheral widths) for the channel
 * (BSIZ bits in the control/count register))
 * must be one of:
 *    DMA_CTC_BSIZ_2
 *    DMA_CTC_BSIZ_4
 *    DMA_CTC_BSIZ_8
 *    DMA_CTC_BSIZ_16
 * Note:
 * For scatter/gather dma, this function MUST be called before the
 * ppc4xx_alloc_dma_handle() func as the chan count register is copied into the
 * sgl list and used as each sgl element is added.
 */
int
ppc4xx_set_burst_size(unsigned int dmanr, unsigned int bsize)
{
	unsigned int ctc;
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk(KERN_ERR "ppc4xx_set_burst_size: bad channel: %d\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}
	ctc = mfdcr(DCRN_DMACT0 + (dmanr * 0x8)) &~ DMA_CTC_BSIZ_MSK;
	ctc |= (bsize & DMA_CTC_BSIZ_MSK);
	mtdcr(DCRN_DMACT0 + (dmanr * 0x8), ctc);
	return DMA_STATUS_GOOD;
}

EXPORT_SYMBOL(ppc4xx_enable_burst);
EXPORT_SYMBOL(ppc4xx_disable_burst);
EXPORT_SYMBOL(ppc4xx_set_burst_size);
#endif /* CONFIG_PPC4xx_EDMA */

EXPORT_SYMBOL(ppc4xx_init_dma_channel);
EXPORT_SYMBOL(ppc4xx_get_channel_config);
EXPORT_SYMBOL(ppc4xx_set_channel_priority);
EXPORT_SYMBOL(ppc4xx_get_peripheral_width);
EXPORT_SYMBOL(dma_channels);
EXPORT_SYMBOL(ppc4xx_set_src_addr);
EXPORT_SYMBOL(ppc4xx_set_dst_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr);
EXPORT_SYMBOL(ppc4xx_set_dma_addr2);
EXPORT_SYMBOL(ppc4xx_enable_dma);
EXPORT_SYMBOL(ppc4xx_disable_dma);
EXPORT_SYMBOL(ppc4xx_set_dma_mode);
EXPORT_SYMBOL(ppc4xx_set_dma_count);
EXPORT_SYMBOL(ppc4xx_get_dma_residue);
EXPORT_SYMBOL(ppc4xx_enable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_disable_dma_interrupt);
EXPORT_SYMBOL(ppc4xx_get_dma_status);
EXPORT_SYMBOL(ppc4xx_clr_dma_status);


[-- Attachment #4: ppc4xx_sgdma.c --]
[-- Type: text/plain, Size: 17134 bytes --]

/*
 * IBM PPC4xx DMA engine scatter/gather library
 *
 * Copyright 2002-2003 MontaVista Software Inc.
 *
 * Cleaned up and converted to new DCR access
 * Matt Porter <mporter@kernel.crashing.org>
 *
 * Original code by Armin Kuster <akuster@mvista.com>
 * and Pete Popov <ppopov@mvista.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.
 *
 * 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.,
 * 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/ppc4xx_dma.h>
#ifdef CONFIG_VDR
#include <asm/cacheflush.h>
#endif

void
ppc4xx_set_sg_addr(int dmanr, phys_addr_t sg_addr)
{
	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_set_sg_addr: bad channel: %d\n", dmanr);
		return;
	}

#ifdef PPC4xx_DMA_64BIT
	mtdcr(DCRN_ASGH0 + (dmanr * 0x8), (u32)(sg_addr >> 32));
#endif
	mtdcr(DCRN_ASG0 + (dmanr * 0x8), (u32)sg_addr);
}

/*
 *   Add a new sgl descriptor to the end of a scatter/gather list
 *   which was created by alloc_dma_handle().
 *
 *   For a memory to memory transfer, both dma addresses must be
 *   valid. For a peripheral to memory transfer, one of the addresses
 *   must be set to NULL, depending on the direction of the transfer:
 *   memory to peripheral: set dst_addr to NULL,
 *   peripheral to memory: set src_addr to NULL.
 */
int
ppc4xx_add_dma_sgl(sgl_handle_t handle, phys_addr_t src_addr, phys_addr_t dst_addr,
		   unsigned int count)
{
	sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
	ppc_dma_ch_t *p_dma_ch;

	if (!handle) {
		printk("ppc4xx_add_dma_sgl: null handle\n");
		return DMA_STATUS_BAD_HANDLE;
	}

	if (psgl->dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_add_dma_sgl: bad channel: %d\n", psgl->dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	p_dma_ch = &dma_channels[psgl->dmanr];

#ifdef DEBUG_4xxDMA
	{
		int error = 0;
		unsigned int aligned =
		    (unsigned) src_addr | (unsigned) dst_addr | count;
		switch (p_dma_ch->pwidth) {
		case PW_8:
			break;
		case PW_16:
			if (aligned & 0x1)
				error = 1;
			break;
		case PW_32:
			if (aligned & 0x3)
				error = 1;
			break;
		case PW_64:
			if (aligned & 0x7)
				error = 1;
			break;
		default:
			printk("ppc4xx_add_dma_sgl: invalid bus width: 0x%x\n",
			       p_dma_ch->pwidth);
			return DMA_STATUS_GENERAL_ERROR;
		}
		if (error)
			printk
			    ("Alignment warning: ppc4xx_add_dma_sgl src 0x%x dst 0x%x count 0x%x bus width var %d\n",
			     src_addr, dst_addr, count, p_dma_ch->pwidth);

	}
#endif

#ifdef CONFIG_VDR
	/* dynamic alloc each list element */
	{
		ppc_sgl_t *sgl_el = kmalloc(sizeof(ppc_sgl_t), GFP_KERNEL|GFP_DMA);
		if (!sgl_el)
			return DMA_STATUS_OUT_OF_MEMORY;

		if (!psgl->phead) { /* list was empty */
			psgl->phead = sgl_el;
		} else { /* not empty, tail exists */
			psgl->ptail->next = (uint32_t)virt_to_phys(sgl_el);
			dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
		}
		psgl->ptail = sgl_el;
	}

	psgl->ptail->control = psgl->control;
	psgl->ptail->src_addr = (uint32_t)src_addr;
	psgl->ptail->dst_addr = (uint32_t)dst_addr;
	psgl->ptail->control_count = (count >> p_dma_ch->shift) |
	    psgl->sgl_control;
	psgl->ptail->next = (uint32_t)virt_to_phys(NULL);
	dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t)); /* handled later, skip this one? */
#else
	if ((unsigned) (psgl->ptail + 1) >= ((unsigned) psgl + SGL_LIST_SIZE)) {
		printk("sgl handle out of memory \n");
		return DMA_STATUS_OUT_OF_MEMORY;
	}

	if (!psgl->ptail) {
		psgl->phead = (ppc_sgl_t *)
		    ((unsigned) psgl + sizeof (sgl_list_info_t));
		psgl->phead_dma = psgl->dma_addr + sizeof(sgl_list_info_t);
		psgl->ptail = psgl->phead;
		psgl->ptail_dma = psgl->phead_dma;
	} else {
		if(p_dma_ch->int_on_final_sg) {
			/* mask out all dma interrupts, except error, on tail
			before adding new tail. */
			psgl->ptail->control_count &=
				~(SG_TCI_ENABLE | SG_ETI_ENABLE);

			/* PATRIK: Added */
			/* Require Terminal Count interrupt on last */
			psgl->ptail->control_count |= SG_TCI_ENABLE; 
		}
		psgl->ptail->next = psgl->ptail_dma + sizeof(ppc_sgl_t);
		psgl->ptail++;
		psgl->ptail_dma += sizeof(ppc_sgl_t);
	}

	psgl->ptail->control = psgl->control;
	psgl->ptail->src_addr = src_addr;
	psgl->ptail->dst_addr = dst_addr;
	psgl->ptail->control_count = (count >> p_dma_ch->shift) |
	    psgl->sgl_control;
	psgl->ptail->next = (uint32_t) NULL;
#endif

	return DMA_STATUS_GOOD;
}

/*
 * Enable (start) the DMA described by the sgl handle.
 */
void
ppc4xx_enable_dma_sgl(sgl_handle_t handle)
{
	sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
	ppc_dma_ch_t *p_dma_ch;
	uint32_t sg_command;

	if (!handle) {
		printk("ppc4xx_enable_dma_sgl: null handle\n");
		return;
	} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
		printk("ppc4xx_enable_dma_sgl: bad channel in handle %d\n",
		       psgl->dmanr);
		return;
	} else if (!psgl->phead) {
		printk("ppc4xx_enable_dma_sgl: sg list empty\n");
		return;
	}

	p_dma_ch = &dma_channels[psgl->dmanr];
	psgl->ptail->control_count &= ~SG_LINK;	/* make this the last dscrptr */
	if (p_dma_ch->int_enable)
	{
		/* Require Terminal Count interrupt on last */
		psgl->ptail->control_count |= SG_TCI_ENABLE; 
	}

#ifdef CONFIG_VDR
	/* No more changes to tail object allowed */
	//dma_cache_wback((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
	dma_cache_wback_inv((unsigned long)psgl->ptail, sizeof(ppc_sgl_t));
	
	ppc4xx_set_sg_addr(psgl->dmanr, virt_to_phys(psgl->phead));
#else
	ppc4xx_set_sg_addr(psgl->dmanr, psgl->phead_dma);
#endif

	sg_command = SSG_ENABLE(psgl->dmanr) | SSG_MASK_ENABLE(psgl->dmanr);
	mtdcr(DCRN_ASGC, sg_command);	/* start transfer */
}

/*
 * Halt an active scatter/gather DMA operation (from handle).
 */
void
ppc4xx_disable_dma_sgl(sgl_handle_t handle)
{
	uint32_t sg_command;

	if (!handle) {
		printk("ppc4xx_disable_dma_sgl: null handle\n");
		return;
	} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
		printk("ppc4xx_disable_dma_sgl: bad channel in handle %d\n",
		       psgl->dmanr);
		return;
	}
	
	sg_command = SSG_MASK_ENABLE(psgl->dmanr);
	mtdcr(DCRN_ASGC, sg_command);	/* stop transfer */
}

/*
 * Halt an active scatter/gather DMA operation (from dmanr).
 */
void
ppc4xx_disable_dma_sgl_nr(unsigned int dmanr)
{
	uint32_t sg_command;

	if (dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
		printk("ppc4xx_disable_dma_sgl_nr: bad channel %d\n", dmanr);
		return;
	}

	sg_command = SSG_MASK_ENABLE(dmanr);
	mtdcr(DCRN_ASGC, sg_command);	/* stop transfer */
}

/*
 *  Returns number of bytes left to be transferred from the entire sgl list.
 *  *src_addr and *dst_addr get set to the source/destination address of
 *  the sgl descriptor where the DMA stopped.
 *
 *  An sgl transfer must NOT be active when this function is called.
 *  Note: Make sure ppc4xx_disable_dma_sgl is called before returning from
 *  interrupt handler (TSn, CSn will not disable the sgl)!
 */
int
ppc4xx_get_dma_sgl_residue(sgl_handle_t handle, phys_addr_t * src_addr,
			   phys_addr_t * dst_addr)
{
	sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
	ppc_dma_ch_t *p_dma_ch;
	ppc_sgl_t *pnext, *sgl_addr;
	uint32_t count_left;

	if (!handle) {
		printk("ppc4xx_get_dma_sgl_residue: null handle\n");
		return DMA_STATUS_BAD_HANDLE;
	} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
		printk("ppc4xx_get_dma_sgl_residue: bad channel in handle %d\n",
		       psgl->dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	sgl_addr = (ppc_sgl_t *) __va(mfdcr(DCRN_ASG0 + (psgl->dmanr * 0x8)));
	count_left = mfdcr(DCRN_DMACT0 + (psgl->dmanr * 0x8)) & SG_COUNT_MASK;


#ifdef CONFIG_VDR
	if (!sgl_addr) {
		/* Last in list */
		return count_left;
	}

	pnext = sgl_addr; /* sgl_addr is next to be loaded */

	/*
	 * Why this interface? Better return nothing or sgl_addr instead...?
	 */
	*src_addr = pnext->src_addr;
	*dst_addr = pnext->dst_addr;

	/*
	 * Now search the remaining descriptors and add their count.
	 * We already have the remaining count from this descriptor in
	 * count_left.
	 */
	while (pnext) {
		count_left += pnext->control_count & SG_COUNT_MASK;
		pnext = phys_to_virt(pnext->next);
	}

	/* success */
	p_dma_ch = &dma_channels[psgl->dmanr];
	return (count_left << p_dma_ch->shift);	/* count in bytes */
#else
	if (!sgl_addr) {
		printk("ppc4xx_get_dma_sgl_residue: sgl addr register is null\n");
		goto error;
	}
	pnext = psgl->phead;
	while (pnext &&
	       ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE) &&
		(pnext != sgl_addr))
	    ) {
		pnext++;
	}

	if (pnext == sgl_addr) {	/* found the sgl descriptor */

		*src_addr = pnext->src_addr;
		*dst_addr = pnext->dst_addr;

		/*
		 * Now search the remaining descriptors and add their count.
		 * We already have the remaining count from this descriptor in
		 * count_left.
		 */
		pnext++;

		while ((pnext != psgl->ptail) &&
		       ((unsigned) pnext < ((unsigned) psgl + SGL_LIST_SIZE))
		    ) {
			count_left += pnext->control_count & SG_COUNT_MASK;
		}

		if (pnext != psgl->ptail) {	/* should never happen */
			printk
			    ("ppc4xx_get_dma_sgl_residue error (1) psgl->ptail 0x%x handle 0x%x\n",
			     (unsigned int) psgl->ptail, (unsigned int) handle);
			goto error;
		}

		/* success */
		p_dma_ch = &dma_channels[psgl->dmanr];
		return (count_left << p_dma_ch->shift);	/* count in bytes */

	} else {
		/* this shouldn't happen */
		printk
		    ("get_dma_sgl_residue, unable to match current address 0x%x, handle 0x%x\n",
		     (unsigned int) sgl_addr, (unsigned int) handle);

	}

      error:
	*src_addr = (phys_addr_t) NULL;
	*dst_addr = (phys_addr_t) NULL;
	return 0;
#endif
}

/*
 * Returns the address(es) of the buffer(s) contained in the head element of
 * the scatter/gather list.  The element is removed from the scatter/gather
 * list and the next element becomes the head.
 *
 * This function should only be called when the DMA is not active.
 */
int
ppc4xx_delete_dma_sgl_element(sgl_handle_t handle, phys_addr_t * src_dma_addr,
			      phys_addr_t * dst_dma_addr)
{
	sgl_list_info_t *psgl = (sgl_list_info_t *) handle;

	if (!handle) {
		printk("ppc4xx_delete_sgl_element: null handle\n");
		return DMA_STATUS_BAD_HANDLE;
	} else if (psgl->dmanr > (MAX_PPC4xx_DMA_CHANNELS - 1)) {
		printk("ppc4xx_delete_sgl_element: bad channel in handle %d\n",
		       psgl->dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	if (!psgl->phead) {
		/* printk("ppc4xx_delete_sgl_element: sgl list empty\n"); - not an error */
		*src_dma_addr = (phys_addr_t) NULL;
		*dst_dma_addr = (phys_addr_t) NULL;
		return DMA_STATUS_SGL_LIST_EMPTY;
	}

	*src_dma_addr = (phys_addr_t) psgl->phead->src_addr;
	*dst_dma_addr = (phys_addr_t) psgl->phead->dst_addr;

	if (psgl->phead == psgl->ptail) {
		/* last descriptor on the list */
#ifdef CONFIG_VDR
		kfree(psgl->phead);
#endif		
		psgl->phead = NULL;
		psgl->ptail = NULL;
	} else {
#ifdef CONFIG_VDR
		ppc_sgl_t *next = phys_to_virt(psgl->phead->next);
		kfree(psgl->phead);
		psgl->phead = next;
#else
		psgl->phead++;
		psgl->phead_dma += sizeof(ppc_sgl_t);
#endif
	}

	return DMA_STATUS_GOOD;
}


/*
 *   Create a scatter/gather list handle.  This is simply a structure which
 *   describes a scatter/gather list.
 *
 *   A handle is returned in "handle" which the driver should save in order to
 *   be able to access this list later.  A chunk of memory will be allocated
 *   to be used by the API for internal management purposes, including managing
 *   the sg list and allocating memory for the sgl descriptors.  One page should
 *   be more than enough for that purpose.  Perhaps it's a bit wasteful to use
 *   a whole page for a single sg list, but most likely there will be only one
 *   sg list per channel.
 *
 *   Interrupt notes:
 *   Each sgl descriptor has a copy of the DMA control word which the DMA engine
 *   loads in the control register.  The control word has a "global" interrupt
 *   enable bit for that channel. Interrupts are further qualified by a few bits
 *   in the sgl descriptor count register.  In order to setup an sgl, we have to
 *   know ahead of time whether or not interrupts will be enabled at the completion
 *   of the transfers.  Thus, enable_dma_interrupt()/disable_dma_interrupt() MUST
 *   be called before calling alloc_dma_handle().  If the interrupt mode will never
 *   change after powerup, then enable_dma_interrupt()/disable_dma_interrupt()
 *   do not have to be called -- interrupts will be enabled or disabled based
 *   on how the channel was configured after powerup by the hw_init_dma_channel()
 *   function.  Each sgl descriptor will be setup to interrupt if an error occurs;
 *   however, only the last descriptor will be setup to interrupt. Thus, an
 *   interrupt will occur (if interrupts are enabled) only after the complete
 *   sgl transfer is done.
 *   End of Transfer Interrupt needs to be enabled in all descriptors, since it
 *   is impossible to know which one will be the last...
 */
int
ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr)
{
	sgl_list_info_t *psgl=NULL;
#ifdef CONFIG_VDR
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
#else
	dma_addr_t dma_addr;
	ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
	uint32_t ctc_settings;
	void *ret;
#endif

	if (dmanr >= MAX_PPC4xx_DMA_CHANNELS) {
		printk("ppc4xx_alloc_dma_handle: invalid channel 0x%x\n", dmanr);
		return DMA_STATUS_BAD_CHANNEL;
	}

	if (!phandle) {
		printk("ppc4xx_alloc_dma_handle: null handle\n");
		return DMA_STATUS_BAD_HANDLE;
	}

#ifdef CONFIG_VDR
	/* Get memory for the listinfo struct */
	psgl = kmalloc(sizeof(sgl_list_info_t), GFP_KERNEL);
	if (psgl == NULL) {
		*phandle = (sgl_handle_t) NULL;
		return DMA_STATUS_OUT_OF_MEMORY;
	}
	memset(psgl, 0, sizeof(sgl_list_info_t));
	
	/* dma_addr is unused now */
	psgl->dmanr = dmanr;
#else
	/* Get a page of memory, which is zeroed out by consistent_alloc() */
	ret = dma_alloc_coherent(NULL, DMA_PPC4xx_SIZE, &dma_addr, GFP_KERNEL);
	if (ret != NULL) {
		memset(ret, 0, DMA_PPC4xx_SIZE);
		psgl = (sgl_list_info_t *) ret;
	}

	if (psgl == NULL) {
		*phandle = (sgl_handle_t) NULL;
		return DMA_STATUS_OUT_OF_MEMORY;
	}

	psgl->dma_addr = dma_addr;
	psgl->dmanr = dmanr;
#endif
	/*
	 * Modify and save the control word. These words will be
	 * written to each sgl descriptor.  The DMA engine then
	 * loads this control word into the control register
	 * every time it reads a new descriptor.
	 */
	psgl->control = p_dma_ch->control;
	/* Clear all mode bits */
	psgl->control &= ~(DMA_TM_MASK | DMA_TD);
	/* Save control word and mode */
	psgl->control |= (mode | DMA_CE_ENABLE);
	 /* PPC Errata? DMA else ignore count on first in list */
	psgl->control |= SET_DMA_TCE(1);
	
	/* In MM mode, we must set ETD/TCE */
	if (mode == DMA_MODE_MM)
		psgl->control |= DMA_ETD_OUTPUT | DMA_TCE_ENABLE;

	if (p_dma_ch->int_enable) {
		/* Enable channel interrupt */
		psgl->control |= DMA_CIE_ENABLE;
	} else {
		psgl->control &= ~DMA_CIE_ENABLE;
	}

	/* Enable SGL control access */
	psgl->sgl_control = SG_ERI_ENABLE | SG_LINK;

#ifndef CONFIG_VDR
	/* keep control count register settings */
	ctc_settings = mfdcr(DCRN_DMACT0 + (dmanr * 0x8))
		& (DMA_CTC_BSIZ_MSK | DMA_CTC_BTEN); /*burst mode settings*/
	psgl->sgl_control |= ctc_settings;
#endif

	if (p_dma_ch->int_enable) {
		if (p_dma_ch->tce_enable)
			psgl->sgl_control |= SG_TCI_ENABLE;
		else
			psgl->sgl_control |= SG_ETI_ENABLE;
	}

	*phandle = (sgl_handle_t) psgl;
	return DMA_STATUS_GOOD;
}

/*
 * Destroy a scatter/gather list handle that was created by alloc_dma_handle().
 * The list must be empty (contain no elements).
 */
void
ppc4xx_free_dma_handle(sgl_handle_t handle)
{
	sgl_list_info_t *psgl = (sgl_list_info_t *) handle;

#ifdef CONFIG_VDR
	if (!handle) {
		printk("ppc4xx_free_dma_handle: got NULL\n");
		return;
	} else if (psgl->phead) { /* free list here, why do it externaly? */
		phys_addr_t dummy;
		while (ppc4xx_delete_dma_sgl_element(handle, &dummy, &dummy) == DMA_STATUS_GOOD)
			/* NOOP */;
		/* printk("ppc4xx_free_dma_handle: list not empty\n"); */
	}

	kfree((void *) psgl);
#else
	if (!handle) {
		printk("ppc4xx_free_dma_handle: got NULL\n");
		return;
	} else if (psgl->phead) {
		printk("ppc4xx_free_dma_handle: list not empty\n");
		return;
	} else if (!psgl->dma_addr) {	/* should never happen */
		printk("ppc4xx_free_dma_handle: no dma address\n");
		return;
	}

	dma_free_coherent(NULL, DMA_PPC4xx_SIZE, (void *) psgl, 0);
#endif
}

EXPORT_SYMBOL(ppc4xx_alloc_dma_handle);
EXPORT_SYMBOL(ppc4xx_free_dma_handle);
EXPORT_SYMBOL(ppc4xx_add_dma_sgl);
EXPORT_SYMBOL(ppc4xx_delete_dma_sgl_element);
EXPORT_SYMBOL(ppc4xx_enable_dma_sgl);
EXPORT_SYMBOL(ppc4xx_disable_dma_sgl);
EXPORT_SYMBOL(ppc4xx_disable_dma_sgl_nr);
EXPORT_SYMBOL(ppc4xx_get_dma_sgl_residue);

^ permalink raw reply

* Re: [PATCH 0/5] Version 17, series to add device tree naming to i2c
From: Jon Smirl @ 2007-12-20 23:59 UTC (permalink / raw)
  To: i2c, linuxppc-dev, linux-kernel
In-Reply-To: <20071220044136.20091.70984.stgit@terra.home>

Are there any other objections to this patch? If not, can it be
targeted for 2.6.25?

On 12/19/07, Jon Smirl <jonsmirl@gmail.com> wrote:
> Since copying i2c-mpc.c to maintain support for the ppc architecture seems to be an issue; instead rework i2c-mpc.c to use CONFIG_PPC_MERGE #ifdefs to support both the ppc and powerpc architecture. When ppc is deleted in six months these #ifdefs will need to be removed.
>
> Another rework of the i2c for powerpc device tree patch. This version implements standard alias naming only on the powerpc platform and only for the device tree names. The old naming mechanism of i2c_client.name,driver_name is left in place and not changed for non-powerpc platforms. This patch is fully capable of dynamically loading the i2c modules. You can modprobe in the i2c-mpc driver and the i2c modules described in the device tree will be automatically loaded. Modules also work if compiled in.
>
> The follow on patch to module-init-tools is also needed since the i2c subsystem has never implemented dynamic loading.
>
> The following series implements standard linux module aliasing for i2c modules on arch=powerpc. It then converts the mpc i2c driver from being a platform driver to an open firmware one. I2C device names are picked up from the device tree. Module aliasing is used to translate from device tree names into to linux kernel names. Several i2c drivers are updated to use the new aliasing.
>
> --
> Jon Smirl
> jonsmirl@gmail.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>


-- 
Jon Smirl
jonsmirl@gmail.com

^ permalink raw reply

* Re: [PATCH 3/4] sbc8560: Add device tree source for Wind River SBC8560 board
From: David Gibson @ 2007-12-20 23:57 UTC (permalink / raw)
  To: Paul Gortmaker; +Cc: linuxppc-dev
In-Reply-To: <6be237dcf5b58d604afa6cea3079ec7de02a8de9.1198107769.git.paul.gortmaker@windriver.com>

On Thu, Dec 20, 2007 at 09:54:31AM -0500, Paul Gortmaker wrote:
> This adds the device tree source for the Wind River SBC8560 board.  The
> biggest difference between this and the MPC8560ADS reference platform
> is the use of an external 16550 compatible UART instead of the CPM2.
> 
> Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com>
> ---
>  arch/powerpc/boot/dts/sbc8560.dts |  202 +++++++++++++++++++++++++++++++++++++
>  1 files changed, 202 insertions(+), 0 deletions(-)
> 
> diff --git a/arch/powerpc/boot/dts/sbc8560.dts b/arch/powerpc/boot/dts/sbc8560.dts
> new file mode 100644
> index 0000000..85fc488
> --- /dev/null
> +++ b/arch/powerpc/boot/dts/sbc8560.dts
> @@ -0,0 +1,202 @@
> +/*
> + * SBC8560 Device Tree Source
> + *
> + * Copyright 2007 Wind River Systems Inc.
> + *
> + * Paul Gortmaker (see MAINTAINERS for contact information)
> + *
> + * 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.
> + */
> +
> +
> +/ {
> +	model = "SBC8560";
> +	compatible = "SBC8560";
> +	#address-cells = <1>;
> +	#size-cells = <1>;
> +
> +	cpus {
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		PowerPC,8560@0 {
> +			device_type = "cpu";
> +			reg = <0>;
> +			d-cache-line-size = <20>;	// 32 bytes
> +			i-cache-line-size = <20>;	// 32 bytes
> +			d-cache-size = <8000>;		// L1, 32K
> +			i-cache-size = <8000>;		// L1, 32K
> +			timebase-frequency = <0>;	// From uboot
> +			bus-frequency = <0>;
> +			clock-frequency = <0>;
> +			32-bit;

Drop the "32-bit".  It was created in analogy with the "64-bit"
property, but nothing ever actually specified it.

> +		};
> +	};
> +
> +	memory {
> +		device_type = "memory";
> +		reg = <00000000 20000000>;
> +	};
> +
> +	soc8560@ff700000 {

I believe we're trying to call these "soc@address" now rather than
"socXXXX@address".

> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		#interrupt-cells = <2>;
> +		device_type = "soc";
> +		ranges = <0 ff700000 00100000>;
> +		reg = <ff700000 00100000>;
> +		bus-frequency = <0>;

This should be "clock-frequency" not "bus-frequency" although I don't
know if you can fix this within your code, or if it's a pre-existing
brokenness.

[snip]
> +		i2c@3000 {
> +			device_type = "i2c";

Drop this device_type.

> +			compatible = "fsl-i2c";
> +			reg = <3000 100>;
> +			interrupts = <2b 2>;
> +			interrupt-parent = <&mpic>;
> +			dfsrr;
> +		};
> +
> +		mdio@24520 {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			device_type = "mdio";
> +			compatible = "gianfar";

And this one, and change the compatible.  The gianfar driver has
recently been updated to change this.

[snip]
> +		ethernet@24000 {
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			device_type = "network";
> +			model = "TSEC";
> +			compatible = "gianfar";

Likewise here.

> +			reg = <24000 1000>;
> +			local-mac-address = [ 00 00 00 00 00 00 ];
> +			interrupts = <1d 2 1e 2 22 2>;
> +			interrupt-parent = <&mpic>;
> +			phy-handle = <&phy0>;
> +		};

[snip]
> +		mpic: pic@40000 {
> +			clock-frequency = <0>;

The mpic has a clock-frequency??

> +			interrupt-controller;
> +			#address-cells = <0>;

Should have #size-cells = <0> too.

> +			#interrupt-cells = <2>;
> +			reg = <40000 40000>;
> +			built-in;
> +			compatible = "chrp,open-pic";
> +			device_type = "open-pic";
> +			big-endian;
> +		};
> +
> +		global-utilities@e0000 {
> +			compatible = "fsl,mpc8560-guts";
> +			reg = <e0000 1000>;
> +			fsl,has-rstcr;
> +		};
> +	};
> +
> +	duart@fc700000 {
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +		#interrupt-cells = <2>;

This is neither an interrupt-controller nor an interrupt-nexus, so it
shouldn't have #interrupt-cells.

> +		device_type = "soc";		// console checks for this!

!?  If console checks this (whatever that means), then console is
doing crap...

This is clearly *not* a SoC, and should have a proper compatible, not
this crap device type.  Come to that, is this really an independent
device or does it belong within the soc or on the localbus or
something?

> +		ranges = <0 fc700000 00200000>;
> +		reg = <fc700000 00200000>;

Ranges and reg should not overlap like this, except in very unusal
cases.  I'm really not sure what this duart node is support to
represent, in any case.

> +		serial@000000 {

No leading zeroes on the unit address part of the name

> +			device_type = "serial";
> +			compatible = "ns16550";
> +			reg = <000000 100>;
> +			clock-frequency = <1C2000>;
> +			interrupts = <9 2>;
> +			interrupt-parent = <&mpic>;
> +		};
> +
> +		serial@100000 {
> +			device_type = "serial";
> +			compatible = "ns16550";
> +			reg = <100000 100>;
> +			clock-frequency = <1C2000>;
> +			interrupts = <a 2>;
> +			interrupt-parent = <&mpic>;
> +		};
> +	};
> +
> +	rtc@fc900000 {

Again, it looks very much like the duart and rtc belong off some
external bus controller, not directly on the CPU bus (which is what
the top-level of the device tree represents).

> +		#address-cells = <1>;
> +		#size-cells = <1>;

This has no child nodes, so no need for #address-cells and #size-cells.

> +		device_type = "rtc";

Drop device_type, we're trying to avoid them.

> +		compatible = "m48t59";
> +		reg = <fc900000 2000>;
> +	};
> +};

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

^ 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