powerpc: Merge maple support code to arch/powerpc/platforms/maple

Signed-off-by: Paul Mackerras <paulus@samba.org>
diff --git a/arch/powerpc/platforms/Makefile b/arch/powerpc/platforms/Makefile
index 01723d4..172c0db 100644
--- a/arch/powerpc/platforms/Makefile
+++ b/arch/powerpc/platforms/Makefile
@@ -10,3 +10,4 @@
 obj-$(CONFIG_85xx)		+= 85xx/
 obj-$(CONFIG_PPC_PSERIES)	+= pseries/
 obj-$(CONFIG_PPC_ISERIES)	+= iseries/
+obj-$(CONFIG_PPC_MAPLE)		+= maple/
diff --git a/arch/powerpc/platforms/maple/Makefile b/arch/powerpc/platforms/maple/Makefile
new file mode 100644
index 0000000..1be1a99
--- /dev/null
+++ b/arch/powerpc/platforms/maple/Makefile
@@ -0,0 +1 @@
+obj-y	+= setup.o pci.o time.o
diff --git a/arch/powerpc/platforms/maple/maple.h b/arch/powerpc/platforms/maple/maple.h
new file mode 100644
index 0000000..0657c57
--- /dev/null
+++ b/arch/powerpc/platforms/maple/maple.h
@@ -0,0 +1,12 @@
+/*
+ * Declarations for maple-specific code.
+ *
+ * Maple is the name of a PPC970 evaluation board.
+ */
+extern int maple_set_rtc_time(struct rtc_time *tm);
+extern void maple_get_rtc_time(struct rtc_time *tm);
+extern unsigned long maple_get_boot_time(void);
+extern void maple_calibrate_decr(void);
+extern void maple_pci_init(void);
+extern void maple_pcibios_fixup(void);
+extern int maple_pci_get_legacy_ide_irq(struct pci_dev *dev, int channel);
diff --git a/arch/powerpc/platforms/maple/pci.c b/arch/powerpc/platforms/maple/pci.c
new file mode 100644
index 0000000..340c21c
--- /dev/null
+++ b/arch/powerpc/platforms/maple/pci.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org),
+ *		      IBM Corp.
+ *
+ * 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.
+ */
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/sections.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/iommu.h>
+#include <asm/ppc-pci.h>
+
+#include "maple.h"
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+static struct pci_controller *u3_agp, *u3_ht;
+
+static int __init fixup_one_level_bus_range(struct device_node *node, int higher)
+{
+	for (; node != 0;node = node->sibling) {
+		int * bus_range;
+		unsigned int *class_code;
+		int len;
+
+		/* For PCI<->PCI bridges or CardBus bridges, we go down */
+		class_code = (unsigned int *) get_property(node, "class-code", NULL);
+		if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
+			(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS))
+			continue;
+		bus_range = (int *) get_property(node, "bus-range", &len);
+		if (bus_range != NULL && len > 2 * sizeof(int)) {
+			if (bus_range[1] > higher)
+				higher = bus_range[1];
+		}
+		higher = fixup_one_level_bus_range(node->child, higher);
+	}
+	return higher;
+}
+
+/* This routine fixes the "bus-range" property of all bridges in the
+ * system since they tend to have their "last" member wrong on macs
+ *
+ * Note that the bus numbers manipulated here are OF bus numbers, they
+ * are not Linux bus numbers.
+ */
+static void __init fixup_bus_range(struct device_node *bridge)
+{
+	int * bus_range;
+	int len;
+
+	/* Lookup the "bus-range" property for the hose */
+	bus_range = (int *) get_property(bridge, "bus-range", &len);
+	if (bus_range == NULL || len < 2 * sizeof(int)) {
+		printk(KERN_WARNING "Can't get bus-range for %s\n",
+			       bridge->full_name);
+		return;
+	}
+	bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]);
+}
+
+
+#define U3_AGP_CFA0(devfn, off)	\
+	((1 << (unsigned long)PCI_SLOT(dev_fn)) \
+	| (((unsigned long)PCI_FUNC(dev_fn)) << 8) \
+	| (((unsigned long)(off)) & 0xFCUL))
+
+#define U3_AGP_CFA1(bus, devfn, off)	\
+	((((unsigned long)(bus)) << 16) \
+	|(((unsigned long)(devfn)) << 8) \
+	|(((unsigned long)(off)) & 0xFCUL) \
+	|1UL)
+
+static unsigned long u3_agp_cfg_access(struct pci_controller* hose,
+				       u8 bus, u8 dev_fn, u8 offset)
+{
+	unsigned int caddr;
+
+	if (bus == hose->first_busno) {
+		if (dev_fn < (11 << 3))
+			return 0;
+		caddr = U3_AGP_CFA0(dev_fn, offset);
+	} else
+		caddr = U3_AGP_CFA1(bus, dev_fn, offset);
+
+	/* Uninorth will return garbage if we don't read back the value ! */
+	do {
+		out_le32(hose->cfg_addr, caddr);
+	} while (in_le32(hose->cfg_addr) != caddr);
+
+	offset &= 0x07;
+	return ((unsigned long)hose->cfg_data) + offset;
+}
+
+static int u3_agp_read_config(struct pci_bus *bus, unsigned int devfn,
+			      int offset, int len, u32 *val)
+{
+	struct pci_controller *hose;
+	unsigned long addr;
+
+	hose = pci_bus_to_host(bus);
+	if (hose == NULL)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = u3_agp_cfg_access(hose, bus->number, devfn, offset);
+	if (!addr)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	/*
+	 * Note: the caller has already checked that offset is
+	 * suitably aligned and that len is 1, 2 or 4.
+	 */
+	switch (len) {
+	case 1:
+		*val = in_8((u8 *)addr);
+		break;
+	case 2:
+		*val = in_le16((u16 *)addr);
+		break;
+	default:
+		*val = in_le32((u32 *)addr);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int u3_agp_write_config(struct pci_bus *bus, unsigned int devfn,
+			       int offset, int len, u32 val)
+{
+	struct pci_controller *hose;
+	unsigned long addr;
+
+	hose = pci_bus_to_host(bus);
+	if (hose == NULL)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = u3_agp_cfg_access(hose, bus->number, devfn, offset);
+	if (!addr)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	/*
+	 * Note: the caller has already checked that offset is
+	 * suitably aligned and that len is 1, 2 or 4.
+	 */
+	switch (len) {
+	case 1:
+		out_8((u8 *)addr, val);
+		(void) in_8((u8 *)addr);
+		break;
+	case 2:
+		out_le16((u16 *)addr, val);
+		(void) in_le16((u16 *)addr);
+		break;
+	default:
+		out_le32((u32 *)addr, val);
+		(void) in_le32((u32 *)addr);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops u3_agp_pci_ops =
+{
+	u3_agp_read_config,
+	u3_agp_write_config
+};
+
+
+#define U3_HT_CFA0(devfn, off)		\
+		((((unsigned long)devfn) << 8) | offset)
+#define U3_HT_CFA1(bus, devfn, off)	\
+		(U3_HT_CFA0(devfn, off) \
+		+ (((unsigned long)bus) << 16) \
+		+ 0x01000000UL)
+
+static unsigned long u3_ht_cfg_access(struct pci_controller* hose,
+				      u8 bus, u8 devfn, u8 offset)
+{
+	if (bus == hose->first_busno) {
+		if (PCI_SLOT(devfn) == 0)
+			return 0;
+		return ((unsigned long)hose->cfg_data) + U3_HT_CFA0(devfn, offset);
+	} else
+		return ((unsigned long)hose->cfg_data) + U3_HT_CFA1(bus, devfn, offset);
+}
+
+static int u3_ht_read_config(struct pci_bus *bus, unsigned int devfn,
+			     int offset, int len, u32 *val)
+{
+	struct pci_controller *hose;
+	unsigned long addr;
+
+	hose = pci_bus_to_host(bus);
+	if (hose == NULL)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
+	if (!addr)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	/*
+	 * Note: the caller has already checked that offset is
+	 * suitably aligned and that len is 1, 2 or 4.
+	 */
+	switch (len) {
+	case 1:
+		*val = in_8((u8 *)addr);
+		break;
+	case 2:
+		*val = in_le16((u16 *)addr);
+		break;
+	default:
+		*val = in_le32((u32 *)addr);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int u3_ht_write_config(struct pci_bus *bus, unsigned int devfn,
+			      int offset, int len, u32 val)
+{
+	struct pci_controller *hose;
+	unsigned long addr;
+
+	hose = pci_bus_to_host(bus);
+	if (hose == NULL)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+
+	addr = u3_ht_cfg_access(hose, bus->number, devfn, offset);
+	if (!addr)
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	/*
+	 * Note: the caller has already checked that offset is
+	 * suitably aligned and that len is 1, 2 or 4.
+	 */
+	switch (len) {
+	case 1:
+		out_8((u8 *)addr, val);
+		(void) in_8((u8 *)addr);
+		break;
+	case 2:
+		out_le16((u16 *)addr, val);
+		(void) in_le16((u16 *)addr);
+		break;
+	default:
+		out_le32((u32 *)addr, val);
+		(void) in_le32((u32 *)addr);
+		break;
+	}
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops u3_ht_pci_ops =
+{
+	u3_ht_read_config,
+	u3_ht_write_config
+};
+
+static void __init setup_u3_agp(struct pci_controller* hose)
+{
+	/* On G5, we move AGP up to high bus number so we don't need
+	 * to reassign bus numbers for HT. If we ever have P2P bridges
+	 * on AGP, we'll have to move pci_assign_all_buses to the
+	 * pci_controller structure so we enable it for AGP and not for
+	 * HT childs.
+	 * We hard code the address because of the different size of
+	 * the reg address cell, we shall fix that by killing struct
+	 * reg_property and using some accessor functions instead
+	 */
+	hose->first_busno = 0xf0;
+	hose->last_busno = 0xff;
+	hose->ops = &u3_agp_pci_ops;
+	hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000);
+	hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000);
+
+	u3_agp = hose;
+}
+
+static void __init setup_u3_ht(struct pci_controller* hose)
+{
+	hose->ops = &u3_ht_pci_ops;
+
+	/* We hard code the address because of the different size of
+	 * the reg address cell, we shall fix that by killing struct
+	 * reg_property and using some accessor functions instead
+	 */
+	hose->cfg_data = (volatile unsigned char *)ioremap(0xf2000000, 0x02000000);
+
+	hose->first_busno = 0;
+	hose->last_busno = 0xef;
+
+	u3_ht = hose;
+}
+
+static int __init add_bridge(struct device_node *dev)
+{
+	int len;
+	struct pci_controller *hose;
+	char* disp_name;
+	int *bus_range;
+	int primary = 1;
+	struct property *of_prop;
+
+	DBG("Adding PCI host bridge %s\n", dev->full_name);
+
+	bus_range = (int *) get_property(dev, "bus-range", &len);
+	if (bus_range == NULL || len < 2 * sizeof(int)) {
+		printk(KERN_WARNING "Can't get bus-range for %s, assume bus 0\n",
+		dev->full_name);
+	}
+
+	hose = alloc_bootmem(sizeof(struct pci_controller));
+	if (hose == NULL)
+		return -ENOMEM;
+	pci_setup_pci_controller(hose);
+
+	hose->arch_data = dev;
+	hose->first_busno = bus_range ? bus_range[0] : 0;
+	hose->last_busno = bus_range ? bus_range[1] : 0xff;
+
+	of_prop = alloc_bootmem(sizeof(struct property) +
+				sizeof(hose->global_number));
+	if (of_prop) {
+		memset(of_prop, 0, sizeof(struct property));
+		of_prop->name = "linux,pci-domain";
+		of_prop->length = sizeof(hose->global_number);
+		of_prop->value = (unsigned char *)&of_prop[1];
+		memcpy(of_prop->value, &hose->global_number, sizeof(hose->global_number));
+		prom_add_property(dev, of_prop);
+	}
+
+	disp_name = NULL;
+	if (device_is_compatible(dev, "u3-agp")) {
+		setup_u3_agp(hose);
+		disp_name = "U3-AGP";
+		primary = 0;
+	} else if (device_is_compatible(dev, "u3-ht")) {
+		setup_u3_ht(hose);
+		disp_name = "U3-HT";
+		primary = 1;
+	}
+	printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n",
+		disp_name, hose->first_busno, hose->last_busno);
+
+	/* Interpret the "ranges" property */
+	/* This also maps the I/O region and sets isa_io/mem_base */
+	pci_process_bridge_OF_ranges(hose, dev, primary);
+	pci_setup_phb_io(hose, primary);
+
+	/* Fixup "bus-range" OF property */
+	fixup_bus_range(dev);
+
+	return 0;
+}
+
+
+void __init maple_pcibios_fixup(void)
+{
+	struct pci_dev *dev = NULL;
+
+	DBG(" -> maple_pcibios_fixup\n");
+
+	for_each_pci_dev(dev)
+		pci_read_irq_line(dev);
+
+	/* Do the mapping of the IO space */
+	phbs_remap_io();
+
+	DBG(" <- maple_pcibios_fixup\n");
+}
+
+static void __init maple_fixup_phb_resources(void)
+{
+	struct pci_controller *hose, *tmp;
+	
+	list_for_each_entry_safe(hose, tmp, &hose_list, list_node) {
+		unsigned long offset = (unsigned long)hose->io_base_virt - pci_io_base;
+		hose->io_resource.start += offset;
+		hose->io_resource.end += offset;
+		printk(KERN_INFO "PCI Host %d, io start: %lx; io end: %lx\n",
+		       hose->global_number,
+		       hose->io_resource.start, hose->io_resource.end);
+	}
+}
+
+void __init maple_pci_init(void)
+{
+	struct device_node *np, *root;
+	struct device_node *ht = NULL;
+
+	/* Probe root PCI hosts, that is on U3 the AGP host and the
+	 * HyperTransport host. That one is actually "kept" around
+	 * and actually added last as it's resource management relies
+	 * on the AGP resources to have been setup first
+	 */
+	root = of_find_node_by_path("/");
+	if (root == NULL) {
+		printk(KERN_CRIT "maple_find_bridges: can't find root of device tree\n");
+		return;
+	}
+	for (np = NULL; (np = of_get_next_child(root, np)) != NULL;) {
+		if (np->name == NULL)
+			continue;
+		if (strcmp(np->name, "pci") == 0) {
+			if (add_bridge(np) == 0)
+				of_node_get(np);
+		}
+		if (strcmp(np->name, "ht") == 0) {
+			of_node_get(np);
+			ht = np;
+		}
+	}
+	of_node_put(root);
+
+	/* Now setup the HyperTransport host if we found any
+	 */
+	if (ht && add_bridge(ht) != 0)
+		of_node_put(ht);
+
+	/* Fixup the IO resources on our host bridges as the common code
+	 * does it only for childs of the host bridges
+	 */
+	maple_fixup_phb_resources();
+
+	/* Setup the linkage between OF nodes and PHBs */ 
+	pci_devs_phb_init();
+
+	/* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We
+	 * assume there is no P2P bridge on the AGP bus, which should be a
+	 * safe assumptions hopefully.
+	 */
+	if (u3_agp) {
+		struct device_node *np = u3_agp->arch_data;
+		PCI_DN(np)->busno = 0xf0;
+		for (np = np->child; np; np = np->sibling)
+			PCI_DN(np)->busno = 0xf0;
+	}
+
+	/* Tell pci.c to use the common resource allocation mecanism */
+	pci_probe_only = 0;
+	
+	/* Allow all IO */
+	io_page_mask = -1;
+}
+
+int maple_pci_get_legacy_ide_irq(struct pci_dev *pdev, int channel)
+{
+	struct device_node *np;
+	int irq = channel ? 15 : 14;
+
+	if (pdev->vendor != PCI_VENDOR_ID_AMD ||
+	    pdev->device != PCI_DEVICE_ID_AMD_8111_IDE)
+		return irq;
+
+	np = pci_device_to_OF_node(pdev);
+	if (np == NULL)
+		return irq;
+	if (np->n_intrs < 2)
+		return irq;
+	return np->intrs[channel & 0x1].line;
+}
+
+/* XXX: To remove once all firmwares are ok */
+static void fixup_maple_ide(struct pci_dev* dev)
+{
+#if 0 /* Enable this to enable IDE port 0 */
+	{
+		u8 v;
+
+		pci_read_config_byte(dev, 0x40, &v);
+		v |= 2;
+		pci_write_config_byte(dev, 0x40, v);
+	}
+#endif
+#if 0 /* fix bus master base */
+	pci_write_config_dword(dev, 0x20, 0xcc01);
+	printk("old ide resource: %lx -> %lx \n",
+	       dev->resource[4].start, dev->resource[4].end);
+	dev->resource[4].start = 0xcc00;
+	dev->resource[4].end = 0xcc10;
+#endif
+#if 1 /* Enable this to fixup IDE sense/polarity of irqs in IO-APICs */
+	{
+		struct pci_dev *apicdev;
+		u32 v;
+
+		apicdev = pci_get_slot (dev->bus, PCI_DEVFN(5,0));
+		if (apicdev == NULL)
+			printk("IDE Fixup IRQ: Can't find IO-APIC !\n");
+		else {
+			pci_write_config_byte(apicdev, 0xf2, 0x10 + 2*14);
+			pci_read_config_dword(apicdev, 0xf4, &v);
+			v &= ~0x00000022;
+			pci_write_config_dword(apicdev, 0xf4, v);
+			pci_write_config_byte(apicdev, 0xf2, 0x10 + 2*15);
+			pci_read_config_dword(apicdev, 0xf4, &v);
+			v &= ~0x00000022;
+			pci_write_config_dword(apicdev, 0xf4, v);
+			pci_dev_put(apicdev);
+		}
+	}
+#endif
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_IDE,
+			 fixup_maple_ide);
diff --git a/arch/powerpc/platforms/maple/setup.c b/arch/powerpc/platforms/maple/setup.c
new file mode 100644
index 0000000..7ece898
--- /dev/null
+++ b/arch/powerpc/platforms/maple/setup.c
@@ -0,0 +1,295 @@
+/*
+ *  Maple (970 eval board) setup code
+ *
+ *  (c) Copyright 2004 Benjamin Herrenschmidt (benh@kernel.crashing.org),
+ *                     IBM Corp. 
+ *
+ *  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.
+ *
+ */
+
+#define DEBUG
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/user.h>
+#include <linux/a.out.h>
+#include <linux/tty.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/initrd.h>
+#include <linux/vt_kern.h>
+#include <linux/console.h>
+#include <linux/ide.h>
+#include <linux/pci.h>
+#include <linux/adb.h>
+#include <linux/cuda.h>
+#include <linux/pmu.h>
+#include <linux/irq.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+#include <linux/serial.h>
+#include <linux/smp.h>
+
+#include <asm/processor.h>
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/iommu.h>
+#include <asm/machdep.h>
+#include <asm/dma.h>
+#include <asm/cputable.h>
+#include <asm/time.h>
+#include <asm/of_device.h>
+#include <asm/lmb.h>
+#include <asm/mpic.h>
+#include <asm/udbg.h>
+
+#include "maple.h"
+
+#ifdef DEBUG
+#define DBG(fmt...) udbg_printf(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+extern void generic_find_legacy_serial_ports(u64 *physport,
+		unsigned int *default_speed);
+
+static void maple_restart(char *cmd)
+{
+	unsigned int maple_nvram_base;
+	unsigned int maple_nvram_offset;
+	unsigned int maple_nvram_command;
+	struct device_node *rtcs;
+
+	/* find NVRAM device */
+	rtcs = find_compatible_devices("nvram", "AMD8111");
+	if (rtcs && rtcs->addrs) {
+		maple_nvram_base = rtcs->addrs[0].address;
+	} else {
+		printk(KERN_EMERG "Maple: Unable to find NVRAM\n");
+		printk(KERN_EMERG "Maple: Manual Restart Required\n");
+		return;
+	}
+
+	/* find service processor device */
+	rtcs = find_devices("service-processor");
+	if (!rtcs) {
+		printk(KERN_EMERG "Maple: Unable to find Service Processor\n");
+		printk(KERN_EMERG "Maple: Manual Restart Required\n");
+		return;
+	}
+	maple_nvram_offset = *(unsigned int*) get_property(rtcs,
+			"restart-addr", NULL);
+	maple_nvram_command = *(unsigned int*) get_property(rtcs,
+			"restart-value", NULL);
+
+	/* send command */
+	outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset);
+	for (;;) ;
+}
+
+static void maple_power_off(void)
+{
+	unsigned int maple_nvram_base;
+	unsigned int maple_nvram_offset;
+	unsigned int maple_nvram_command;
+	struct device_node *rtcs;
+
+	/* find NVRAM device */
+	rtcs = find_compatible_devices("nvram", "AMD8111");
+	if (rtcs && rtcs->addrs) {
+		maple_nvram_base = rtcs->addrs[0].address;
+	} else {
+		printk(KERN_EMERG "Maple: Unable to find NVRAM\n");
+		printk(KERN_EMERG "Maple: Manual Power-Down Required\n");
+		return;
+	}
+
+	/* find service processor device */
+	rtcs = find_devices("service-processor");
+	if (!rtcs) {
+		printk(KERN_EMERG "Maple: Unable to find Service Processor\n");
+		printk(KERN_EMERG "Maple: Manual Power-Down Required\n");
+		return;
+	}
+	maple_nvram_offset = *(unsigned int*) get_property(rtcs,
+			"power-off-addr", NULL);
+	maple_nvram_command = *(unsigned int*) get_property(rtcs,
+			"power-off-value", NULL);
+
+	/* send command */
+	outb_p(maple_nvram_command, maple_nvram_base + maple_nvram_offset);
+	for (;;) ;
+}
+
+static void maple_halt(void)
+{
+	maple_power_off();
+}
+
+#ifdef CONFIG_SMP
+struct smp_ops_t maple_smp_ops = {
+	.probe		= smp_mpic_probe,
+	.message_pass	= smp_mpic_message_pass,
+	.kick_cpu	= smp_generic_kick_cpu,
+	.setup_cpu	= smp_mpic_setup_cpu,
+	.give_timebase	= smp_generic_give_timebase,
+	.take_timebase	= smp_generic_take_timebase,
+};
+#endif /* CONFIG_SMP */
+
+void __init maple_setup_arch(void)
+{
+	/* init to some ~sane value until calibrate_delay() runs */
+	loops_per_jiffy = 50000000;
+
+	/* Setup SMP callback */
+#ifdef CONFIG_SMP
+	smp_ops = &maple_smp_ops;
+#endif
+	/* Lookup PCI hosts */
+       	maple_pci_init();
+
+#ifdef CONFIG_DUMMY_CONSOLE
+	conswitchp = &dummy_con;
+#endif
+
+	printk(KERN_INFO "Using native/NAP idle loop\n");
+}
+
+/* 
+ * Early initialization.
+ */
+static void __init maple_init_early(void)
+{
+	unsigned int default_speed;
+	u64 physport;
+
+	DBG(" -> maple_init_early\n");
+
+	/* Initialize hash table, from now on, we can take hash faults
+	 * and call ioremap
+	 */
+	hpte_init_native();
+
+	/* Find the serial port */
+	generic_find_legacy_serial_ports(&physport, &default_speed);
+
+	DBG("phys port addr: %lx\n", (long)physport);
+
+	if (physport) {
+		void *comport;
+		/* Map the uart for udbg. */
+		comport = (void *)ioremap(physport, 16);
+		udbg_init_uart(comport, default_speed);
+
+		DBG("Hello World !\n");
+	}
+
+	/* Setup interrupt mapping options */
+	ppc64_interrupt_controller = IC_OPEN_PIC;
+
+	iommu_init_early_u3();
+
+	DBG(" <- maple_init_early\n");
+}
+
+
+static __init void maple_init_IRQ(void)
+{
+	struct device_node *root;
+	unsigned int *opprop;
+	unsigned long opic_addr;
+	struct mpic *mpic;
+	unsigned char senses[128];
+	int n;
+
+	DBG(" -> maple_init_IRQ\n");
+
+	/* XXX: Non standard, replace that with a proper openpic/mpic node
+	 * in the device-tree. Find the Open PIC if present */
+	root = of_find_node_by_path("/");
+	opprop = (unsigned int *) get_property(root,
+				"platform-open-pic", NULL);
+	if (opprop == 0)
+		panic("OpenPIC not found !\n");
+
+	n = prom_n_addr_cells(root);
+	for (opic_addr = 0; n > 0; --n)
+		opic_addr = (opic_addr << 32) + *opprop++;
+	of_node_put(root);
+
+	/* Obtain sense values from device-tree */
+	prom_get_irq_senses(senses, 0, 128);
+
+	mpic = mpic_alloc(opic_addr,
+			  MPIC_PRIMARY | MPIC_BIG_ENDIAN |
+			  MPIC_BROKEN_U3 | MPIC_WANTS_RESET,
+			  0, 0, 128, 128, senses, 128, "U3-MPIC");
+	BUG_ON(mpic == NULL);
+	mpic_init(mpic);
+
+	DBG(" <- maple_init_IRQ\n");
+}
+
+static void __init maple_progress(char *s, unsigned short hex)
+{
+	printk("*** %04x : %s\n", hex, s ? s : "");
+}
+
+
+/*
+ * Called very early, MMU is off, device-tree isn't unflattened
+ */
+static int __init maple_probe(int platform)
+{
+	if (platform != PLATFORM_MAPLE)
+		return 0;
+	/*
+	 * On U3, the DART (iommu) must be allocated now since it
+	 * has an impact on htab_initialize (due to the large page it
+	 * occupies having to be broken up so the DART itself is not
+	 * part of the cacheable linar mapping
+	 */
+	alloc_u3_dart_table();
+
+	return 1;
+}
+
+struct machdep_calls __initdata maple_md = {
+	.probe			= maple_probe,
+	.setup_arch		= maple_setup_arch,
+	.init_early		= maple_init_early,
+	.init_IRQ		= maple_init_IRQ,
+	.get_irq		= mpic_get_irq,
+	.pcibios_fixup		= maple_pcibios_fixup,
+	.pci_get_legacy_ide_irq	= maple_pci_get_legacy_ide_irq,
+	.restart		= maple_restart,
+	.power_off		= maple_power_off,
+	.halt			= maple_halt,
+       	.get_boot_time		= maple_get_boot_time,
+       	.set_rtc_time		= maple_set_rtc_time,
+       	.get_rtc_time		= maple_get_rtc_time,
+      	.calibrate_decr		= generic_calibrate_decr,
+	.progress		= maple_progress,
+	.idle_loop		= native_idle,
+};
diff --git a/arch/powerpc/platforms/maple/time.c b/arch/powerpc/platforms/maple/time.c
new file mode 100644
index 0000000..40fc07a
--- /dev/null
+++ b/arch/powerpc/platforms/maple/time.c
@@ -0,0 +1,180 @@
+/*
+ *  arch/ppc64/kernel/maple_time.c
+ *
+ *  (c) Copyright 2004 Benjamin Herrenschmidt (benh@kernel.crashing.org),
+ *                     IBM Corp. 
+ *
+ *  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.
+ *
+ */
+
+#undef DEBUG
+
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include <linux/interrupt.h>
+#include <linux/mc146818rtc.h>
+#include <linux/bcd.h>
+
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+
+#include "maple.h"
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+extern void GregorianDay(struct rtc_time * tm);
+
+static int maple_rtc_addr;
+
+static int maple_clock_read(int addr)
+{
+	outb_p(addr, maple_rtc_addr);
+	return inb_p(maple_rtc_addr+1);
+}
+
+static void maple_clock_write(unsigned long val, int addr)
+{
+	outb_p(addr, maple_rtc_addr);
+	outb_p(val, maple_rtc_addr+1);
+}
+
+void maple_get_rtc_time(struct rtc_time *tm)
+{
+	int uip, i;
+
+	/* The Linux interpretation of the CMOS clock register contents:
+	 * When the Update-In-Progress (UIP) flag goes from 1 to 0, the
+	 * RTC registers show the second which has precisely just started.
+	 * Let's hope other operating systems interpret the RTC the same way.
+	 */
+
+	/* Since the UIP flag is set for about 2.2 ms and the clock
+	 * is typically written with a precision of 1 jiffy, trying
+	 * to obtain a precision better than a few milliseconds is
+	 * an illusion. Only consistency is interesting, this also
+	 * allows to use the routine for /dev/rtc without a potential
+	 * 1 second kernel busy loop triggered by any reader of /dev/rtc.
+	 */
+
+	for (i = 0; i<1000000; i++) {
+		uip = maple_clock_read(RTC_FREQ_SELECT);
+		tm->tm_sec = maple_clock_read(RTC_SECONDS);
+		tm->tm_min = maple_clock_read(RTC_MINUTES);
+		tm->tm_hour = maple_clock_read(RTC_HOURS);
+		tm->tm_mday = maple_clock_read(RTC_DAY_OF_MONTH);
+		tm->tm_mon = maple_clock_read(RTC_MONTH);
+		tm->tm_year = maple_clock_read(RTC_YEAR);
+		uip |= maple_clock_read(RTC_FREQ_SELECT);
+		if ((uip & RTC_UIP)==0)
+			break;
+	}
+
+	if (!(maple_clock_read(RTC_CONTROL) & RTC_DM_BINARY)
+	    || RTC_ALWAYS_BCD) {
+		BCD_TO_BIN(tm->tm_sec);
+		BCD_TO_BIN(tm->tm_min);
+		BCD_TO_BIN(tm->tm_hour);
+		BCD_TO_BIN(tm->tm_mday);
+		BCD_TO_BIN(tm->tm_mon);
+		BCD_TO_BIN(tm->tm_year);
+	  }
+	if ((tm->tm_year + 1900) < 1970)
+		tm->tm_year += 100;
+
+	GregorianDay(tm);
+}
+
+int maple_set_rtc_time(struct rtc_time *tm)
+{
+	unsigned char save_control, save_freq_select;
+	int sec, min, hour, mon, mday, year;
+
+	spin_lock(&rtc_lock);
+
+	save_control = maple_clock_read(RTC_CONTROL); /* tell the clock it's being set */
+
+	maple_clock_write((save_control|RTC_SET), RTC_CONTROL);
+
+	save_freq_select = maple_clock_read(RTC_FREQ_SELECT); /* stop and reset prescaler */
+
+	maple_clock_write((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+	sec = tm->tm_sec;
+	min = tm->tm_min;
+	hour = tm->tm_hour;
+	mon = tm->tm_mon;
+	mday = tm->tm_mday;
+	year = tm->tm_year;
+
+	if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+		BIN_TO_BCD(sec);
+		BIN_TO_BCD(min);
+		BIN_TO_BCD(hour);
+		BIN_TO_BCD(mon);
+		BIN_TO_BCD(mday);
+		BIN_TO_BCD(year);
+	}
+	maple_clock_write(sec, RTC_SECONDS);
+	maple_clock_write(min, RTC_MINUTES);
+	maple_clock_write(hour, RTC_HOURS);
+	maple_clock_write(mon, RTC_MONTH);
+	maple_clock_write(mday, RTC_DAY_OF_MONTH);
+	maple_clock_write(year, RTC_YEAR);
+
+	/* The following flags have to be released exactly in this order,
+	 * otherwise the DS12887 (popular MC146818A clone with integrated
+	 * battery and quartz) will not reset the oscillator and will not
+	 * update precisely 500 ms later. You won't find this mentioned in
+	 * the Dallas Semiconductor data sheets, but who believes data
+	 * sheets anyway ...                           -- Markus Kuhn
+	 */
+	maple_clock_write(save_control, RTC_CONTROL);
+	maple_clock_write(save_freq_select, RTC_FREQ_SELECT);
+
+	spin_unlock(&rtc_lock);
+
+	return 0;
+}
+
+unsigned long __init maple_get_boot_time(void)
+{
+	struct rtc_time tm;
+	struct device_node *rtcs;
+
+	rtcs = find_compatible_devices("rtc", "pnpPNP,b00");
+	if (rtcs && rtcs->addrs) {
+		maple_rtc_addr = rtcs->addrs[0].address;
+		printk(KERN_INFO "Maple: Found RTC at 0x%x\n", maple_rtc_addr);
+	} else {
+		maple_rtc_addr = RTC_PORT(0); /* legacy address */
+		printk(KERN_INFO "Maple: No device node for RTC, assuming "
+		       "legacy address (0x%x)\n", maple_rtc_addr);
+	}
+	
+	maple_get_rtc_time(&tm);
+	return mktime(tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+		      tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+