summaryrefslogtreecommitdiff
path: root/arch/ppc/syslib
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/ppc/syslib
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'arch/ppc/syslib')
-rw-r--r--arch/ppc/syslib/Makefile115
-rw-r--r--arch/ppc/syslib/btext.c861
-rw-r--r--arch/ppc/syslib/cpc700.h98
-rw-r--r--arch/ppc/syslib/cpc700_pic.c187
-rw-r--r--arch/ppc/syslib/cpc710.h83
-rw-r--r--arch/ppc/syslib/cpm2_common.c198
-rw-r--r--arch/ppc/syslib/cpm2_pic.c172
-rw-r--r--arch/ppc/syslib/cpm2_pic.h8
-rw-r--r--arch/ppc/syslib/dcr.S41
-rw-r--r--arch/ppc/syslib/gen550.h16
-rw-r--r--arch/ppc/syslib/gen550_dbg.c182
-rw-r--r--arch/ppc/syslib/gen550_kgdb.c86
-rw-r--r--arch/ppc/syslib/gt64260_pic.c328
-rw-r--r--arch/ppc/syslib/harrier.c302
-rw-r--r--arch/ppc/syslib/hawk_common.c319
-rw-r--r--arch/ppc/syslib/i8259.c211
-rw-r--r--arch/ppc/syslib/ibm440gp_common.c76
-rw-r--r--arch/ppc/syslib/ibm440gp_common.h35
-rw-r--r--arch/ppc/syslib/ibm440gx_common.c270
-rw-r--r--arch/ppc/syslib/ibm440gx_common.h57
-rw-r--r--arch/ppc/syslib/ibm440sp_common.c71
-rw-r--r--arch/ppc/syslib/ibm440sp_common.h25
-rw-r--r--arch/ppc/syslib/ibm44x_common.c193
-rw-r--r--arch/ppc/syslib/ibm44x_common.h42
-rw-r--r--arch/ppc/syslib/ibm_ocp.c9
-rw-r--r--arch/ppc/syslib/indirect_pci.c135
-rw-r--r--arch/ppc/syslib/ipic.c646
-rw-r--r--arch/ppc/syslib/ipic.h49
-rw-r--r--arch/ppc/syslib/m8260_pci.c194
-rw-r--r--arch/ppc/syslib/m8260_pci.h76
-rw-r--r--arch/ppc/syslib/m8260_pci_erratum9.c473
-rw-r--r--arch/ppc/syslib/m8260_setup.c264
-rw-r--r--arch/ppc/syslib/m8xx_setup.c433
-rw-r--r--arch/ppc/syslib/m8xx_wdt.c99
-rw-r--r--arch/ppc/syslib/m8xx_wdt.h16
-rw-r--r--arch/ppc/syslib/mpc10x_common.c510
-rw-r--r--arch/ppc/syslib/mpc52xx_devices.c318
-rw-r--r--arch/ppc/syslib/mpc52xx_pci.c235
-rw-r--r--arch/ppc/syslib/mpc52xx_pci.h139
-rw-r--r--arch/ppc/syslib/mpc52xx_pic.c257
-rw-r--r--arch/ppc/syslib/mpc52xx_setup.c230
-rw-r--r--arch/ppc/syslib/mpc52xx_sys.c38
-rw-r--r--arch/ppc/syslib/mpc83xx_devices.c237
-rw-r--r--arch/ppc/syslib/mpc83xx_sys.c100
-rw-r--r--arch/ppc/syslib/mpc85xx_devices.c552
-rw-r--r--arch/ppc/syslib/mpc85xx_sys.c118
-rw-r--r--arch/ppc/syslib/mv64360_pic.c426
-rw-r--r--arch/ppc/syslib/mv64x60.c2392
-rw-r--r--arch/ppc/syslib/mv64x60_dbg.c123
-rw-r--r--arch/ppc/syslib/mv64x60_win.c1168
-rw-r--r--arch/ppc/syslib/ocp.c485
-rw-r--r--arch/ppc/syslib/of_device.c273
-rw-r--r--arch/ppc/syslib/open_pic.c1083
-rw-r--r--arch/ppc/syslib/open_pic2.c716
-rw-r--r--arch/ppc/syslib/open_pic_defs.h292
-rw-r--r--arch/ppc/syslib/pci_auto.c517
-rw-r--r--arch/ppc/syslib/ppc403_pic.c127
-rw-r--r--arch/ppc/syslib/ppc405_pci.c177
-rw-r--r--arch/ppc/syslib/ppc4xx_dma.c708
-rw-r--r--arch/ppc/syslib/ppc4xx_kgdb.c124
-rw-r--r--arch/ppc/syslib/ppc4xx_pic.c244
-rw-r--r--arch/ppc/syslib/ppc4xx_pm.c47
-rw-r--r--arch/ppc/syslib/ppc4xx_setup.c321
-rw-r--r--arch/ppc/syslib/ppc4xx_sgdma.c467
-rw-r--r--arch/ppc/syslib/ppc83xx_setup.c138
-rw-r--r--arch/ppc/syslib/ppc83xx_setup.h53
-rw-r--r--arch/ppc/syslib/ppc85xx_common.c33
-rw-r--r--arch/ppc/syslib/ppc85xx_common.h25
-rw-r--r--arch/ppc/syslib/ppc85xx_setup.c354
-rw-r--r--arch/ppc/syslib/ppc85xx_setup.h59
-rw-r--r--arch/ppc/syslib/ppc8xx_pic.c130
-rw-r--r--arch/ppc/syslib/ppc8xx_pic.h21
-rw-r--r--arch/ppc/syslib/ppc_sys.c103
-rw-r--r--arch/ppc/syslib/prep_nvram.c141
-rw-r--r--arch/ppc/syslib/prom.c1447
-rw-r--r--arch/ppc/syslib/prom_init.c1002
-rw-r--r--arch/ppc/syslib/qspan_pci.c381
-rw-r--r--arch/ppc/syslib/todc_time.c513
-rw-r--r--arch/ppc/syslib/xilinx_pic.c157
79 files changed, 23351 insertions, 0 deletions
diff --git a/arch/ppc/syslib/Makefile b/arch/ppc/syslib/Makefile
new file mode 100644
index 000000000000..dd418ea3426c
--- /dev/null
+++ b/arch/ppc/syslib/Makefile
@@ -0,0 +1,115 @@
+#
+# Makefile for the linux kernel.
+#
+
+CFLAGS_prom_init.o += -fPIC
+CFLAGS_btext.o += -fPIC
+
+wdt-mpc8xx-$(CONFIG_8xx_WDT) += m8xx_wdt.o
+
+obj-$(CONFIG_PPCBUG_NVRAM) += prep_nvram.o
+obj-$(CONFIG_PPC_OCP) += ocp.o
+obj-$(CONFIG_IBM_OCP) += ibm_ocp.o
+obj-$(CONFIG_44x) += ibm44x_common.o
+obj-$(CONFIG_440GP) += ibm440gp_common.o
+obj-$(CONFIG_440GX) += ibm440gx_common.o
+obj-$(CONFIG_440SP) += ibm440gx_common.o ibm440sp_common.o
+ifeq ($(CONFIG_4xx),y)
+ifeq ($(CONFIG_VIRTEX_II_PRO),y)
+obj-$(CONFIG_40x) += xilinx_pic.o
+else
+ifeq ($(CONFIG_403),y)
+obj-$(CONFIG_40x) += ppc403_pic.o
+else
+obj-$(CONFIG_40x) += ppc4xx_pic.o
+endif
+endif
+obj-$(CONFIG_44x) += ppc4xx_pic.o
+obj-$(CONFIG_40x) += ppc4xx_setup.o
+obj-$(CONFIG_GEN_RTC) += todc_time.o
+obj-$(CONFIG_PPC4xx_DMA) += ppc4xx_dma.o
+obj-$(CONFIG_PPC4xx_EDMA) += ppc4xx_sgdma.o
+ifeq ($(CONFIG_40x),y)
+obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o ppc405_pci.o
+endif
+endif
+obj-$(CONFIG_8xx) += m8xx_setup.o ppc8xx_pic.o $(wdt-mpc8xx-y)
+ifeq ($(CONFIG_8xx),y)
+obj-$(CONFIG_PCI) += qspan_pci.o i8259.o
+endif
+obj-$(CONFIG_PPC_OF) += prom_init.o prom.o of_device.o
+obj-$(CONFIG_PPC_PMAC) += open_pic.o indirect_pci.o
+obj-$(CONFIG_POWER4) += open_pic2.o
+obj-$(CONFIG_PPC_CHRP) += open_pic.o indirect_pci.o i8259.o
+obj-$(CONFIG_PPC_PREP) += open_pic.o indirect_pci.o i8259.o todc_time.o
+obj-$(CONFIG_ADIR) += i8259.o indirect_pci.o pci_auto.o \
+ todc_time.o
+obj-$(CONFIG_CPCI690) += todc_time.o pci_auto.o
+obj-$(CONFIG_EBONY) += indirect_pci.o pci_auto.o todc_time.o
+obj-$(CONFIG_EV64260) += todc_time.o pci_auto.o
+obj-$(CONFIG_CHESTNUT) += mv64360_pic.o pci_auto.o
+obj-$(CONFIG_GEMINI) += open_pic.o indirect_pci.o
+obj-$(CONFIG_GT64260) += gt64260_pic.o
+obj-$(CONFIG_K2) += i8259.o indirect_pci.o todc_time.o \
+ pci_auto.o
+obj-$(CONFIG_LOPEC) += i8259.o pci_auto.o todc_time.o
+obj-$(CONFIG_HDPU) += pci_auto.o
+obj-$(CONFIG_LUAN) += indirect_pci.o pci_auto.o todc_time.o
+obj-$(CONFIG_KATANA) += pci_auto.o
+obj-$(CONFIG_MCPN765) += todc_time.o indirect_pci.o pci_auto.o \
+ open_pic.o i8259.o hawk_common.o
+obj-$(CONFIG_MENF1) += todc_time.o i8259.o mpc10x_common.o \
+ pci_auto.o indirect_pci.o
+obj-$(CONFIG_MV64360) += mv64360_pic.o
+obj-$(CONFIG_MV64X60) += mv64x60.o mv64x60_win.o indirect_pci.o
+obj-$(CONFIG_MVME5100) += open_pic.o todc_time.o indirect_pci.o \
+ pci_auto.o hawk_common.o
+obj-$(CONFIG_MVME5100_IPMC761_PRESENT) += i8259.o
+obj-$(CONFIG_OCOTEA) += indirect_pci.o pci_auto.o todc_time.o
+obj-$(CONFIG_PAL4) += cpc700_pic.o
+obj-$(CONFIG_PCORE) += todc_time.o i8259.o pci_auto.o
+obj-$(CONFIG_POWERPMC250) += pci_auto.o
+obj-$(CONFIG_PPLUS) += hawk_common.o open_pic.o i8259.o \
+ indirect_pci.o todc_time.o pci_auto.o
+obj-$(CONFIG_PRPMC750) += open_pic.o indirect_pci.o pci_auto.o \
+ hawk_common.o
+obj-$(CONFIG_HARRIER) += harrier.o
+obj-$(CONFIG_PRPMC800) += open_pic.o indirect_pci.o pci_auto.o
+obj-$(CONFIG_RADSTONE_PPC7D) += i8259.o pci_auto.o
+obj-$(CONFIG_SANDPOINT) += i8259.o pci_auto.o todc_time.o
+obj-$(CONFIG_SBC82xx) += todc_time.o
+obj-$(CONFIG_SPRUCE) += cpc700_pic.o indirect_pci.o pci_auto.o \
+ todc_time.o
+obj-$(CONFIG_8260) += m8260_setup.o
+obj-$(CONFIG_PCI_8260) += m8260_pci.o indirect_pci.o
+obj-$(CONFIG_8260_PCI9) += m8260_pci_erratum9.o
+obj-$(CONFIG_CPM2) += cpm2_common.o cpm2_pic.o
+ifeq ($(CONFIG_PPC_GEN550),y)
+obj-$(CONFIG_KGDB) += gen550_kgdb.o gen550_dbg.o
+obj-$(CONFIG_SERIAL_TEXT_DEBUG) += gen550_dbg.o
+endif
+ifeq ($(CONFIG_SERIAL_MPSC_CONSOLE),y)
+obj-$(CONFIG_SERIAL_TEXT_DEBUG) += mv64x60_dbg.o
+endif
+obj-$(CONFIG_BOOTX_TEXT) += btext.o
+obj-$(CONFIG_MPC10X_BRIDGE) += mpc10x_common.o indirect_pci.o
+obj-$(CONFIG_MPC10X_OPENPIC) += open_pic.o
+obj-$(CONFIG_40x) += dcr.o
+obj-$(CONFIG_BOOKE) += dcr.o
+obj-$(CONFIG_85xx) += open_pic.o ppc85xx_common.o ppc85xx_setup.o \
+ ppc_sys.o mpc85xx_sys.o \
+ mpc85xx_devices.o
+ifeq ($(CONFIG_85xx),y)
+obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o
+endif
+obj-$(CONFIG_83xx) += ipic.o ppc83xx_setup.o ppc_sys.o \
+ mpc83xx_sys.o mpc83xx_devices.o
+ifeq ($(CONFIG_83xx),y)
+obj-$(CONFIG_PCI) += indirect_pci.o pci_auto.o
+endif
+obj-$(CONFIG_MPC8555_CDS) += todc_time.o
+obj-$(CONFIG_PPC_MPC52xx) += mpc52xx_setup.o mpc52xx_pic.o \
+ mpc52xx_sys.o mpc52xx_devices.o ppc_sys.o
+ifeq ($(CONFIG_PPC_MPC52xx),y)
+obj-$(CONFIG_PCI) += mpc52xx_pci.o
+endif
diff --git a/arch/ppc/syslib/btext.c b/arch/ppc/syslib/btext.c
new file mode 100644
index 000000000000..7734f6836174
--- /dev/null
+++ b/arch/ppc/syslib/btext.c
@@ -0,0 +1,861 @@
+/*
+ * Procedures for drawing on the screen early on in the boot process.
+ *
+ * Benjamin Herrenschmidt <benh@kernel.crashing.org>
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/version.h>
+
+#include <asm/sections.h>
+#include <asm/bootx.h>
+#include <asm/btext.h>
+#include <asm/prom.h>
+#include <asm/page.h>
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/io.h>
+#include <asm/reg.h>
+
+#define NO_SCROLL
+
+#ifndef NO_SCROLL
+static void scrollscreen(void);
+#endif
+
+static void draw_byte(unsigned char c, long locX, long locY);
+static void draw_byte_32(unsigned char *bits, unsigned long *base, int rb);
+static void draw_byte_16(unsigned char *bits, unsigned long *base, int rb);
+static void draw_byte_8(unsigned char *bits, unsigned long *base, int rb);
+
+static int g_loc_X;
+static int g_loc_Y;
+static int g_max_loc_X;
+static int g_max_loc_Y;
+
+unsigned long disp_BAT[2] __initdata = {0, 0};
+
+#define cmapsz (16*256)
+
+static unsigned char vga_font[cmapsz];
+
+int boot_text_mapped;
+int force_printk_to_btext = 0;
+
+boot_infos_t disp_bi;
+
+extern char *klimit;
+
+/*
+ * Powermac can use btext_* after boot for xmon,
+ * chrp only uses it during early boot.
+ */
+#ifdef CONFIG_XMON
+#define BTEXT __pmac
+#define BTDATA __pmacdata
+#else
+#define BTEXT __init
+#define BTDATA __initdata
+#endif /* CONFIG_XMON */
+
+/*
+ * This is called only when we are booted via BootX.
+ */
+void __init
+btext_init(boot_infos_t *bi)
+{
+ g_loc_X = 0;
+ g_loc_Y = 0;
+ g_max_loc_X = (bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) / 8;
+ g_max_loc_Y = (bi->dispDeviceRect[3] - bi->dispDeviceRect[1]) / 16;
+ disp_bi = *bi;
+ boot_text_mapped = 1;
+}
+
+void __init
+btext_welcome(void)
+{
+ unsigned long flags;
+ unsigned long pvr;
+ boot_infos_t* bi = &disp_bi;
+
+ btext_drawstring("Welcome to Linux, kernel " UTS_RELEASE "\n");
+ btext_drawstring("\nlinked at : 0x");
+ btext_drawhex(KERNELBASE);
+ btext_drawstring("\nframe buffer at : 0x");
+ btext_drawhex((unsigned long)bi->dispDeviceBase);
+ btext_drawstring(" (phys), 0x");
+ btext_drawhex((unsigned long)bi->logicalDisplayBase);
+ btext_drawstring(" (log)");
+ btext_drawstring("\nklimit : 0x");
+ btext_drawhex((unsigned long)klimit);
+ btext_drawstring("\nMSR : 0x");
+ __asm__ __volatile__ ("mfmsr %0" : "=r" (flags));
+ btext_drawhex(flags);
+ __asm__ __volatile__ ("mfspr %0, 287" : "=r" (pvr));
+ pvr >>= 16;
+ if (pvr > 1) {
+ btext_drawstring("\nHID0 : 0x");
+ __asm__ __volatile__ ("mfspr %0, 1008" : "=r" (flags));
+ btext_drawhex(flags);
+ }
+ if (pvr == 8 || pvr == 12 || pvr == 0x800c) {
+ btext_drawstring("\nICTC : 0x");
+ __asm__ __volatile__ ("mfspr %0, 1019" : "=r" (flags));
+ btext_drawhex(flags);
+ }
+ btext_drawstring("\n\n");
+}
+
+/* Calc BAT values for mapping the display and store them
+ * in disp_BAT. Those values are then used from head.S to map
+ * the display during identify_machine() and MMU_Init()
+ *
+ * The display is mapped to virtual address 0xD0000000, rather
+ * than 1:1, because some some CHRP machines put the frame buffer
+ * in the region starting at 0xC0000000 (KERNELBASE).
+ * This mapping is temporary and will disappear as soon as the
+ * setup done by MMU_Init() is applied.
+ *
+ * For now, we align the BAT and then map 8Mb on 601 and 16Mb
+ * on other PPCs. This may cause trouble if the framebuffer
+ * is really badly aligned, but I didn't encounter this case
+ * yet.
+ */
+void __init
+btext_prepare_BAT(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ unsigned long vaddr = KERNELBASE + 0x10000000;
+ unsigned long addr;
+ unsigned long lowbits;
+
+ addr = (unsigned long)bi->dispDeviceBase;
+ if (!addr) {
+ boot_text_mapped = 0;
+ return;
+ }
+ if (PVR_VER(mfspr(SPRN_PVR)) != 1) {
+ /* 603, 604, G3, G4, ... */
+ lowbits = addr & ~0xFF000000UL;
+ addr &= 0xFF000000UL;
+ disp_BAT[0] = vaddr | (BL_16M<<2) | 2;
+ disp_BAT[1] = addr | (_PAGE_NO_CACHE | _PAGE_GUARDED | BPP_RW);
+ } else {
+ /* 601 */
+ lowbits = addr & ~0xFF800000UL;
+ addr &= 0xFF800000UL;
+ disp_BAT[0] = vaddr | (_PAGE_NO_CACHE | PP_RWXX) | 4;
+ disp_BAT[1] = addr | BL_8M | 0x40;
+ }
+ bi->logicalDisplayBase = (void *) (vaddr + lowbits);
+}
+
+/* This function will enable the early boot text when doing OF booting. This
+ * way, xmon output should work too
+ */
+void __init
+btext_setup_display(int width, int height, int depth, int pitch,
+ unsigned long address)
+{
+ boot_infos_t* bi = &disp_bi;
+
+ g_loc_X = 0;
+ g_loc_Y = 0;
+ g_max_loc_X = width / 8;
+ g_max_loc_Y = height / 16;
+ bi->logicalDisplayBase = (unsigned char *)address;
+ bi->dispDeviceBase = (unsigned char *)address;
+ bi->dispDeviceRowBytes = pitch;
+ bi->dispDeviceDepth = depth;
+ bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;
+ bi->dispDeviceRect[2] = width;
+ bi->dispDeviceRect[3] = height;
+ boot_text_mapped = 1;
+}
+
+/* Here's a small text engine to use during early boot
+ * or for debugging purposes
+ *
+ * todo:
+ *
+ * - build some kind of vgacon with it to enable early printk
+ * - move to a separate file
+ * - add a few video driver hooks to keep in sync with display
+ * changes.
+ */
+
+void __openfirmware
+map_boot_text(void)
+{
+ unsigned long base, offset, size;
+ boot_infos_t *bi = &disp_bi;
+ unsigned char *vbase;
+
+ /* By default, we are no longer mapped */
+ boot_text_mapped = 0;
+ if (bi->dispDeviceBase == 0)
+ return;
+ base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL;
+ offset = ((unsigned long) bi->dispDeviceBase) - base;
+ size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset
+ + bi->dispDeviceRect[0];
+ vbase = ioremap(base, size);
+ if (vbase == 0)
+ return;
+ bi->logicalDisplayBase = vbase + offset;
+ boot_text_mapped = 1;
+}
+
+/* Calc the base address of a given point (x,y) */
+static unsigned char * BTEXT
+calc_base(boot_infos_t *bi, int x, int y)
+{
+ unsigned char *base;
+
+ base = bi->logicalDisplayBase;
+ if (base == 0)
+ base = bi->dispDeviceBase;
+ base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3);
+ base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes;
+ return base;
+}
+
+/* Adjust the display to a new resolution */
+void
+btext_update_display(unsigned long phys, int width, int height,
+ int depth, int pitch)
+{
+ boot_infos_t *bi = &disp_bi;
+
+ if (bi->dispDeviceBase == 0)
+ return;
+
+ /* check it's the same frame buffer (within 256MB) */
+ if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000)
+ return;
+
+ bi->dispDeviceBase = (__u8 *) phys;
+ bi->dispDeviceRect[0] = 0;
+ bi->dispDeviceRect[1] = 0;
+ bi->dispDeviceRect[2] = width;
+ bi->dispDeviceRect[3] = height;
+ bi->dispDeviceDepth = depth;
+ bi->dispDeviceRowBytes = pitch;
+ if (boot_text_mapped) {
+ iounmap(bi->logicalDisplayBase);
+ boot_text_mapped = 0;
+ }
+ map_boot_text();
+ g_loc_X = 0;
+ g_loc_Y = 0;
+ g_max_loc_X = width / 8;
+ g_max_loc_Y = height / 16;
+}
+
+void BTEXT btext_clearscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ unsigned long *base = (unsigned long *)calc_base(bi, 0, 0);
+ unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ int i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+ {
+ unsigned long *ptr = base;
+ for(j=width; j; --j)
+ *(ptr++) = 0;
+ base += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+
+__inline__ void dcbst(const void* addr)
+{
+ __asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));
+}
+
+void BTEXT btext_flushscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ unsigned long *base = (unsigned long *)calc_base(bi, 0, 0);
+ unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ int i,j;
+
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
+ {
+ unsigned long *ptr = base;
+ for(j=width; j>0; j-=8) {
+ dcbst(ptr);
+ ptr += 8;
+ }
+ base += (bi->dispDeviceRowBytes >> 2);
+ }
+}
+
+#ifndef NO_SCROLL
+static BTEXT void
+scrollscreen(void)
+{
+ boot_infos_t* bi = &disp_bi;
+ unsigned long *src = (unsigned long *)calc_base(bi,0,16);
+ unsigned long *dst = (unsigned long *)calc_base(bi,0,0);
+ unsigned long width = ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
+ (bi->dispDeviceDepth >> 3)) >> 2;
+ int i,j;
+
+#ifdef CONFIG_ADB_PMU
+ pmu_suspend(); /* PMU will not shut us down ! */
+#endif
+ for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++)
+ {
+ unsigned long *src_ptr = src;
+ unsigned long *dst_ptr = dst;
+ for(j=width; j; --j)
+ *(dst_ptr++) = *(src_ptr++);
+ src += (bi->dispDeviceRowBytes >> 2);
+ dst += (bi->dispDeviceRowBytes >> 2);
+ }
+ for (i=0; i<16; i++)
+ {
+ unsigned long *dst_ptr = dst;
+ for(j=width; j; --j)
+ *(dst_ptr++) = 0;
+ dst += (bi->dispDeviceRowBytes >> 2);
+ }
+#ifdef CONFIG_ADB_PMU
+ pmu_resume(); /* PMU will not shut us down ! */
+#endif
+}
+#endif /* ndef NO_SCROLL */
+
+void BTEXT btext_drawchar(char c)
+{
+ int cline = 0, x;
+
+ if (!boot_text_mapped)
+ return;
+
+ switch (c) {
+ case '\b':
+ if (g_loc_X > 0)
+ --g_loc_X;
+ break;
+ case '\t':
+ g_loc_X = (g_loc_X & -8) + 8;
+ break;
+ case '\r':
+ g_loc_X = 0;
+ break;
+ case '\n':
+ g_loc_X = 0;
+ g_loc_Y++;
+ cline = 1;
+ break;
+ default:
+ draw_byte(c, g_loc_X++, g_loc_Y);
+ }
+ if (g_loc_X >= g_max_loc_X) {
+ g_loc_X = 0;
+ g_loc_Y++;
+ cline = 1;
+ }
+#ifndef NO_SCROLL
+ while (g_loc_Y >= g_max_loc_Y) {
+ scrollscreen();
+ g_loc_Y--;
+ }
+#else
+ /* wrap around from bottom to top of screen so we don't
+ waste time scrolling each line. -- paulus. */
+ if (g_loc_Y >= g_max_loc_Y)
+ g_loc_Y = 0;
+ if (cline) {
+ for (x = 0; x < g_max_loc_X; ++x)
+ draw_byte(' ', x, g_loc_Y);
+ }
+#endif
+}
+
+void BTEXT
+btext_drawstring(const char *c)
+{
+ if (!boot_text_mapped)
+ return;
+ while (*c)
+ btext_drawchar(*c++);
+}
+
+void BTEXT
+btext_drawhex(unsigned long v)
+{
+ static char hex_table[] = "0123456789abcdef";
+
+ if (!boot_text_mapped)
+ return;
+ btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 8) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 4) & 0x0000000FUL]);
+ btext_drawchar(hex_table[(v >> 0) & 0x0000000FUL]);
+ btext_drawchar(' ');
+}
+
+static void BTEXT
+draw_byte(unsigned char c, long locX, long locY)
+{
+ boot_infos_t* bi = &disp_bi;
+ unsigned char *base = calc_base(bi, locX << 3, locY << 4);
+ unsigned char *font = &vga_font[((unsigned long)c) * 16];
+ int rb = bi->dispDeviceRowBytes;
+
+ switch(bi->dispDeviceDepth) {
+ case 24:
+ case 32:
+ draw_byte_32(font, (unsigned long *)base, rb);
+ break;
+ case 15:
+ case 16:
+ draw_byte_16(font, (unsigned long *)base, rb);
+ break;
+ case 8:
+ draw_byte_8(font, (unsigned long *)base, rb);
+ break;
+ }
+}
+
+static unsigned long expand_bits_8[16] BTDATA = {
+ 0x00000000,
+ 0x000000ff,
+ 0x0000ff00,
+ 0x0000ffff,
+ 0x00ff0000,
+ 0x00ff00ff,
+ 0x00ffff00,
+ 0x00ffffff,
+ 0xff000000,
+ 0xff0000ff,
+ 0xff00ff00,
+ 0xff00ffff,
+ 0xffff0000,
+ 0xffff00ff,
+ 0xffffff00,
+ 0xffffffff
+};
+
+static unsigned long expand_bits_16[4] BTDATA = {
+ 0x00000000,
+ 0x0000ffff,
+ 0xffff0000,
+ 0xffffffff
+};
+
+
+static void BTEXT
+draw_byte_32(unsigned char *font, unsigned long *base, int rb)
+{
+ int l, bits;
+ int fg = 0xFFFFFFFFUL;
+ int bg = 0x00000000UL;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (-(bits >> 7) & fg) ^ bg;
+ base[1] = (-((bits >> 6) & 1) & fg) ^ bg;
+ base[2] = (-((bits >> 5) & 1) & fg) ^ bg;
+ base[3] = (-((bits >> 4) & 1) & fg) ^ bg;
+ base[4] = (-((bits >> 3) & 1) & fg) ^ bg;
+ base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
+ base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
+ base[7] = (-(bits & 1) & fg) ^ bg;
+ base = (unsigned long *) ((char *)base + rb);
+ }
+}
+
+static void BTEXT
+draw_byte_16(unsigned char *font, unsigned long *base, int rb)
+{
+ int l, bits;
+ int fg = 0xFFFFFFFFUL;
+ int bg = 0x00000000UL;
+ unsigned long *eb = expand_bits_16;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (eb[bits >> 6] & fg) ^ bg;
+ base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
+ base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
+ base[3] = (eb[bits & 3] & fg) ^ bg;
+ base = (unsigned long *) ((char *)base + rb);
+ }
+}
+
+static void BTEXT
+draw_byte_8(unsigned char *font, unsigned long *base, int rb)
+{
+ int l, bits;
+ int fg = 0x0F0F0F0FUL;
+ int bg = 0x00000000UL;
+ unsigned long *eb = expand_bits_8;
+
+ for (l = 0; l < 16; ++l)
+ {
+ bits = *font++;
+ base[0] = (eb[bits >> 4] & fg) ^ bg;
+ base[1] = (eb[bits & 0xf] & fg) ^ bg;
+ base = (unsigned long *) ((char *)base + rb);
+ }
+}
+
+static unsigned char vga_font[cmapsz] BTDATA = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x81, 0xa5, 0x81, 0x81, 0xbd,
+0x99, 0x81, 0x81, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xff,
+0xdb, 0xff, 0xff, 0xc3, 0xe7, 0xff, 0xff, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x6c, 0xfe, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x7c, 0xfe,
+0x7c, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+0x3c, 0x3c, 0xe7, 0xe7, 0xe7, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x42, 0x42, 0x66, 0x3c, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc3, 0x99, 0xbd,
+0xbd, 0x99, 0xc3, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x1e, 0x0e,
+0x1a, 0x32, 0x78, 0xcc, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3c, 0x66, 0x66, 0x66, 0x66, 0x3c, 0x18, 0x7e, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x33, 0x3f, 0x30, 0x30, 0x30,
+0x30, 0x70, 0xf0, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x63,
+0x7f, 0x63, 0x63, 0x63, 0x63, 0x67, 0xe7, 0xe6, 0xc0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x18, 0x18, 0xdb, 0x3c, 0xe7, 0x3c, 0xdb, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfe, 0xf8,
+0xf0, 0xe0, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x0e,
+0x1e, 0x3e, 0xfe, 0x3e, 0x1e, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66,
+0x66, 0x00, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xdb,
+0xdb, 0xdb, 0x7b, 0x1b, 0x1b, 0x1b, 0x1b, 0x1b, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0xc6, 0x60, 0x38, 0x6c, 0xc6, 0xc6, 0x6c, 0x38, 0x0c, 0xc6,
+0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xfe, 0xfe, 0xfe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c,
+0x7e, 0x18, 0x18, 0x18, 0x7e, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x7e, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x0c, 0xfe, 0x0c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x60, 0xfe, 0x60, 0x30, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0xc0,
+0xc0, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x24, 0x66, 0xff, 0x66, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x38, 0x7c, 0x7c, 0xfe, 0xfe, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x7c, 0x7c,
+0x38, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x66, 0x66, 0x24, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c,
+0x6c, 0xfe, 0x6c, 0x6c, 0x6c, 0xfe, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x7c, 0xc6, 0xc2, 0xc0, 0x7c, 0x06, 0x06, 0x86, 0xc6, 0x7c,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc2, 0xc6, 0x0c, 0x18,
+0x30, 0x60, 0xc6, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c,
+0x6c, 0x38, 0x76, 0xdc, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x30, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x18, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x18,
+0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x3c, 0xff, 0x3c, 0x66, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x02, 0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0x80, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xce, 0xde, 0xf6, 0xe6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x38, 0x78, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0x06, 0x0c, 0x18, 0x30, 0x60, 0xc0, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0x06, 0x06, 0x3c, 0x06, 0x06, 0x06, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x1c, 0x3c, 0x6c, 0xcc, 0xfe,
+0x0c, 0x0c, 0x0c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+0xc0, 0xc0, 0xfc, 0x06, 0x06, 0x06, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x60, 0xc0, 0xc0, 0xfc, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0x06, 0x06, 0x0c, 0x18,
+0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0xc6, 0xc6, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x06, 0x06, 0x0c, 0x78,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00,
+0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x18, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x06,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
+0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60,
+0x30, 0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x60, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0x0c, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xde, 0xde,
+0xde, 0xdc, 0xc0, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38,
+0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x66, 0x66, 0x66, 0x66, 0xfc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0xc2, 0xc0, 0xc0, 0xc0,
+0xc0, 0xc2, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x6c,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x6c, 0xf8, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x66, 0x62, 0x68, 0x78, 0x68,
+0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+0xc2, 0xc0, 0xc0, 0xde, 0xc6, 0xc6, 0x66, 0x3a, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x0c,
+0x0c, 0x0c, 0x0c, 0x0c, 0xcc, 0xcc, 0xcc, 0x78, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xe6, 0x66, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0x66, 0xe6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x60, 0x60, 0x60, 0x60, 0x60,
+0x60, 0x62, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xe7,
+0xff, 0xff, 0xdb, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66,
+0x66, 0x66, 0x7c, 0x60, 0x60, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xd6, 0xde, 0x7c,
+0x0c, 0x0e, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66, 0x66, 0x7c, 0x6c,
+0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6,
+0xc6, 0x60, 0x38, 0x0c, 0x06, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xff, 0xdb, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xc3, 0xc3, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x66,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0x66, 0x3c, 0x18, 0x18,
+0x3c, 0x66, 0xc3, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3,
+0xc3, 0x66, 0x3c, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xff, 0xc3, 0x86, 0x0c, 0x18, 0x30, 0x60, 0xc1, 0xc3, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x30, 0x30, 0x30, 0x30,
+0x30, 0x30, 0x30, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+0xc0, 0xe0, 0x70, 0x38, 0x1c, 0x0e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x3c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
+0x30, 0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x78, 0x6c, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc0, 0xc0, 0xc0, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x0c, 0x0c, 0x3c, 0x6c, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xf0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0xcc, 0x78, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x6c, 0x76, 0x66, 0x66, 0x66, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x18, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x0e, 0x06, 0x06,
+0x06, 0x06, 0x06, 0x06, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0xe0, 0x60,
+0x60, 0x66, 0x6c, 0x78, 0x78, 0x6c, 0x66, 0xe6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe6, 0xff, 0xdb,
+0xdb, 0xdb, 0xdb, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x66, 0x66,
+0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x7c, 0x0c, 0x0c, 0x1e, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x76, 0x66, 0x60, 0x60, 0x60, 0xf0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xc6, 0x60,
+0x38, 0x0c, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x30,
+0x30, 0xfc, 0x30, 0x30, 0x30, 0x30, 0x36, 0x1c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0xc3, 0xc3,
+0xc3, 0x66, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xc3, 0xc3, 0xc3, 0xdb, 0xdb, 0xff, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0x3c, 0x66, 0xc3,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xfe, 0xcc, 0x18, 0x30, 0x60, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x0e, 0x18, 0x18, 0x18, 0x70, 0x18, 0x18, 0x18, 0x18, 0x0e,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x00, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x18,
+0x18, 0x18, 0x0e, 0x18, 0x18, 0x18, 0x18, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0xc6,
+0xc6, 0xc6, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x66,
+0xc2, 0xc0, 0xc0, 0xc0, 0xc2, 0x66, 0x3c, 0x0c, 0x06, 0x7c, 0x00, 0x00,
+0x00, 0x00, 0xcc, 0x00, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x00, 0x7c, 0xc6, 0xfe,
+0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c,
+0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xcc, 0x00, 0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38,
+0x00, 0x78, 0x0c, 0x7c, 0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x3c, 0x66, 0x60, 0x60, 0x66, 0x3c, 0x0c, 0x06,
+0x3c, 0x00, 0x00, 0x00, 0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xfe,
+0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
+0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x7c, 0xc6, 0xfe, 0xc0, 0xc0, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x38, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x3c, 0x66,
+0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x60, 0x30, 0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x10, 0x38, 0x6c, 0xc6, 0xc6,
+0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x38, 0x00,
+0x38, 0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x30, 0x60, 0x00, 0xfe, 0x66, 0x60, 0x7c, 0x60, 0x60, 0x66, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6e, 0x3b, 0x1b,
+0x7e, 0xd8, 0xdc, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x6c,
+0xcc, 0xcc, 0xfe, 0xcc, 0xcc, 0xcc, 0xcc, 0xce, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x10, 0x38, 0x6c, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x7c, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18,
+0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x30, 0x78, 0xcc, 0x00, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x76,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x30, 0x18, 0x00, 0xcc, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00,
+0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7e, 0x06, 0x0c, 0x78, 0x00,
+0x00, 0xc6, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6,
+0xc6, 0xc6, 0xc6, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e,
+0xc3, 0xc0, 0xc0, 0xc0, 0xc3, 0x7e, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x38, 0x6c, 0x64, 0x60, 0xf0, 0x60, 0x60, 0x60, 0x60, 0xe6, 0xfc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc3, 0x66, 0x3c, 0x18, 0xff, 0x18,
+0xff, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x66, 0x66,
+0x7c, 0x62, 0x66, 0x6f, 0x66, 0x66, 0x66, 0xf3, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x0e, 0x1b, 0x18, 0x18, 0x18, 0x7e, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xd8, 0x70, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0x78, 0x0c, 0x7c,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x18, 0x30,
+0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x18, 0x30, 0x60, 0x00, 0x7c, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x30, 0x60, 0x00, 0xcc, 0xcc, 0xcc,
+0xcc, 0xcc, 0xcc, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc,
+0x00, 0xdc, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x00, 0x00, 0x00, 0x00,
+0x76, 0xdc, 0x00, 0xc6, 0xe6, 0xf6, 0xfe, 0xde, 0xce, 0xc6, 0xc6, 0xc6,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x6c, 0x6c, 0x3e, 0x00, 0x7e, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
+0x38, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x30, 0x30, 0x00, 0x30, 0x30, 0x60, 0xc0, 0xc6, 0xc6, 0x7c,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc0,
+0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0xfe, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30, 0x60, 0xce, 0x9b, 0x06,
+0x0c, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xc0, 0xc2, 0xc6, 0xcc, 0x18, 0x30,
+0x66, 0xce, 0x96, 0x3e, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18,
+0x00, 0x18, 0x18, 0x18, 0x3c, 0x3c, 0x3c, 0x18, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x6c, 0xd8, 0x6c, 0x36, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x6c, 0x36,
+0x6c, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x44, 0x11, 0x44,
+0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44, 0x11, 0x44,
+0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa,
+0x55, 0xaa, 0x55, 0xaa, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77,
+0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x18, 0xf8,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf6, 0x06, 0xf6, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x06, 0xf6,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf6, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xfe, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8, 0x18, 0xf8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x37,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x37, 0x30, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xf7, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xff, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x37, 0x30, 0x37, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36,
+0x36, 0xf7, 0x00, 0xf7, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0xff,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x3f,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f,
+0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x36, 0x36, 0x36, 0xff, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36, 0x36,
+0x18, 0x18, 0x18, 0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0xf8,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xf0, 0xf0, 0xf0,
+0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0,
+0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f,
+0x0f, 0x0f, 0x0f, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x76, 0xdc, 0xd8, 0xd8, 0xd8, 0xdc, 0x76, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x78, 0xcc, 0xcc, 0xcc, 0xd8, 0xcc, 0xc6, 0xc6, 0xc6, 0xcc,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0xc6, 0xc6, 0xc0, 0xc0, 0xc0,
+0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0xfe, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0xfe, 0xc6, 0x60, 0x30, 0x18, 0x30, 0x60, 0xc6, 0xfe,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0xd8, 0xd8,
+0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x66, 0x66, 0x66, 0x66, 0x66, 0x7c, 0x60, 0x60, 0xc0, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x18, 0x3c, 0x66, 0x66,
+0x66, 0x3c, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38,
+0x6c, 0xc6, 0xc6, 0xfe, 0xc6, 0xc6, 0x6c, 0x38, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x38, 0x6c, 0xc6, 0xc6, 0xc6, 0x6c, 0x6c, 0x6c, 0x6c, 0xee,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x18, 0x0c, 0x3e, 0x66,
+0x66, 0x66, 0x66, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x7e, 0xdb, 0xdb, 0xdb, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x03, 0x06, 0x7e, 0xdb, 0xdb, 0xf3, 0x7e, 0x60, 0xc0,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x30, 0x60, 0x60, 0x7c, 0x60,
+0x60, 0x60, 0x30, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c,
+0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0xc6, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00, 0x00, 0xfe, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x7e, 0x18,
+0x18, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30,
+0x18, 0x0c, 0x06, 0x0c, 0x18, 0x30, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x0c, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0c, 0x00, 0x7e,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x1b, 0x1b, 0x1b, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+0x18, 0x18, 0x18, 0x18, 0xd8, 0xd8, 0xd8, 0x70, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x7e, 0x00, 0x18, 0x18, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xdc, 0x00,
+0x76, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6c, 0x6c,
+0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0c, 0x0c,
+0x0c, 0x0c, 0x0c, 0xec, 0x6c, 0x6c, 0x3c, 0x1c, 0x00, 0x00, 0x00, 0x00,
+0x00, 0xd8, 0x6c, 0x6c, 0x6c, 0x6c, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xd8, 0x30, 0x60, 0xc8, 0xf8, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00,
+};
diff --git a/arch/ppc/syslib/cpc700.h b/arch/ppc/syslib/cpc700.h
new file mode 100644
index 000000000000..f2c002531019
--- /dev/null
+++ b/arch/ppc/syslib/cpc700.h
@@ -0,0 +1,98 @@
+/*
+ * arch/ppc/syslib/cpc700.h
+ *
+ * Header file for IBM CPC700 Host Bridge, et. al.
+ *
+ * Author: Mark A. Greer
+ * mgreer@mvista.com
+ *
+ * 2000-2002 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+/*
+ * This file contains the defines and macros for the IBM CPC700 host bridge,
+ * memory controller, PIC, UARTs, IIC, and Timers.
+ */
+
+#ifndef __PPC_SYSLIB_CPC700_H__
+#define __PPC_SYSLIB_CPC700_H__
+
+#include <linux/stddef.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+/* XXX no barriers? not even any volatiles? -- paulus */
+#define CPC700_OUT_32(a,d) (*(u_int *)a = d)
+#define CPC700_IN_32(a) (*(u_int *)a)
+
+/*
+ * PCI Section
+ */
+#define CPC700_PCI_CONFIG_ADDR 0xfec00000
+#define CPC700_PCI_CONFIG_DATA 0xfec00004
+
+/* CPU -> PCI memory window 0 */
+#define CPC700_PMM0_LOCAL 0xff400000 /* CPU physical addr */
+#define CPC700_PMM0_MASK_ATTR 0xff400004 /* size and attrs */
+#define CPC700_PMM0_PCI_LOW 0xff400008 /* PCI addr, low word */
+#define CPC700_PMM0_PCI_HIGH 0xff40000c /* PCI addr, high wd */
+/* CPU -> PCI memory window 1 */
+#define CPC700_PMM1_LOCAL 0xff400010
+#define CPC700_PMM1_MASK_ATTR 0xff400014
+#define CPC700_PMM1_PCI_LOW 0xff400018
+#define CPC700_PMM1_PCI_HIGH 0xff40001c
+/* CPU -> PCI memory window 2 */
+#define CPC700_PMM2_LOCAL 0xff400020
+#define CPC700_PMM2_MASK_ATTR 0xff400024
+#define CPC700_PMM2_PCI_LOW 0xff400028
+#define CPC700_PMM2_PCI_HIGH 0xff40002c
+/* PCI memory -> CPU window 1 */
+#define CPC700_PTM1_MEMSIZE 0xff400030 /* window size */
+#define CPC700_PTM1_LOCAL 0xff400034 /* CPU phys addr */
+/* PCI memory -> CPU window 2 */
+#define CPC700_PTM2_MEMSIZE 0xff400038 /* size and enable */
+#define CPC700_PTM2_LOCAL 0xff40003c
+
+/*
+ * PIC Section
+ *
+ * IBM calls the CPC700's programmable interrupt controller the Universal
+ * Interrupt Controller or UIC.
+ */
+
+/*
+ * UIC Register Addresses.
+ */
+#define CPC700_UIC_UICSR 0xff500880 /* Status Reg (Rd/Clr)*/
+#define CPC700_UIC_UICSRS 0xff500884 /* Status Reg (Set) */
+#define CPC700_UIC_UICER 0xff500888 /* Enable Reg */
+#define CPC700_UIC_UICCR 0xff50088c /* Critical Reg */
+#define CPC700_UIC_UICPR 0xff500890 /* Polarity Reg */
+#define CPC700_UIC_UICTR 0xff500894 /* Trigger Reg */
+#define CPC700_UIC_UICMSR 0xff500898 /* Masked Status Reg */
+#define CPC700_UIC_UICVR 0xff50089c /* Vector Reg */
+#define CPC700_UIC_UICVCR 0xff5008a0 /* Vector Config Reg */
+
+#define CPC700_UIC_UICER_ENABLE 0x00000001 /* Enable an IRQ */
+
+#define CPC700_UIC_UICVCR_31_HI 0x00000000 /* IRQ 31 hi priority */
+#define CPC700_UIC_UICVCR_0_HI 0x00000001 /* IRQ 0 hi priority */
+#define CPC700_UIC_UICVCR_BASE_MASK 0xfffffffc
+#define CPC700_UIC_UICVCR_ORDER_MASK 0x00000001
+
+/* Specify value of a bit for an IRQ. */
+#define CPC700_UIC_IRQ_BIT(i) ((0x00000001) << (31 - (i)))
+
+/*
+ * UIC Exports...
+ */
+extern struct hw_interrupt_type cpc700_pic;
+extern unsigned int cpc700_irq_assigns[32][2];
+
+extern void __init cpc700_init_IRQ(void);
+extern int cpc700_get_irq(struct pt_regs *);
+
+#endif /* __PPC_SYSLIB_CPC700_H__ */
diff --git a/arch/ppc/syslib/cpc700_pic.c b/arch/ppc/syslib/cpc700_pic.c
new file mode 100644
index 000000000000..774709807538
--- /dev/null
+++ b/arch/ppc/syslib/cpc700_pic.c
@@ -0,0 +1,187 @@
+/*
+ * arch/ppc/syslib/cpc700_pic.c
+ *
+ * Interrupt controller support for IBM Spruce
+ *
+ * Authors: Mark Greer, Matt Porter, and Johnnie Peters
+ * mgreer@mvista.com
+ * mporter@mvista.com
+ * jpeters@mvista.com
+ *
+ * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+
+#include "cpc700.h"
+
+static void
+cpc700_unmask_irq(unsigned int irq)
+{
+ unsigned int tr_bits;
+
+ /*
+ * IRQ 31 is largest IRQ supported.
+ * IRQs 17-19 are reserved.
+ */
+ if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
+ tr_bits = CPC700_IN_32(CPC700_UIC_UICTR);
+
+ if ((tr_bits & (1 << (31 - irq))) == 0) {
+ /* level trigger interrupt, clear bit in status
+ * register */
+ CPC700_OUT_32(CPC700_UIC_UICSR, 1 << (31 - irq));
+ }
+
+ /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
+ ppc_cached_irq_mask[0] |= CPC700_UIC_IRQ_BIT(irq);
+
+ CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
+ }
+ return;
+}
+
+static void
+cpc700_mask_irq(unsigned int irq)
+{
+ /*
+ * IRQ 31 is largest IRQ supported.
+ * IRQs 17-19 are reserved.
+ */
+ if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
+ /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
+ ppc_cached_irq_mask[0] &=
+ ~CPC700_UIC_IRQ_BIT(irq);
+
+ CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
+ }
+ return;
+}
+
+static void
+cpc700_mask_and_ack_irq(unsigned int irq)
+{
+ u_int bit;
+
+ /*
+ * IRQ 31 is largest IRQ supported.
+ * IRQs 17-19 are reserved.
+ */
+ if ((irq <= 31) && ((irq < 17) || (irq > 19))) {
+ /* Know IRQ fits in entry 0 of ppc_cached_irq_mask[] */
+ bit = CPC700_UIC_IRQ_BIT(irq);
+
+ ppc_cached_irq_mask[0] &= ~bit;
+ CPC700_OUT_32(CPC700_UIC_UICER, ppc_cached_irq_mask[0]);
+ CPC700_OUT_32(CPC700_UIC_UICSR, bit); /* Write 1 clears IRQ */
+ }
+ return;
+}
+
+static struct hw_interrupt_type cpc700_pic = {
+ "CPC700 PIC",
+ NULL,
+ NULL,
+ cpc700_unmask_irq,
+ cpc700_mask_irq,
+ cpc700_mask_and_ack_irq,
+ NULL,
+ NULL
+};
+
+__init static void
+cpc700_pic_init_irq(unsigned int irq)
+{
+ unsigned int tmp;
+
+ /* Set interrupt sense */
+ tmp = CPC700_IN_32(CPC700_UIC_UICTR);
+ if (cpc700_irq_assigns[irq][0] == 0) {
+ tmp &= ~CPC700_UIC_IRQ_BIT(irq);
+ } else {
+ tmp |= CPC700_UIC_IRQ_BIT(irq);
+ }
+ CPC700_OUT_32(CPC700_UIC_UICTR, tmp);
+
+ /* Set interrupt polarity */
+ tmp = CPC700_IN_32(CPC700_UIC_UICPR);
+ if (cpc700_irq_assigns[irq][1]) {
+ tmp |= CPC700_UIC_IRQ_BIT(irq);
+ } else {
+ tmp &= ~CPC700_UIC_IRQ_BIT(irq);
+ }
+ CPC700_OUT_32(CPC700_UIC_UICPR, tmp);
+
+ /* Set interrupt critical */
+ tmp = CPC700_IN_32(CPC700_UIC_UICCR);
+ tmp |= CPC700_UIC_IRQ_BIT(irq);
+ CPC700_OUT_32(CPC700_UIC_UICCR, tmp);
+
+ return;
+}
+
+__init void
+cpc700_init_IRQ(void)
+{
+ int i;
+
+ ppc_cached_irq_mask[0] = 0;
+ CPC700_OUT_32(CPC700_UIC_UICER, 0x00000000); /* Disable all irq's */
+ CPC700_OUT_32(CPC700_UIC_UICSR, 0xffffffff); /* Clear cur intrs */
+ CPC700_OUT_32(CPC700_UIC_UICCR, 0xffffffff); /* Gen INT not MCP */
+ CPC700_OUT_32(CPC700_UIC_UICPR, 0x00000000); /* Active low */
+ CPC700_OUT_32(CPC700_UIC_UICTR, 0x00000000); /* Level Sensitive */
+ CPC700_OUT_32(CPC700_UIC_UICVR, CPC700_UIC_UICVCR_0_HI);
+ /* IRQ 0 is highest */
+
+ for (i = 0; i < 17; i++) {
+ irq_desc[i].handler = &cpc700_pic;
+ cpc700_pic_init_irq(i);
+ }
+
+ for (i = 20; i < 32; i++) {
+ irq_desc[i].handler = &cpc700_pic;
+ cpc700_pic_init_irq(i);
+ }
+
+ return;
+}
+
+
+
+/*
+ * Find the highest IRQ that generating an interrupt, if any.
+ */
+int
+cpc700_get_irq(struct pt_regs *regs)
+{
+ int irq = 0;
+ u_int irq_status, irq_test = 1;
+
+ irq_status = CPC700_IN_32(CPC700_UIC_UICMSR);
+
+ do
+ {
+ if (irq_status & irq_test)
+ break;
+ irq++;
+ irq_test <<= 1;
+ } while (irq < NR_IRQS);
+
+
+ if (irq == NR_IRQS)
+ irq = 33;
+
+ return (31 - irq);
+}
diff --git a/arch/ppc/syslib/cpc710.h b/arch/ppc/syslib/cpc710.h
new file mode 100644
index 000000000000..cc0afd804029
--- /dev/null
+++ b/arch/ppc/syslib/cpc710.h
@@ -0,0 +1,83 @@
+/*
+ * arch/ppc/syslib/cpc710.h
+ *
+ * Definitions for the IBM CPC710 PCI Host Bridge
+ *
+ * Author: Matt Porter <mporter@mvista.com>
+ *
+ * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#ifndef __PPC_PLATFORMS_CPC710_H
+#define __PPC_PLATFORMS_CPC710_H
+
+/* General bridge and memory controller registers */
+#define PIDR 0xff000008
+#define CNFR 0xff00000c
+#define RSTR 0xff000010
+#define UCTL 0xff001000
+#define MPSR 0xff001010
+#define SIOC 0xff001020
+#define ABCNTL 0xff001030
+#define SRST 0xff001040
+#define ERRC 0xff001050
+#define SESR 0xff001060
+#define SEAR 0xff001070
+#define SIOC1 0xff001090
+#define PGCHP 0xff001100
+#define GPDIR 0xff001130
+#define GPOUT 0xff001150
+#define ATAS 0xff001160
+#define AVDG 0xff001170
+#define MCCR 0xff001200
+#define MESR 0xff001220
+#define MEAR 0xff001230
+#define MCER0 0xff001300
+#define MCER1 0xff001310
+#define MCER2 0xff001320
+#define MCER3 0xff001330
+#define MCER4 0xff001340
+#define MCER5 0xff001350
+#define MCER6 0xff001360
+#define MCER7 0xff001370
+
+/*
+ * PCI32/64 configuration registers
+ * Given as offsets from their
+ * respective physical segment BAR
+ */
+#define PIBAR 0x000f7800
+#define PMBAR 0x000f7810
+#define MSIZE 0x000f7f40
+#define IOSIZE 0x000f7f60
+#define SMBAR 0x000f7f80
+#define SIBAR 0x000f7fc0
+#define PSSIZE 0x000f8100
+#define PPSIZE 0x000f8110
+#define BARPS 0x000f8120
+#define BARPP 0x000f8130
+#define PSBAR 0x000f8140
+#define PPBAR 0x000f8150
+#define BPMDLK 0x000f8200 /* Bottom of Peripheral Memory Space */
+#define TPMDLK 0x000f8210 /* Top of Peripheral Memory Space */
+#define BIODLK 0x000f8220 /* Bottom of Peripheral I/O Space */
+#define TIODLK 0x000f8230 /* Top of Perioheral I/O Space */
+#define DLKCTRL 0x000f8240 /* Deadlock control */
+#define DLKDEV 0x000f8250 /* Deadlock device */
+
+/* System standard configuration registers space */
+#define DCR 0xff200000
+#define DID 0xff200004
+#define BAR 0xff200018
+
+/* Device specific configuration space */
+#define PCIENB 0xff201000
+
+/* Configuration space registers */
+#define CPC710_BUS_NUMBER 0x40
+#define CPC710_SUB_BUS_NUMBER 0x41
+
+#endif /* __PPC_PLATFORMS_CPC710_H */
diff --git a/arch/ppc/syslib/cpm2_common.c b/arch/ppc/syslib/cpm2_common.c
new file mode 100644
index 000000000000..ea5e77080e8d
--- /dev/null
+++ b/arch/ppc/syslib/cpm2_common.c
@@ -0,0 +1,198 @@
+/*
+ * General Purpose functions for the global management of the
+ * 8260 Communication Processor Module.
+ * Copyright (c) 1999 Dan Malek (dmalek@jlc.net)
+ * Copyright (c) 2000 MontaVista Software, Inc (source@mvista.com)
+ * 2.3.99 Updates
+ *
+ * In addition to the individual control of the communication
+ * channels, there are a few functions that globally affect the
+ * communication processor.
+ *
+ * Buffer descriptors must be allocated from the dual ported memory
+ * space. The allocator for that is here. When the communication
+ * process is reset, we reclaim the memory available. There is
+ * currently no deallocator for this memory.
+ */
+#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/interrupt.h>
+#include <linux/bootmem.h>
+#include <linux/module.h>
+#include <asm/irq.h>
+#include <asm/mpc8260.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/immap_cpm2.h>
+#include <asm/cpm2.h>
+#include <asm/rheap.h>
+
+static void cpm2_dpinit(void);
+cpm_cpm2_t *cpmp; /* Pointer to comm processor space */
+
+/* We allocate this here because it is used almost exclusively for
+ * the communication processor devices.
+ */
+cpm2_map_t *cpm2_immr;
+
+#define CPM_MAP_SIZE (0x40000) /* 256k - the PQ3 reserve this amount
+ of space for CPM as it is larger
+ than on PQ2 */
+
+void
+cpm2_reset(void)
+{
+ cpm2_immr = (cpm2_map_t *)ioremap(CPM_MAP_ADDR, CPM_MAP_SIZE);
+
+ /* Reclaim the DP memory for our use.
+ */
+ cpm2_dpinit();
+
+ /* Tell everyone where the comm processor resides.
+ */
+ cpmp = &cpm2_immr->im_cpm;
+}
+
+/* Set a baud rate generator. This needs lots of work. There are
+ * eight BRGs, which can be connected to the CPM channels or output
+ * as clocks. The BRGs are in two different block of internal
+ * memory mapped space.
+ * The baud rate clock is the system clock divided by something.
+ * It was set up long ago during the initial boot phase and is
+ * is given to us.
+ * Baud rate clocks are zero-based in the driver code (as that maps
+ * to port numbers). Documentation uses 1-based numbering.
+ */
+#define BRG_INT_CLK (((bd_t *)__res)->bi_brgfreq)
+#define BRG_UART_CLK (BRG_INT_CLK/16)
+
+/* This function is used by UARTS, or anything else that uses a 16x
+ * oversampled clock.
+ */
+void
+cpm_setbrg(uint brg, uint rate)
+{
+ volatile uint *bp;
+
+ /* This is good enough to get SMCs running.....
+ */
+ if (brg < 4) {
+ bp = (uint *)&cpm2_immr->im_brgc1;
+ }
+ else {
+ bp = (uint *)&cpm2_immr->im_brgc5;
+ brg -= 4;
+ }
+ bp += brg;
+ *bp = ((BRG_UART_CLK / rate) << 1) | CPM_BRG_EN;
+}
+
+/* This function is used to set high speed synchronous baud rate
+ * clocks.
+ */
+void
+cpm2_fastbrg(uint brg, uint rate, int div16)
+{
+ volatile uint *bp;
+
+ if (brg < 4) {
+ bp = (uint *)&cpm2_immr->im_brgc1;
+ }
+ else {
+ bp = (uint *)&cpm2_immr->im_brgc5;
+ brg -= 4;
+ }
+ bp += brg;
+ *bp = ((BRG_INT_CLK / rate) << 1) | CPM_BRG_EN;
+ if (div16)
+ *bp |= CPM_BRG_DIV16;
+}
+
+/*
+ * dpalloc / dpfree bits.
+ */
+static spinlock_t cpm_dpmem_lock;
+/* 16 blocks should be enough to satisfy all requests
+ * until the memory subsystem goes up... */
+static rh_block_t cpm_boot_dpmem_rh_block[16];
+static rh_info_t cpm_dpmem_info;
+
+static void cpm2_dpinit(void)
+{
+ spin_lock_init(&cpm_dpmem_lock);
+
+ /* initialize the info header */
+ rh_init(&cpm_dpmem_info, 1,
+ sizeof(cpm_boot_dpmem_rh_block) /
+ sizeof(cpm_boot_dpmem_rh_block[0]),
+ cpm_boot_dpmem_rh_block);
+
+ /* Attach the usable dpmem area */
+ /* XXX: This is actually crap. CPM_DATAONLY_BASE and
+ * CPM_DATAONLY_SIZE is only a subset of the available dpram. It
+ * varies with the processor and the microcode patches activated.
+ * But the following should be at least safe.
+ */
+ rh_attach_region(&cpm_dpmem_info, (void *)CPM_DATAONLY_BASE,
+ CPM_DATAONLY_SIZE);
+}
+
+/* This function returns an index into the DPRAM area.
+ */
+uint cpm_dpalloc(uint size, uint align)
+{
+ void *start;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpm_dpmem_lock, flags);
+ cpm_dpmem_info.alignment = align;
+ start = rh_alloc(&cpm_dpmem_info, size, "commproc");
+ spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
+
+ return (uint)start;
+}
+EXPORT_SYMBOL(cpm_dpalloc);
+
+int cpm_dpfree(uint offset)
+{
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpm_dpmem_lock, flags);
+ ret = rh_free(&cpm_dpmem_info, (void *)offset);
+ spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL(cpm_dpfree);
+
+/* not sure if this is ever needed */
+uint cpm_dpalloc_fixed(uint offset, uint size, uint align)
+{
+ void *start;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cpm_dpmem_lock, flags);
+ cpm_dpmem_info.alignment = align;
+ start = rh_alloc_fixed(&cpm_dpmem_info, (void *)offset, size, "commproc");
+ spin_unlock_irqrestore(&cpm_dpmem_lock, flags);
+
+ return (uint)start;
+}
+EXPORT_SYMBOL(cpm_dpalloc_fixed);
+
+void cpm_dpdump(void)
+{
+ rh_dump(&cpm_dpmem_info);
+}
+EXPORT_SYMBOL(cpm_dpdump);
+
+void *cpm_dpram_addr(uint offset)
+{
+ return (void *)&cpm2_immr->im_dprambase[offset];
+}
+EXPORT_SYMBOL(cpm_dpram_addr);
diff --git a/arch/ppc/syslib/cpm2_pic.c b/arch/ppc/syslib/cpm2_pic.c
new file mode 100644
index 000000000000..954b07fc1df3
--- /dev/null
+++ b/arch/ppc/syslib/cpm2_pic.c
@@ -0,0 +1,172 @@
+/* The CPM2 internal interrupt controller. It is usually
+ * the only interrupt controller.
+ * There are two 32-bit registers (high/low) for up to 64
+ * possible interrupts.
+ *
+ * Now, the fun starts.....Interrupt Numbers DO NOT MAP
+ * in a simple arithmetic fashion to mask or pending registers.
+ * That is, interrupt 4 does not map to bit position 4.
+ * We create two tables, indexed by vector number, to indicate
+ * which register to use and which bit in the register to use.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/irq.h>
+
+#include <asm/immap_cpm2.h>
+#include <asm/mpc8260.h>
+
+#include "cpm2_pic.h"
+
+static u_char irq_to_siureg[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* bit numbers do not match the docs, these are precomputed so the bit for
+ * a given irq is (1 << irq_to_siubit[irq]) */
+static u_char irq_to_siubit[] = {
+ 0, 15, 14, 13, 12, 11, 10, 9,
+ 8, 7, 6, 5, 4, 3, 2, 1,
+ 2, 1, 15, 14, 13, 12, 11, 10,
+ 9, 8, 7, 6, 5, 4, 3, 0,
+ 31, 30, 29, 28, 27, 26, 25, 24,
+ 23, 22, 21, 20, 19, 18, 17, 16,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+};
+
+static void cpm2_mask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ irq_nr -= CPM_IRQ_OFFSET;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ ppc_cached_irq_mask[word] &= ~(1 << bit);
+ simr[word] = ppc_cached_irq_mask[word];
+}
+
+static void cpm2_unmask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ irq_nr -= CPM_IRQ_OFFSET;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ ppc_cached_irq_mask[word] |= 1 << bit;
+ simr[word] = ppc_cached_irq_mask[word];
+}
+
+static void cpm2_mask_and_ack(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr, *sipnr;
+
+ irq_nr -= CPM_IRQ_OFFSET;
+
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ sipnr = &(cpm2_immr->im_intctl.ic_sipnrh);
+ ppc_cached_irq_mask[word] &= ~(1 << bit);
+ simr[word] = ppc_cached_irq_mask[word];
+ sipnr[word] = 1 << bit;
+}
+
+static void cpm2_end_irq(unsigned int irq_nr)
+{
+ int bit, word;
+ volatile uint *simr;
+
+ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
+ && irq_desc[irq_nr].action) {
+
+ irq_nr -= CPM_IRQ_OFFSET;
+ bit = irq_to_siubit[irq_nr];
+ word = irq_to_siureg[irq_nr];
+
+ simr = &(cpm2_immr->im_intctl.ic_simrh);
+ ppc_cached_irq_mask[word] |= 1 << bit;
+ simr[word] = ppc_cached_irq_mask[word];
+ }
+}
+
+static struct hw_interrupt_type cpm2_pic = {
+ .typename = " CPM2 SIU ",
+ .enable = cpm2_unmask_irq,
+ .disable = cpm2_mask_irq,
+ .ack = cpm2_mask_and_ack,
+ .end = cpm2_end_irq,
+};
+
+int cpm2_get_irq(struct pt_regs *regs)
+{
+ int irq;
+ unsigned long bits;
+
+ /* For CPM2, read the SIVEC register and shift the bits down
+ * to get the irq number. */
+ bits = cpm2_immr->im_intctl.ic_sivec;
+ irq = bits >> 26;
+
+ if (irq == 0)
+ return(-1);
+ return irq+CPM_IRQ_OFFSET;
+}
+
+void cpm2_init_IRQ(void)
+{
+ int i;
+
+ /* Clear the CPM IRQ controller, in case it has any bits set
+ * from the bootloader
+ */
+
+ /* Mask out everything */
+ cpm2_immr->im_intctl.ic_simrh = 0x00000000;
+ cpm2_immr->im_intctl.ic_simrl = 0x00000000;
+ wmb();
+
+ /* Ack everything */
+ cpm2_immr->im_intctl.ic_sipnrh = 0xffffffff;
+ cpm2_immr->im_intctl.ic_sipnrl = 0xffffffff;
+ wmb();
+
+ /* Dummy read of the vector */
+ i = cpm2_immr->im_intctl.ic_sivec;
+ rmb();
+
+ /* Initialize the default interrupt mapping priorities,
+ * in case the boot rom changed something on us.
+ */
+ cpm2_immr->im_intctl.ic_sicr = 0;
+ cpm2_immr->im_intctl.ic_scprrh = 0x05309770;
+ cpm2_immr->im_intctl.ic_scprrl = 0x05309770;
+
+
+ /* Enable chaining to OpenPIC, and make everything level
+ */
+ for (i = 0; i < NR_CPM_INTS; i++) {
+ irq_desc[i+CPM_IRQ_OFFSET].handler = &cpm2_pic;
+ irq_desc[i+CPM_IRQ_OFFSET].status |= IRQ_LEVEL;
+ }
+}
diff --git a/arch/ppc/syslib/cpm2_pic.h b/arch/ppc/syslib/cpm2_pic.h
new file mode 100644
index 000000000000..97cab8f13a1a
--- /dev/null
+++ b/arch/ppc/syslib/cpm2_pic.h
@@ -0,0 +1,8 @@
+#ifndef _PPC_KERNEL_CPM2_H
+#define _PPC_KERNEL_CPM2_H
+
+extern int cpm2_get_irq(struct pt_regs *regs);
+
+extern void cpm2_init_IRQ(void);
+
+#endif /* _PPC_KERNEL_CPM2_H */
diff --git a/arch/ppc/syslib/dcr.S b/arch/ppc/syslib/dcr.S
new file mode 100644
index 000000000000..895f10243a43
--- /dev/null
+++ b/arch/ppc/syslib/dcr.S
@@ -0,0 +1,41 @@
+/*
+ * arch/ppc/syslib/dcr.S
+ *
+ * "Indirect" DCR access
+ *
+ * Copyright (c) 2004 Eugene Surovegin <ebs@ebshome.net>
+ *
+ * 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 <asm/ppc_asm.h>
+#include <asm/processor.h>
+
+#define DCR_ACCESS_PROLOG(table) \
+ rlwinm r3,r3,4,18,27; \
+ lis r5,table@h; \
+ ori r5,r5,table@l; \
+ add r3,r3,r5; \
+ mtctr r3; \
+ bctr
+
+_GLOBAL(__mfdcr)
+ DCR_ACCESS_PROLOG(__mfdcr_table)
+
+_GLOBAL(__mtdcr)
+ DCR_ACCESS_PROLOG(__mtdcr_table)
+
+__mfdcr_table:
+ mfdcr r3,0; blr
+__mtdcr_table:
+ mtdcr 0,r4; blr
+
+dcr = 1
+ .rept 1023
+ mfdcr r3,dcr; blr
+ mtdcr dcr,r4; blr
+ dcr = dcr + 1
+ .endr
diff --git a/arch/ppc/syslib/gen550.h b/arch/ppc/syslib/gen550.h
new file mode 100644
index 000000000000..039d249e19a8
--- /dev/null
+++ b/arch/ppc/syslib/gen550.h
@@ -0,0 +1,16 @@
+/*
+ * arch/ppc/syslib/gen550.h
+ *
+ * gen550 prototypes
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+extern void gen550_progress(char *, unsigned short);
+extern void gen550_init(int, struct uart_port *);
+extern void gen550_kgdb_map_scc(void);
diff --git a/arch/ppc/syslib/gen550_dbg.c b/arch/ppc/syslib/gen550_dbg.c
new file mode 100644
index 000000000000..9ef0113c83d1
--- /dev/null
+++ b/arch/ppc/syslib/gen550_dbg.c
@@ -0,0 +1,182 @@
+/*
+ * arch/ppc/syslib/gen550_dbg.c
+ *
+ * A library of polled 16550 serial routines. These are intended to
+ * be used to support progress messages, xmon, kgdb, etc. on a
+ * variety of platforms.
+ *
+ * Adapted from lots of code ripped from the arch/ppc/boot/ polled
+ * 16550 support.
+ *
+ * Author: Matt Porter <mporter@mvista.com>
+ *
+ * 2002-2003 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/tty.h> /* For linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/serialP.h>
+#include <linux/serial_reg.h>
+#include <asm/machdep.h>
+#include <asm/serial.h>
+#include <asm/io.h>
+
+#define SERIAL_BAUD 9600
+
+/* SERIAL_PORT_DFNS is defined in <asm/serial.h> */
+#ifndef SERIAL_PORT_DFNS
+#define SERIAL_PORT_DFNS
+#endif
+
+static struct serial_state rs_table[RS_TABLE_SIZE] = {
+ SERIAL_PORT_DFNS /* defined in <asm/serial.h> */
+};
+
+static void (*serial_outb)(unsigned long, unsigned char);
+static unsigned long (*serial_inb)(unsigned long);
+
+static int shift;
+
+unsigned long direct_inb(unsigned long addr)
+{
+ return readb((void __iomem *)addr);
+}
+
+void direct_outb(unsigned long addr, unsigned char val)
+{
+ writeb(val, (void __iomem *)addr);
+}
+
+unsigned long io_inb(unsigned long port)
+{
+ return inb(port);
+}
+
+void io_outb(unsigned long port, unsigned char val)
+{
+ outb(val, port);
+}
+
+unsigned long serial_init(int chan, void *ignored)
+{
+ unsigned long com_port;
+ unsigned char lcr, dlm;
+
+ /* We need to find out which type io we're expecting. If it's
+ * 'SERIAL_IO_PORT', we get an offset from the isa_io_base.
+ * If it's 'SERIAL_IO_MEM', we can the exact location. -- Tom */
+ switch (rs_table[chan].io_type) {
+ case SERIAL_IO_PORT:
+ com_port = rs_table[chan].port;
+ serial_outb = io_outb;
+ serial_inb = io_inb;
+ break;
+ case SERIAL_IO_MEM:
+ com_port = (unsigned long)rs_table[chan].iomem_base;
+ serial_outb = direct_outb;
+ serial_inb = direct_inb;
+ break;
+ default:
+ /* We can't deal with it. */
+ return -1;
+ }
+
+ /* How far apart the registers are. */
+ shift = rs_table[chan].iomem_reg_shift;
+
+ /* save the LCR */
+ lcr = serial_inb(com_port + (UART_LCR << shift));
+
+ /* Access baud rate */
+ serial_outb(com_port + (UART_LCR << shift), UART_LCR_DLAB);
+ dlm = serial_inb(com_port + (UART_DLM << shift));
+
+ /*
+ * Test if serial port is unconfigured
+ * We assume that no-one uses less than 110 baud or
+ * less than 7 bits per character these days.
+ * -- paulus.
+ */
+ if ((dlm <= 4) && (lcr & 2)) {
+ /* port is configured, put the old LCR back */
+ serial_outb(com_port + (UART_LCR << shift), lcr);
+ }
+ else {
+ /* Input clock. */
+ serial_outb(com_port + (UART_DLL << shift),
+ (rs_table[chan].baud_base / SERIAL_BAUD) & 0xFF);
+ serial_outb(com_port + (UART_DLM << shift),
+ (rs_table[chan].baud_base / SERIAL_BAUD) >> 8);
+ /* 8 data, 1 stop, no parity */
+ serial_outb(com_port + (UART_LCR << shift), 0x03);
+ /* RTS/DTR */
+ serial_outb(com_port + (UART_MCR << shift), 0x03);
+
+ /* Clear & enable FIFOs */
+ serial_outb(com_port + (UART_FCR << shift), 0x07);
+ }
+
+ return (com_port);
+}
+
+void
+serial_putc(unsigned long com_port, unsigned char c)
+{
+ while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_THRE) == 0)
+ ;
+ serial_outb(com_port, c);
+}
+
+unsigned char
+serial_getc(unsigned long com_port)
+{
+ while ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) == 0)
+ ;
+ return serial_inb(com_port);
+}
+
+int
+serial_tstc(unsigned long com_port)
+{
+ return ((serial_inb(com_port + (UART_LSR << shift)) & UART_LSR_DR) != 0);
+}
+
+void
+serial_close(unsigned long com_port)
+{
+}
+
+void
+gen550_init(int i, struct uart_port *serial_req)
+{
+ rs_table[i].io_type = serial_req->iotype;
+ rs_table[i].port = serial_req->iobase;
+ rs_table[i].iomem_base = serial_req->membase;
+ rs_table[i].iomem_reg_shift = serial_req->regshift;
+ rs_table[i].baud_base = serial_req->uartclk ? serial_req->uartclk / 16 : BASE_BAUD;
+}
+
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+void
+gen550_progress(char *s, unsigned short hex)
+{
+ volatile unsigned int progress_debugport;
+ volatile char c;
+
+ progress_debugport = serial_init(0, NULL);
+
+ serial_putc(progress_debugport, '\r');
+
+ while ((c = *s++) != 0)
+ serial_putc(progress_debugport, c);
+
+ serial_putc(progress_debugport, '\n');
+ serial_putc(progress_debugport, '\r');
+}
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
diff --git a/arch/ppc/syslib/gen550_kgdb.c b/arch/ppc/syslib/gen550_kgdb.c
new file mode 100644
index 000000000000..7239d5d7ddcd
--- /dev/null
+++ b/arch/ppc/syslib/gen550_kgdb.c
@@ -0,0 +1,86 @@
+/*
+ * arch/ppc/syslib/gen550_kgdb.c
+ *
+ * Generic 16550 kgdb support intended to be useful on a variety
+ * of platforms. To enable this support, it is necessary to set
+ * the CONFIG_GEN550 option. Any virtual mapping of the serial
+ * port(s) to be used can be accomplished by setting
+ * ppc_md.early_serial_map to a platform-specific mapping function.
+ *
+ * Adapted from ppc4xx_kgdb.c.
+ *
+ * Author: Matt Porter <mporter@kernel.crashing.org>
+ *
+ * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <asm/machdep.h>
+
+extern unsigned long serial_init(int, void *);
+extern unsigned long serial_getc(unsigned long);
+extern unsigned long serial_putc(unsigned long, unsigned char);
+
+#if defined(CONFIG_KGDB_TTYS0)
+#define KGDB_PORT 0
+#elif defined(CONFIG_KGDB_TTYS1)
+#define KGDB_PORT 1
+#elif defined(CONFIG_KGDB_TTYS2)
+#define KGDB_PORT 2
+#elif defined(CONFIG_KGDB_TTYS3)
+#define KGDB_PORT 3
+#else
+#error "invalid kgdb_tty port"
+#endif
+
+static volatile unsigned int kgdb_debugport;
+
+void putDebugChar(unsigned char c)
+{
+ if (kgdb_debugport == 0)
+ kgdb_debugport = serial_init(KGDB_PORT, NULL);
+
+ serial_putc(kgdb_debugport, c);
+}
+
+int getDebugChar(void)
+{
+ if (kgdb_debugport == 0)
+ kgdb_debugport = serial_init(KGDB_PORT, NULL);
+
+ return(serial_getc(kgdb_debugport));
+}
+
+void kgdb_interruptible(int enable)
+{
+ return;
+}
+
+void putDebugString(char* str)
+{
+ while (*str != '\0') {
+ putDebugChar(*str);
+ str++;
+ }
+ putDebugChar('\r');
+ return;
+}
+
+/*
+ * Note: gen550_init() must be called already on the port we are going
+ * to use.
+ */
+void
+gen550_kgdb_map_scc(void)
+{
+ printk(KERN_DEBUG "kgdb init\n");
+ if (ppc_md.early_serial_map)
+ ppc_md.early_serial_map();
+ kgdb_debugport = serial_init(KGDB_PORT, NULL);
+}
diff --git a/arch/ppc/syslib/gt64260_pic.c b/arch/ppc/syslib/gt64260_pic.c
new file mode 100644
index 000000000000..44aa87385451
--- /dev/null
+++ b/arch/ppc/syslib/gt64260_pic.c
@@ -0,0 +1,328 @@
+/*
+ * arch/ppc/syslib/gt64260_pic.c
+ *
+ * Interrupt controller support for Galileo's GT64260.
+ *
+ * Author: Chris Zankel <source@mvista.com>
+ * Modified by: Mark A. Greer <mgreer@mvista.com>
+ *
+ * Based on sources from Rabeeh Khoury / Galileo Technology
+ *
+ * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+/*
+ * This file contains the specific functions to support the GT64260
+ * interrupt controller.
+ *
+ * The GT64260 has two main interrupt registers (high and low) that
+ * summarizes the interrupts generated by the units of the GT64260.
+ * Each bit is assigned to an interrupt number, where the low register
+ * are assigned from IRQ0 to IRQ31 and the high cause register
+ * from IRQ32 to IRQ63
+ * The GPP (General Purpose Port) interrupts are assigned from IRQ64 (GPP0)
+ * to IRQ95 (GPP31).
+ * get_irq() returns the lowest interrupt number that is currently asserted.
+ *
+ * Note:
+ * - This driver does not initialize the GPP when used as an interrupt
+ * input.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/mv64x60.h>
+
+#define CPU_INTR_STR "gt64260 cpu interface error"
+#define PCI0_INTR_STR "gt64260 pci 0 error"
+#define PCI1_INTR_STR "gt64260 pci 1 error"
+
+/* ========================== forward declaration ========================== */
+
+static void gt64260_unmask_irq(unsigned int);
+static void gt64260_mask_irq(unsigned int);
+
+/* ========================== local declarations =========================== */
+
+struct hw_interrupt_type gt64260_pic = {
+ .typename = " gt64260_pic ",
+ .enable = gt64260_unmask_irq,
+ .disable = gt64260_mask_irq,
+ .ack = gt64260_mask_irq,
+ .end = gt64260_unmask_irq,
+};
+
+u32 gt64260_irq_base = 0; /* GT64260 handles the next 96 IRQs from here */
+
+static struct mv64x60_handle bh;
+
+/* gt64260_init_irq()
+ *
+ * This function initializes the interrupt controller. It assigns
+ * all interrupts from IRQ0 to IRQ95 to the gt64260 interrupt controller.
+ *
+ * Note:
+ * We register all GPP inputs as interrupt source, but disable them.
+ */
+void __init
+gt64260_init_irq(void)
+{
+ int i;
+
+ if (ppc_md.progress)
+ ppc_md.progress("gt64260_init_irq: enter", 0x0);
+
+ bh.v_base = mv64x60_get_bridge_vbase();
+
+ ppc_cached_irq_mask[0] = 0;
+ ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */
+ ppc_cached_irq_mask[2] = 0;
+
+ /* disable all interrupts and clear current interrupts */
+ mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]);
+ mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0);
+ mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO, ppc_cached_irq_mask[0]);
+ mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI, ppc_cached_irq_mask[1]);
+
+ /* use the gt64260 for all (possible) interrupt sources */
+ for (i = gt64260_irq_base; i < (gt64260_irq_base + 96); i++)
+ irq_desc[i].handler = &gt64260_pic;
+
+ if (ppc_md.progress)
+ ppc_md.progress("gt64260_init_irq: exit", 0x0);
+}
+
+/*
+ * gt64260_get_irq()
+ *
+ * This function returns the lowest interrupt number of all interrupts that
+ * are currently asserted.
+ *
+ * Input Variable(s):
+ * struct pt_regs* not used
+ *
+ * Output Variable(s):
+ * None.
+ *
+ * Returns:
+ * int <interrupt number> or -2 (bogus interrupt)
+ */
+int
+gt64260_get_irq(struct pt_regs *regs)
+{
+ int irq;
+ int irq_gpp;
+
+ irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_LO);
+ irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]);
+
+ if (irq == -1) {
+ irq = mv64x60_read(&bh, GT64260_IC_MAIN_CAUSE_HI);
+ irq = __ilog2((irq & 0x0f000db7) & ppc_cached_irq_mask[1]);
+
+ if (irq == -1)
+ irq = -2; /* bogus interrupt, should never happen */
+ else {
+ if (irq >= 24) {
+ irq_gpp = mv64x60_read(&bh,
+ MV64x60_GPP_INTR_CAUSE);
+ irq_gpp = __ilog2(irq_gpp &
+ ppc_cached_irq_mask[2]);
+
+ if (irq_gpp == -1)
+ irq = -2;
+ else {
+ irq = irq_gpp + 64;
+ mv64x60_write(&bh,
+ MV64x60_GPP_INTR_CAUSE,
+ ~(1 << (irq - 64)));
+ }
+ } else
+ irq += 32;
+ }
+ }
+
+ (void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE);
+
+ if (irq < 0)
+ return (irq);
+ else
+ return (gt64260_irq_base + irq);
+}
+
+/* gt64260_unmask_irq()
+ *
+ * This function enables an interrupt.
+ *
+ * Input Variable(s):
+ * unsigned int interrupt number (IRQ0...IRQ95).
+ *
+ * Output Variable(s):
+ * None.
+ *
+ * Returns:
+ * void
+ */
+static void
+gt64260_unmask_irq(unsigned int irq)
+{
+ irq -= gt64260_irq_base;
+
+ if (irq > 31)
+ if (irq > 63) /* unmask GPP irq */
+ mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
+ ppc_cached_irq_mask[2] |= (1 << (irq - 64)));
+ else /* mask high interrupt register */
+ mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI,
+ ppc_cached_irq_mask[1] |= (1 << (irq - 32)));
+ else /* mask low interrupt register */
+ mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO,
+ ppc_cached_irq_mask[0] |= (1 << irq));
+
+ (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
+ return;
+}
+
+/* gt64260_mask_irq()
+ *
+ * This function disables the requested interrupt.
+ *
+ * Input Variable(s):
+ * unsigned int interrupt number (IRQ0...IRQ95).
+ *
+ * Output Variable(s):
+ * None.
+ *
+ * Returns:
+ * void
+ */
+static void
+gt64260_mask_irq(unsigned int irq)
+{
+ irq -= gt64260_irq_base;
+
+ if (irq > 31)
+ if (irq > 63) /* mask GPP irq */
+ mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
+ ppc_cached_irq_mask[2] &= ~(1 << (irq - 64)));
+ else /* mask high interrupt register */
+ mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_HI,
+ ppc_cached_irq_mask[1] &= ~(1 << (irq - 32)));
+ else /* mask low interrupt register */
+ mv64x60_write(&bh, GT64260_IC_CPU_INTR_MASK_LO,
+ ppc_cached_irq_mask[0] &= ~(1 << irq));
+
+ (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
+ return;
+}
+
+static irqreturn_t
+gt64260_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk(KERN_ERR "gt64260_cpu_error_int_handler: %s 0x%08x\n",
+ "Error on CPU interface - Cause regiser",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE));
+ printk(KERN_ERR "\tCPU error register dump:\n");
+ printk(KERN_ERR "\tAddress low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO));
+ printk(KERN_ERR "\tAddress high 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI));
+ printk(KERN_ERR "\tData low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO));
+ printk(KERN_ERR "\tData high 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI));
+ printk(KERN_ERR "\tParity 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY));
+ mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+gt64260_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 val;
+ unsigned int pci_bus = (unsigned int)dev_id;
+
+ if (pci_bus == 0) { /* Error on PCI 0 */
+ val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE);
+ printk(KERN_ERR "%s: Error in PCI %d Interface\n",
+ "gt64260_pci_error_int_handler", pci_bus);
+ printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
+ printk(KERN_ERR "\tCause register 0x%08x\n", val);
+ printk(KERN_ERR "\tAddress Low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO));
+ printk(KERN_ERR "\tAddress High 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI));
+ printk(KERN_ERR "\tAttribute 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO));
+ printk(KERN_ERR "\tCommand 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD));
+ mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val);
+ }
+ if (pci_bus == 1) { /* Error on PCI 1 */
+ val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE);
+ printk(KERN_ERR "%s: Error in PCI %d Interface\n",
+ "gt64260_pci_error_int_handler", pci_bus);
+ printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
+ printk(KERN_ERR "\tCause register 0x%08x\n", val);
+ printk(KERN_ERR "\tAddress Low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO));
+ printk(KERN_ERR "\tAddress High 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI));
+ printk(KERN_ERR "\tAttribute 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO));
+ printk(KERN_ERR "\tCommand 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD));
+ mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val);
+ }
+ return IRQ_HANDLED;
+}
+
+static int __init
+gt64260_register_hdlrs(void)
+{
+ int rc;
+
+ /* Register CPU interface error interrupt handler */
+ if ((rc = request_irq(MV64x60_IRQ_CPU_ERR,
+ gt64260_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0)))
+ printk(KERN_WARNING "Can't register cpu error handler: %d", rc);
+
+ mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0);
+ mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000fe);
+
+ /* Register PCI 0 error interrupt handler */
+ if ((rc = request_irq(MV64360_IRQ_PCI0, gt64260_pci_error_int_handler,
+ SA_INTERRUPT, PCI0_INTR_STR, (void *)0)))
+ printk(KERN_WARNING "Can't register pci 0 error handler: %d",
+ rc);
+
+ mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0);
+ mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0x003c0c24);
+
+ /* Register PCI 1 error interrupt handler */
+ if ((rc = request_irq(MV64360_IRQ_PCI1, gt64260_pci_error_int_handler,
+ SA_INTERRUPT, PCI1_INTR_STR, (void *)1)))
+ printk(KERN_WARNING "Can't register pci 1 error handler: %d",
+ rc);
+
+ mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0);
+ mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0x003c0c24);
+
+ return 0;
+}
+
+arch_initcall(gt64260_register_hdlrs);
diff --git a/arch/ppc/syslib/harrier.c b/arch/ppc/syslib/harrier.c
new file mode 100644
index 000000000000..a6b3f8645793
--- /dev/null
+++ b/arch/ppc/syslib/harrier.c
@@ -0,0 +1,302 @@
+/*
+ * arch/ppc/syslib/harrier.c
+ *
+ * Motorola MCG Harrier northbridge/memory controller support
+ *
+ * Author: Dale Farnsworth
+ * dale.farnsworth@mvista.com
+ *
+ * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/harrier_defs.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pci.h>
+#include <asm/pci-bridge.h>
+#include <asm/open_pic.h>
+#include <asm/harrier.h>
+
+/* define defaults for inbound windows */
+#define HARRIER_ITAT_DEFAULT (HARRIER_ITAT_ENA | \
+ HARRIER_ITAT_MEM | \
+ HARRIER_ITAT_WPE | \
+ HARRIER_ITAT_GBL)
+
+#define HARRIER_MPAT_DEFAULT (HARRIER_ITAT_ENA | \
+ HARRIER_ITAT_MEM | \
+ HARRIER_ITAT_WPE | \
+ HARRIER_ITAT_GBL)
+
+/*
+ * Initialize the inbound window size on a non-monarch harrier.
+ */
+void __init harrier_setup_nonmonarch(uint ppc_reg_base, uint in0_size)
+{
+ u16 temps;
+ u32 temp;
+
+ if (in0_size > HARRIER_ITSZ_2GB) {
+ printk
+ ("harrier_setup_nonmonarch: Invalid window size code %d\n",
+ in0_size);
+ return;
+ }
+
+ /* Clear the PCI memory enable bit. If we don't, then when the
+ * inbound windows are enabled below, the corresponding BARs will be
+ * "live" and start answering to PCI memory reads from their default
+ * addresses (0x0), which overlap with system RAM.
+ */
+ temps = in_le16((u16 *) (ppc_reg_base +
+ HARRIER_XCSR_CONFIG(PCI_COMMAND)));
+ temps &= ~(PCI_COMMAND_MEMORY);
+ out_le16((u16 *) (ppc_reg_base + HARRIER_XCSR_CONFIG(PCI_COMMAND)),
+ temps);
+
+ /* Setup a non-prefetchable inbound window */
+ out_le32((u32 *) (ppc_reg_base +
+ HARRIER_XCSR_CONFIG(HARRIER_ITSZ0_OFF)), in0_size);
+
+ temp = in_le32((u32 *) (ppc_reg_base +
+ HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF)));
+ temp &= ~HARRIER_ITAT_PRE;
+ temp |= HARRIER_ITAT_DEFAULT;
+ out_le32((u32 *) (ppc_reg_base +
+ HARRIER_XCSR_CONFIG(HARRIER_ITAT0_OFF)), temp);
+
+ /* Enable the message passing block */
+ temp = in_le32((u32 *) (ppc_reg_base +
+ HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF)));
+ temp |= HARRIER_MPAT_DEFAULT;
+ out_le32((u32 *) (ppc_reg_base +
+ HARRIER_XCSR_CONFIG(HARRIER_MPAT_OFF)), temp);
+}
+
+void __init harrier_release_eready(uint ppc_reg_base)
+{
+ ulong temp;
+
+ /*
+ * Set EREADY to allow the line to be pulled up after everyone is
+ * ready.
+ */
+ temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF));
+ temp |= HARRIER_EREADY;
+ out_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF), temp);
+}
+
+void __init harrier_wait_eready(uint ppc_reg_base)
+{
+ ulong temp;
+
+ /*
+ * Poll the ERDYS line until it goes high to indicate that all
+ * non-monarch PrPMCs are ready for bus enumeration (or that there are
+ * no PrPMCs present).
+ */
+
+ /* FIXME: Add a timeout of some kind to prevent endless waits. */
+ do {
+
+ temp = in_be32((uint *) (ppc_reg_base + HARRIER_MISC_CSR_OFF));
+
+ } while (!(temp & HARRIER_ERDYS));
+}
+
+/*
+ * Initialize the Motorola MCG Harrier host bridge.
+ *
+ * This means setting up the PPC bus to PCI memory and I/O space mappings,
+ * setting the PCI memory space address of the MPIC (mapped straight
+ * through), and ioremap'ing the mpic registers.
+ * 'OpenPIC_Addr' will be set correctly by this routine.
+ * This routine will not change the PCI_CONFIG_ADDR or PCI_CONFIG_DATA
+ * addresses and assumes that the mapping of PCI memory space back to system
+ * memory is set up correctly by PPCBug.
+ */
+int __init
+harrier_init(struct pci_controller *hose,
+ uint ppc_reg_base,
+ ulong processor_pci_mem_start,
+ ulong processor_pci_mem_end,
+ ulong processor_pci_io_start,
+ ulong processor_pci_io_end, ulong processor_mpic_base)
+{
+ uint addr, offset;
+
+ /*
+ * Some sanity checks...
+ */
+ if (((processor_pci_mem_start & 0xffff0000) != processor_pci_mem_start)
+ || ((processor_pci_io_start & 0xffff0000) !=
+ processor_pci_io_start)) {
+ printk("harrier_init: %s\n",
+ "PPC to PCI mappings must start on 64 KB boundaries");
+ return -1;
+ }
+
+ if (((processor_pci_mem_end & 0x0000ffff) != 0x0000ffff) ||
+ ((processor_pci_io_end & 0x0000ffff) != 0x0000ffff)) {
+ printk("harrier_init: PPC to PCI mappings %s\n",
+ "must end just before a 64 KB boundaries");
+ return -1;
+ }
+
+ if (((processor_pci_mem_end - processor_pci_mem_start) !=
+ (hose->mem_space.end - hose->mem_space.start)) ||
+ ((processor_pci_io_end - processor_pci_io_start) !=
+ (hose->io_space.end - hose->io_space.start))) {
+ printk("harrier_init: %s\n",
+ "PPC and PCI memory or I/O space sizes don't match");
+ return -1;
+ }
+
+ if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) {
+ printk("harrier_init: %s\n",
+ "MPIC address must start on 256 KB boundary");
+ return -1;
+ }
+
+ if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) {
+ printk("harrier_init: %s\n",
+ "pci_dram_offset must be multiple of 64 KB");
+ return -1;
+ }
+
+ /*
+ * Program the OTAD/OTOF registers to set up the PCI Mem & I/O
+ * space mappings. These are the mappings going from the processor to
+ * the PCI bus.
+ *
+ * Note: Don't need to 'AND' start/end addresses with 0xffff0000
+ * because sanity check above ensures that they are properly
+ * aligned.
+ */
+
+ /* Set up PPC->PCI Mem mapping */
+ addr = processor_pci_mem_start | (processor_pci_mem_end >> 16);
+#ifdef CONFIG_HARRIER_STORE_GATHERING
+ offset = (hose->mem_space.start - processor_pci_mem_start) | 0x9a;
+#else
+ offset = (hose->mem_space.start - processor_pci_mem_start) | 0x92;
+#endif
+ out_be32((uint *) (ppc_reg_base + HARRIER_OTAD0_OFF), addr);
+ out_be32((uint *) (ppc_reg_base + HARRIER_OTOF0_OFF), offset);
+
+ /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */
+ addr = processor_pci_io_start | (processor_pci_io_end >> 16);
+ offset = (hose->io_space.start - processor_pci_io_start) | 0x80;
+ out_be32((uint *) (ppc_reg_base + HARRIER_OTAD1_OFF), addr);
+ out_be32((uint *) (ppc_reg_base + HARRIER_OTOF1_OFF), offset);
+
+ /* Enable MPIC */
+ OpenPIC_Addr = (void *)processor_mpic_base;
+ addr = (processor_mpic_base >> 16) | 1;
+ out_be16((ushort *) (ppc_reg_base + HARRIER_MBAR_OFF), addr);
+ out_8((u_char *) (ppc_reg_base + HARRIER_MPIC_CSR_OFF),
+ HARRIER_MPIC_OPI_ENABLE);
+
+ return 0;
+}
+
+/*
+ * Find the amount of RAM present.
+ * This assumes that PPCBug has initialized the memory controller (SMC)
+ * on the Harrier correctly (i.e., it does no sanity checking).
+ * It also assumes that the memory base registers are set to configure the
+ * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc.
+ * however, RAM base registers can be skipped (e.g. A, B, C are set,
+ * D is skipped but E is set is okay).
+ */
+#define MB (1024*1024UL)
+
+static uint harrier_size_table[] __initdata = {
+ 0 * MB, /* 0 ==> 0 MB */
+ 32 * MB, /* 1 ==> 32 MB */
+ 64 * MB, /* 2 ==> 64 MB */
+ 64 * MB, /* 3 ==> 64 MB */
+ 128 * MB, /* 4 ==> 128 MB */
+ 128 * MB, /* 5 ==> 128 MB */
+ 128 * MB, /* 6 ==> 128 MB */
+ 256 * MB, /* 7 ==> 256 MB */
+ 256 * MB, /* 8 ==> 256 MB */
+ 256 * MB, /* 9 ==> 256 MB */
+ 512 * MB, /* a ==> 512 MB */
+ 512 * MB, /* b ==> 512 MB */
+ 512 * MB, /* c ==> 512 MB */
+ 1024 * MB, /* d ==> 1024 MB */
+ 1024 * MB, /* e ==> 1024 MB */
+ 2048 * MB, /* f ==> 2048 MB */
+};
+
+/*
+ * *** WARNING: You MUST have a BAT set up to map in the XCSR regs ***
+ *
+ * Read the memory controller's registers to determine the amount of system
+ * memory. Assumes that the memory controller registers are already mapped
+ * into virtual memory--too early to use ioremap().
+ */
+unsigned long __init harrier_get_mem_size(uint xcsr_base)
+{
+ ulong last_addr;
+ int i;
+ uint vend_dev_id;
+ uint *size_table;
+ uint val;
+ uint *csrp;
+ uint size;
+ int size_table_entries;
+
+ vend_dev_id = in_be32((uint *) xcsr_base + PCI_VENDOR_ID);
+
+ if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) {
+ printk("harrier_get_mem_size: %s (0x%x)\n",
+ "Not a Motorola Memory Controller", vend_dev_id);
+ return 0;
+ }
+
+ vend_dev_id &= 0x0000ffff;
+
+ if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HARRIER) {
+ size_table = harrier_size_table;
+ size_table_entries = sizeof(harrier_size_table) /
+ sizeof(harrier_size_table[0]);
+ } else {
+ printk("harrier_get_mem_size: %s (0x%x)\n",
+ "Not a Harrier", vend_dev_id);
+ return 0;
+ }
+
+ last_addr = 0;
+
+ csrp = (uint *) (xcsr_base + HARRIER_SDBA_OFF);
+ for (i = 0; i < 8; i++) {
+ val = in_be32(csrp++);
+
+ if (val & 0x100) { /* If enabled */
+ size = val >> HARRIER_SDB_SIZE_SHIFT;
+ size &= HARRIER_SDB_SIZE_MASK;
+ if (size >= size_table_entries) {
+ break; /* Register not set correctly */
+ }
+ size = size_table[size];
+
+ val &= ~(size - 1);
+ val += size;
+
+ if (val > last_addr) {
+ last_addr = val;
+ }
+ }
+ }
+
+ return last_addr;
+}
diff --git a/arch/ppc/syslib/hawk_common.c b/arch/ppc/syslib/hawk_common.c
new file mode 100644
index 000000000000..a9911dc3a82f
--- /dev/null
+++ b/arch/ppc/syslib/hawk_common.c
@@ -0,0 +1,319 @@
+/*
+ * arch/ppc/syslib/hawk_common.c
+ *
+ * Common Motorola PowerPlus Platform--really Falcon/Raven or HAWK.
+ *
+ * Author: Mark A. Greer
+ * mgreer@mvista.com
+ *
+ * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pci.h>
+#include <asm/pci-bridge.h>
+#include <asm/open_pic.h>
+#include <asm/hawk.h>
+
+/*
+ * The Falcon/Raven and HAWK has 4 sets of registers:
+ * 1) PPC Registers which define the mappings from PPC bus to PCI bus,
+ * etc.
+ * 2) PCI Registers which define the mappings from PCI bus to PPC bus and the
+ * MPIC base address.
+ * 3) MPIC registers.
+ * 4) System Memory Controller (SMC) registers.
+ */
+
+/*
+ * Initialize the Motorola MCG Raven or HAWK host bridge.
+ *
+ * This means setting up the PPC bus to PCI memory and I/O space mappings,
+ * setting the PCI memory space address of the MPIC (mapped straight
+ * through), and ioremap'ing the mpic registers.
+ * This routine will set the PCI_CONFIG_ADDR or PCI_CONFIG_DATA
+ * addresses based on the PCI I/O address that is passed in.
+ * 'OpenPIC_Addr' will be set correctly by this routine.
+ */
+int __init
+hawk_init(struct pci_controller *hose,
+ uint ppc_reg_base,
+ ulong processor_pci_mem_start,
+ ulong processor_pci_mem_end,
+ ulong processor_pci_io_start,
+ ulong processor_pci_io_end,
+ ulong processor_mpic_base)
+{
+ uint addr, offset;
+
+ /*
+ * Some sanity checks...
+ */
+ if (((processor_pci_mem_start&0xffff0000) != processor_pci_mem_start) ||
+ ((processor_pci_io_start &0xffff0000) != processor_pci_io_start)) {
+ printk("hawk_init: %s\n",
+ "PPC to PCI mappings must start on 64 KB boundaries");
+ return -1;
+ }
+
+ if (((processor_pci_mem_end &0x0000ffff) != 0x0000ffff) ||
+ ((processor_pci_io_end &0x0000ffff) != 0x0000ffff)) {
+ printk("hawk_init: PPC to PCI mappings %s\n",
+ "must end just before a 64 KB boundaries");
+ return -1;
+ }
+
+ if (((processor_pci_mem_end - processor_pci_mem_start) !=
+ (hose->mem_space.end - hose->mem_space.start)) ||
+ ((processor_pci_io_end - processor_pci_io_start) !=
+ (hose->io_space.end - hose->io_space.start))) {
+ printk("hawk_init: %s\n",
+ "PPC and PCI memory or I/O space sizes don't match");
+ return -1;
+ }
+
+ if ((processor_mpic_base & 0xfffc0000) != processor_mpic_base) {
+ printk("hawk_init: %s\n",
+ "MPIC address must start on 256 MB boundary");
+ return -1;
+ }
+
+ if ((pci_dram_offset & 0xffff0000) != pci_dram_offset) {
+ printk("hawk_init: %s\n",
+ "pci_dram_offset must be multiple of 64 KB");
+ return -1;
+ }
+
+ /*
+ * Disable previous PPC->PCI mappings.
+ */
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), 0x00000000);
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), 0x00000000);
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF2_OFF), 0x00000000);
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), 0x00000000);
+
+ /*
+ * Program the XSADD/XSOFF registers to set up the PCI Mem & I/O
+ * space mappings. These are the mappings going from the processor to
+ * the PCI bus.
+ *
+ * Note: Don't need to 'AND' start/end addresses with 0xffff0000
+ * because sanity check above ensures that they are properly
+ * aligned.
+ */
+
+ /* Set up PPC->PCI Mem mapping */
+ addr = processor_pci_mem_start | (processor_pci_mem_end >> 16);
+ offset = (hose->mem_space.start - processor_pci_mem_start) | 0xd2;
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD0_OFF), addr);
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF0_OFF), offset);
+
+ /* Set up PPC->MPIC mapping on the bridge */
+ addr = processor_mpic_base |
+ (((processor_mpic_base + HAWK_MPIC_SIZE) >> 16) - 1);
+ /* No write posting for this PCI Mem space */
+ offset = (hose->mem_space.start - processor_pci_mem_start) | 0xc2;
+
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD1_OFF), addr);
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF1_OFF), offset);
+
+ /* Set up PPC->PCI I/O mapping -- Contiguous I/O space */
+ addr = processor_pci_io_start | (processor_pci_io_end >> 16);
+ offset = (hose->io_space.start - processor_pci_io_start) | 0xc0;
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSADD3_OFF), addr);
+ out_be32((uint *)(ppc_reg_base + HAWK_PPC_XSOFF3_OFF), offset);
+
+ hose->io_base_virt = (void *)ioremap(processor_pci_io_start,
+ (processor_pci_io_end - processor_pci_io_start + 1));
+
+ /*
+ * Set up the indirect method of accessing PCI config space.
+ * The PCI config addr/data pair based on start addr of PCI I/O space.
+ */
+ setup_indirect_pci(hose,
+ processor_pci_io_start + HAWK_PCI_CONFIG_ADDR_OFF,
+ processor_pci_io_start + HAWK_PCI_CONFIG_DATA_OFF);
+
+ /*
+ * Disable previous PCI->PPC mappings.
+ */
+
+ /* XXXX Put in mappings from PCI bus to processor bus XXXX */
+
+ /*
+ * Disable MPIC response to PCI I/O space (BAR 0).
+ * Make MPIC respond to PCI Mem space at specified address.
+ * (BAR 1).
+ */
+ early_write_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ PCI_BASE_ADDRESS_0,
+ 0x00000000 | 0x1);
+
+ early_write_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ PCI_BASE_ADDRESS_1,
+ (processor_mpic_base -
+ processor_pci_mem_start +
+ hose->mem_space.start) | 0x0);
+
+ /* Map MPIC into vitual memory */
+ OpenPIC_Addr = ioremap(processor_mpic_base, HAWK_MPIC_SIZE);
+
+ return 0;
+}
+
+/*
+ * Find the amount of RAM present.
+ * This assumes that PPCBug has initialized the memory controller (SMC)
+ * on the Falcon/HAWK correctly (i.e., it does no sanity checking).
+ * It also assumes that the memory base registers are set to configure the
+ * memory as contigous starting with "RAM A BASE", "RAM B BASE", etc.
+ * however, RAM base registers can be skipped (e.g. A, B, C are set,
+ * D is skipped but E is set is okay).
+ */
+#define MB (1024*1024)
+
+static uint reg_offset_table[] __initdata = {
+ HAWK_SMC_RAM_A_SIZE_REG_OFF,
+ HAWK_SMC_RAM_B_SIZE_REG_OFF,
+ HAWK_SMC_RAM_C_SIZE_REG_OFF,
+ HAWK_SMC_RAM_D_SIZE_REG_OFF,
+ HAWK_SMC_RAM_E_SIZE_REG_OFF,
+ HAWK_SMC_RAM_F_SIZE_REG_OFF,
+ HAWK_SMC_RAM_G_SIZE_REG_OFF,
+ HAWK_SMC_RAM_H_SIZE_REG_OFF
+};
+
+static uint falcon_size_table[] __initdata = {
+ 0 * MB, /* 0 ==> 0 MB */
+ 16 * MB, /* 1 ==> 16 MB */
+ 32 * MB, /* 2 ==> 32 MB */
+ 64 * MB, /* 3 ==> 64 MB */
+ 128 * MB, /* 4 ==> 128 MB */
+ 256 * MB, /* 5 ==> 256 MB */
+ 1024 * MB, /* 6 ==> 1024 MB (1 GB) */
+};
+
+static uint hawk_size_table[] __initdata = {
+ 0 * MB, /* 0 ==> 0 MB */
+ 32 * MB, /* 1 ==> 32 MB */
+ 64 * MB, /* 2 ==> 64 MB */
+ 64 * MB, /* 3 ==> 64 MB */
+ 128 * MB, /* 4 ==> 128 MB */
+ 128 * MB, /* 5 ==> 128 MB */
+ 128 * MB, /* 6 ==> 128 MB */
+ 256 * MB, /* 7 ==> 256 MB */
+ 256 * MB, /* 8 ==> 256 MB */
+ 512 * MB, /* 9 ==> 512 MB */
+};
+
+/*
+ * *** WARNING: You MUST have a BAT set up to map in the SMC regs ***
+ *
+ * Read the memory controller's registers to determine the amount of system
+ * memory. Assumes that the memory controller registers are already mapped
+ * into virtual memory--too early to use ioremap().
+ */
+unsigned long __init
+hawk_get_mem_size(uint smc_base)
+{
+ unsigned long total;
+ int i, size_table_entries, reg_limit;
+ uint vend_dev_id;
+ uint *size_table;
+ u_char val;
+
+
+ vend_dev_id = in_be32((uint *)smc_base + PCI_VENDOR_ID);
+
+ if (((vend_dev_id & 0xffff0000) >> 16) != PCI_VENDOR_ID_MOTOROLA) {
+ printk("hawk_get_mem_size: %s (0x%x)\n",
+ "Not a Motorola Memory Controller", vend_dev_id);
+ return 0;
+ }
+
+ vend_dev_id &= 0x0000ffff;
+
+ if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_FALCON) {
+ size_table = falcon_size_table;
+ size_table_entries = sizeof(falcon_size_table) /
+ sizeof(falcon_size_table[0]);
+
+ reg_limit = FALCON_SMC_REG_COUNT;
+ }
+ else if (vend_dev_id == PCI_DEVICE_ID_MOTOROLA_HAWK) {
+ size_table = hawk_size_table;
+ size_table_entries = sizeof(hawk_size_table) /
+ sizeof(hawk_size_table[0]);
+ reg_limit = HAWK_SMC_REG_COUNT;
+ }
+ else {
+ printk("hawk_get_mem_size: %s (0x%x)\n",
+ "Not a Falcon or HAWK", vend_dev_id);
+ return 0;
+ }
+
+ total = 0;
+
+ /* Check every reg because PPCBug may skip some */
+ for (i=0; i<reg_limit; i++) {
+ val = in_8((u_char *)(smc_base + reg_offset_table[i]));
+
+ if (val & 0x80) { /* If enabled */
+ val &= 0x0f;
+
+ /* Don't go past end of size_table */
+ if (val < size_table_entries) {
+ total += size_table[val];
+ }
+ else { /* Register not set correctly */
+ break;
+ }
+ }
+ }
+
+ return total;
+}
+
+int __init
+hawk_mpic_init(unsigned int pci_mem_offset)
+{
+ unsigned short devid;
+ unsigned int pci_membase;
+
+ /* Check the first PCI device to see if it is a Raven or Hawk. */
+ early_read_config_word(0, 0, 0, PCI_DEVICE_ID, &devid);
+
+ switch (devid) {
+ case PCI_DEVICE_ID_MOTOROLA_RAVEN:
+ case PCI_DEVICE_ID_MOTOROLA_HAWK:
+ break;
+ default:
+ OpenPIC_Addr = NULL;
+ return 1;
+ }
+
+ /* Read the memory base register. */
+ early_read_config_dword(0, 0, 0, PCI_BASE_ADDRESS_1, &pci_membase);
+
+ if (pci_membase == 0) {
+ OpenPIC_Addr = NULL;
+ return 1;
+ }
+
+ /* Map the MPIC registers to virtual memory. */
+ OpenPIC_Addr = ioremap(pci_membase + pci_mem_offset, 0x22000);
+
+ return 0;
+}
diff --git a/arch/ppc/syslib/i8259.c b/arch/ppc/syslib/i8259.c
new file mode 100644
index 000000000000..b9391e650141
--- /dev/null
+++ b/arch/ppc/syslib/i8259.c
@@ -0,0 +1,211 @@
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/i8259.h>
+
+static volatile unsigned char *pci_intack; /* RO, gives us the irq vector */
+
+unsigned char cached_8259[2] = { 0xff, 0xff };
+#define cached_A1 (cached_8259[0])
+#define cached_21 (cached_8259[1])
+
+static DEFINE_SPINLOCK(i8259_lock);
+
+int i8259_pic_irq_offset;
+
+/*
+ * Acknowledge the IRQ using either the PCI host bridge's interrupt
+ * acknowledge feature or poll. How i8259_init() is called determines
+ * which is called. It should be noted that polling is broken on some
+ * IBM and Motorola PReP boxes so we must use the int-ack feature on them.
+ */
+int
+i8259_irq(struct pt_regs *regs)
+{
+ int irq;
+
+ spin_lock(&i8259_lock);
+
+ /* Either int-ack or poll for the IRQ */
+ if (pci_intack)
+ irq = *pci_intack;
+ else {
+ /* Perform an interrupt acknowledge cycle on controller 1. */
+ outb(0x0C, 0x20); /* prepare for poll */
+ irq = inb(0x20) & 7;
+ if (irq == 2 ) {
+ /*
+ * Interrupt is cascaded so perform interrupt
+ * acknowledge on controller 2.
+ */
+ outb(0x0C, 0xA0); /* prepare for poll */
+ irq = (inb(0xA0) & 7) + 8;
+ }
+ }
+
+ if (irq == 7) {
+ /*
+ * This may be a spurious interrupt.
+ *
+ * Read the interrupt status register (ISR). If the most
+ * significant bit is not set then there is no valid
+ * interrupt.
+ */
+ if (!pci_intack)
+ outb(0x0B, 0x20); /* ISR register */
+ if(~inb(0x20) & 0x80)
+ irq = -1;
+ }
+
+ spin_unlock(&i8259_lock);
+ return irq;
+}
+
+static void i8259_mask_and_ack_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8259_lock, flags);
+ if ( irq_nr >= i8259_pic_irq_offset )
+ irq_nr -= i8259_pic_irq_offset;
+
+ if (irq_nr > 7) {
+ cached_A1 |= 1 << (irq_nr-8);
+ inb(0xA1); /* DUMMY */
+ outb(cached_A1,0xA1);
+ outb(0x20,0xA0); /* Non-specific EOI */
+ outb(0x20,0x20); /* Non-specific EOI to cascade */
+ } else {
+ cached_21 |= 1 << irq_nr;
+ inb(0x21); /* DUMMY */
+ outb(cached_21,0x21);
+ outb(0x20,0x20); /* Non-specific EOI */
+ }
+ spin_unlock_irqrestore(&i8259_lock, flags);
+}
+
+static void i8259_set_irq_mask(int irq_nr)
+{
+ outb(cached_A1,0xA1);
+ outb(cached_21,0x21);
+}
+
+static void i8259_mask_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8259_lock, flags);
+ if ( irq_nr >= i8259_pic_irq_offset )
+ irq_nr -= i8259_pic_irq_offset;
+ if ( irq_nr < 8 )
+ cached_21 |= 1 << irq_nr;
+ else
+ cached_A1 |= 1 << (irq_nr-8);
+ i8259_set_irq_mask(irq_nr);
+ spin_unlock_irqrestore(&i8259_lock, flags);
+}
+
+static void i8259_unmask_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8259_lock, flags);
+ if ( irq_nr >= i8259_pic_irq_offset )
+ irq_nr -= i8259_pic_irq_offset;
+ if ( irq_nr < 8 )
+ cached_21 &= ~(1 << irq_nr);
+ else
+ cached_A1 &= ~(1 << (irq_nr-8));
+ i8259_set_irq_mask(irq_nr);
+ spin_unlock_irqrestore(&i8259_lock, flags);
+}
+
+static void i8259_end_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))
+ && irq_desc[irq].action)
+ i8259_unmask_irq(irq);
+}
+
+struct hw_interrupt_type i8259_pic = {
+ " i8259 ",
+ NULL,
+ NULL,
+ i8259_unmask_irq,
+ i8259_mask_irq,
+ i8259_mask_and_ack_irq,
+ i8259_end_irq,
+ NULL
+};
+
+static struct resource pic1_iores = {
+ .name = "8259 (master)",
+ .start = 0x20,
+ .end = 0x21,
+ .flags = IORESOURCE_BUSY,
+};
+
+static struct resource pic2_iores = {
+ .name = "8259 (slave)",
+ .start = 0xa0,
+ .end = 0xa1,
+ .flags = IORESOURCE_BUSY,
+};
+
+static struct resource pic_edgectrl_iores = {
+ .name = "8259 edge control",
+ .start = 0x4d0,
+ .end = 0x4d1,
+ .flags = IORESOURCE_BUSY,
+};
+
+static struct irqaction i8259_irqaction = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .mask = CPU_MASK_NONE,
+ .name = "82c59 secondary cascade",
+};
+
+/*
+ * i8259_init()
+ * intack_addr - PCI interrupt acknowledge (real) address which will return
+ * the active irq from the 8259
+ */
+void __init
+i8259_init(long intack_addr)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8259_lock, flags);
+ /* init master interrupt controller */
+ outb(0x11, 0x20); /* Start init sequence */
+ outb(0x00, 0x21); /* Vector base */
+ outb(0x04, 0x21); /* edge tiggered, Cascade (slave) on IRQ2 */
+ outb(0x01, 0x21); /* Select 8086 mode */
+
+ /* init slave interrupt controller */
+ outb(0x11, 0xA0); /* Start init sequence */
+ outb(0x08, 0xA1); /* Vector base */
+ outb(0x02, 0xA1); /* edge triggered, Cascade (slave) on IRQ2 */
+ outb(0x01, 0xA1); /* Select 8086 mode */
+
+ /* always read ISR */
+ outb(0x0B, 0x20);
+ outb(0x0B, 0xA0);
+
+ /* Mask all interrupts */
+ outb(cached_A1, 0xA1);
+ outb(cached_21, 0x21);
+
+ spin_unlock_irqrestore(&i8259_lock, flags);
+
+ /* reserve our resources */
+ setup_irq( i8259_pic_irq_offset + 2, &i8259_irqaction);
+ request_resource(&ioport_resource, &pic1_iores);
+ request_resource(&ioport_resource, &pic2_iores);
+ request_resource(&ioport_resource, &pic_edgectrl_iores);
+
+ if (intack_addr != 0)
+ pci_intack = ioremap(intack_addr, 1);
+}
diff --git a/arch/ppc/syslib/ibm440gp_common.c b/arch/ppc/syslib/ibm440gp_common.c
new file mode 100644
index 000000000000..0d6be2d6dd67
--- /dev/null
+++ b/arch/ppc/syslib/ibm440gp_common.c
@@ -0,0 +1,76 @@
+/*
+ * arch/ppc/syslib/ibm440gp_common.c
+ *
+ * PPC440GP system library
+ *
+ * Matt Porter <mporter@mvista.com>
+ * Copyright 2002-2003 MontaVista Software Inc.
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003 Zultys Technologies
+ *
+ * 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/config.h>
+#include <linux/types.h>
+#include <asm/reg.h>
+#include <asm/ibm44x.h>
+#include <asm/mmu.h>
+
+/*
+ * Calculate 440GP clocks
+ */
+void __init ibm440gp_get_clocks(struct ibm44x_clocks* p,
+ unsigned int sys_clk,
+ unsigned int ser_clk)
+{
+ u32 cpc0_sys0 = mfdcr(DCRN_CPC0_SYS0);
+ u32 cpc0_cr0 = mfdcr(DCRN_CPC0_CR0);
+ u32 opdv = ((cpc0_sys0 >> 10) & 0x3) + 1;
+ u32 epdv = ((cpc0_sys0 >> 8) & 0x3) + 1;
+
+ if (cpc0_sys0 & 0x2){
+ /* Bypass system PLL */
+ p->cpu = p->plb = sys_clk;
+ }
+ else {
+ u32 fbdv, fwdva, fwdvb, m, vco;
+
+ fbdv = (cpc0_sys0 >> 18) & 0x0f;
+ if (!fbdv)
+ fbdv = 16;
+
+ fwdva = 8 - ((cpc0_sys0 >> 15) & 0x7);
+ fwdvb = 8 - ((cpc0_sys0 >> 12) & 0x7);
+
+ /* Feedback path */
+ if (cpc0_sys0 & 0x00000080){
+ /* PerClk */
+ m = fwdvb * opdv * epdv;
+ }
+ else {
+ /* CPU clock */
+ m = fbdv * fwdva;
+ }
+ vco = sys_clk * m;
+ p->cpu = vco / fwdva;
+ p->plb = vco / fwdvb;
+ }
+
+ p->opb = p->plb / opdv;
+ p->ebc = p->opb / epdv;
+
+ if (cpc0_cr0 & 0x00400000){
+ /* External UART clock */
+ p->uart0 = p->uart1 = ser_clk;
+ }
+ else {
+ /* Internal UART clock */
+ u32 uart_div = ((cpc0_cr0 >> 16) & 0x1f) + 1;
+ p->uart0 = p->uart1 = p->plb / uart_div;
+ }
+}
diff --git a/arch/ppc/syslib/ibm440gp_common.h b/arch/ppc/syslib/ibm440gp_common.h
new file mode 100644
index 000000000000..a054d83cb1ac
--- /dev/null
+++ b/arch/ppc/syslib/ibm440gp_common.h
@@ -0,0 +1,35 @@
+/*
+ * arch/ppc/kernel/ibm440gp_common.h
+ *
+ * PPC440GP system library
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003 Zultys Technologies
+ *
+ * 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.
+ *
+ */
+#ifdef __KERNEL__
+#ifndef __PPC_SYSLIB_IBM440GP_COMMON_H
+#define __PPC_SYSLIB_IBM440GP_COMMON_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <syslib/ibm44x_common.h>
+
+/*
+ * Please, refer to the Figure 13.1 in 440GP user manual
+ *
+ * if internal UART clock is used, ser_clk is ignored
+ */
+void ibm440gp_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk,
+ unsigned int ser_clk) __init;
+
+#endif /* __ASSEMBLY__ */
+#endif /* __PPC_SYSLIB_IBM440GP_COMMON_H */
+#endif /* __KERNEL__ */
diff --git a/arch/ppc/syslib/ibm440gx_common.c b/arch/ppc/syslib/ibm440gx_common.c
new file mode 100644
index 000000000000..4ad85e0e0234
--- /dev/null
+++ b/arch/ppc/syslib/ibm440gx_common.c
@@ -0,0 +1,270 @@
+/*
+ * arch/ppc/kernel/ibm440gx_common.c
+ *
+ * PPC440GX system library
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003, 2004 Zultys Technologies
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <asm/ibm44x.h>
+#include <asm/mmu.h>
+#include <asm/processor.h>
+#include <syslib/ibm440gx_common.h>
+
+/*
+ * Calculate 440GX clocks
+ */
+static inline u32 __fix_zero(u32 v, u32 def){
+ return v ? v : def;
+}
+
+void __init ibm440gx_get_clocks(struct ibm44x_clocks* p, unsigned int sys_clk,
+ unsigned int ser_clk)
+{
+ u32 pllc = CPR_READ(DCRN_CPR_PLLC);
+ u32 plld = CPR_READ(DCRN_CPR_PLLD);
+ u32 uart0 = SDR_READ(DCRN_SDR_UART0);
+ u32 uart1 = SDR_READ(DCRN_SDR_UART1);
+
+ /* Dividers */
+ u32 fbdv = __fix_zero((plld >> 24) & 0x1f, 32);
+ u32 fwdva = __fix_zero((plld >> 16) & 0xf, 16);
+ u32 fwdvb = __fix_zero((plld >> 8) & 7, 8);
+ u32 lfbdv = __fix_zero(plld & 0x3f, 64);
+ u32 pradv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMAD) >> 24) & 7, 8);
+ u32 prbdv0 = __fix_zero((CPR_READ(DCRN_CPR_PRIMBD) >> 24) & 7, 8);
+ u32 opbdv0 = __fix_zero((CPR_READ(DCRN_CPR_OPBD) >> 24) & 3, 4);
+ u32 perdv0 = __fix_zero((CPR_READ(DCRN_CPR_PERD) >> 24) & 3, 4);
+
+ /* Input clocks for primary dividers */
+ u32 clk_a, clk_b;
+
+ if (pllc & 0x40000000){
+ u32 m;
+
+ /* Feedback path */
+ switch ((pllc >> 24) & 7){
+ case 0:
+ /* PLLOUTx */
+ m = ((pllc & 0x20000000) ? fwdvb : fwdva) * lfbdv;
+ break;
+ case 1:
+ /* CPU */
+ m = fwdva * pradv0;
+ break;
+ case 5:
+ /* PERClk */
+ m = fwdvb * prbdv0 * opbdv0 * perdv0;
+ break;
+ default:
+ printk(KERN_EMERG "invalid PLL feedback source\n");
+ goto bypass;
+ }
+ m *= fbdv;
+ p->vco = sys_clk * m;
+ clk_a = p->vco / fwdva;
+ clk_b = p->vco / fwdvb;
+ }
+ else {
+bypass:
+ /* Bypass system PLL */
+ p->vco = 0;
+ clk_a = clk_b = sys_clk;
+ }
+
+ p->cpu = clk_a / pradv0;
+ p->plb = clk_b / prbdv0;
+ p->opb = p->plb / opbdv0;
+ p->ebc = p->opb / perdv0;
+
+ /* UARTs clock */
+ if (uart0 & 0x00800000)
+ p->uart0 = ser_clk;
+ else
+ p->uart0 = p->plb / __fix_zero(uart0 & 0xff, 256);
+
+ if (uart1 & 0x00800000)
+ p->uart1 = ser_clk;
+ else
+ p->uart1 = p->plb / __fix_zero(uart1 & 0xff, 256);
+}
+
+/* Issue L2C diagnostic command */
+static inline u32 l2c_diag(u32 addr)
+{
+ mtdcr(DCRN_L2C0_ADDR, addr);
+ mtdcr(DCRN_L2C0_CMD, L2C_CMD_DIAG);
+ while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ;
+ return mfdcr(DCRN_L2C0_DATA);
+}
+
+static irqreturn_t l2c_error_handler(int irq, void* dev, struct pt_regs* regs)
+{
+ u32 sr = mfdcr(DCRN_L2C0_SR);
+ if (sr & L2C_SR_CPE){
+ /* Read cache trapped address */
+ u32 addr = l2c_diag(0x42000000);
+ printk(KERN_EMERG "L2C: Cache Parity Error, addr[16:26] = 0x%08x\n", addr);
+ }
+ if (sr & L2C_SR_TPE){
+ /* Read tag trapped address */
+ u32 addr = l2c_diag(0x82000000) >> 16;
+ printk(KERN_EMERG "L2C: Tag Parity Error, addr[16:26] = 0x%08x\n", addr);
+ }
+
+ /* Clear parity errors */
+ if (sr & (L2C_SR_CPE | L2C_SR_TPE)){
+ mtdcr(DCRN_L2C0_ADDR, 0);
+ mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
+ } else
+ printk(KERN_EMERG "L2C: LRU error\n");
+
+ return IRQ_HANDLED;
+}
+
+/* Enable L2 cache */
+void __init ibm440gx_l2c_enable(void){
+ u32 r;
+ unsigned long flags;
+
+ /* Install error handler */
+ if (request_irq(87, l2c_error_handler, SA_INTERRUPT, "L2C", 0) < 0){
+ printk(KERN_ERR "Cannot install L2C error handler, cache is not enabled\n");
+ return;
+ }
+
+ local_irq_save(flags);
+ asm volatile ("sync" ::: "memory");
+
+ /* Disable SRAM */
+ mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) & ~SRAM_DPC_ENABLE);
+ mtdcr(DCRN_SRAM0_SB0CR, mfdcr(DCRN_SRAM0_SB0CR) & ~SRAM_SBCR_BU_MASK);
+ mtdcr(DCRN_SRAM0_SB1CR, mfdcr(DCRN_SRAM0_SB1CR) & ~SRAM_SBCR_BU_MASK);
+ mtdcr(DCRN_SRAM0_SB2CR, mfdcr(DCRN_SRAM0_SB2CR) & ~SRAM_SBCR_BU_MASK);
+ mtdcr(DCRN_SRAM0_SB3CR, mfdcr(DCRN_SRAM0_SB3CR) & ~SRAM_SBCR_BU_MASK);
+
+ /* Enable L2_MODE without ICU/DCU */
+ r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_SS_MASK);
+ r |= L2C_CFG_L2M | L2C_CFG_SS_256;
+ mtdcr(DCRN_L2C0_CFG, r);
+
+ mtdcr(DCRN_L2C0_ADDR, 0);
+
+ /* Hardware Clear Command */
+ mtdcr(DCRN_L2C0_CMD, L2C_CMD_HCC);
+ while (!(mfdcr(DCRN_L2C0_SR) & L2C_SR_CC)) ;
+
+ /* Clear Cache Parity and Tag Errors */
+ mtdcr(DCRN_L2C0_CMD, L2C_CMD_CCP | L2C_CMD_CTE);
+
+ /* Enable 64G snoop region starting at 0 */
+ r = mfdcr(DCRN_L2C0_SNP0) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
+ r |= L2C_SNP_SSR_32G | L2C_SNP_ESR;
+ mtdcr(DCRN_L2C0_SNP0, r);
+
+ r = mfdcr(DCRN_L2C0_SNP1) & ~(L2C_SNP_BA_MASK | L2C_SNP_SSR_MASK);
+ r |= 0x80000000 | L2C_SNP_SSR_32G | L2C_SNP_ESR;
+ mtdcr(DCRN_L2C0_SNP1, r);
+
+ asm volatile ("sync" ::: "memory");
+
+ /* Enable ICU/DCU ports */
+ r = mfdcr(DCRN_L2C0_CFG);
+ r &= ~(L2C_CFG_DCW_MASK | L2C_CFG_PMUX_MASK | L2C_CFG_PMIM | L2C_CFG_TPEI
+ | L2C_CFG_CPEI | L2C_CFG_NAM | L2C_CFG_NBRM);
+ r |= L2C_CFG_ICU | L2C_CFG_DCU | L2C_CFG_TPC | L2C_CFG_CPC | L2C_CFG_FRAN
+ | L2C_CFG_CPIM | L2C_CFG_TPIM | L2C_CFG_LIM | L2C_CFG_SMCM;
+ mtdcr(DCRN_L2C0_CFG, r);
+
+ asm volatile ("sync; isync" ::: "memory");
+ local_irq_restore(flags);
+}
+
+/* Disable L2 cache */
+void __init ibm440gx_l2c_disable(void){
+ u32 r;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ asm volatile ("sync" ::: "memory");
+
+ /* Disable L2C mode */
+ r = mfdcr(DCRN_L2C0_CFG) & ~(L2C_CFG_L2M | L2C_CFG_ICU | L2C_CFG_DCU);
+ mtdcr(DCRN_L2C0_CFG, r);
+
+ /* Enable SRAM */
+ mtdcr(DCRN_SRAM0_DPC, mfdcr(DCRN_SRAM0_DPC) | SRAM_DPC_ENABLE);
+ mtdcr(DCRN_SRAM0_SB0CR,
+ SRAM_SBCR_BAS0 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
+ mtdcr(DCRN_SRAM0_SB1CR,
+ SRAM_SBCR_BAS1 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
+ mtdcr(DCRN_SRAM0_SB2CR,
+ SRAM_SBCR_BAS2 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
+ mtdcr(DCRN_SRAM0_SB3CR,
+ SRAM_SBCR_BAS3 | SRAM_SBCR_BS_64KB | SRAM_SBCR_BU_RW);
+
+ asm volatile ("sync; isync" ::: "memory");
+ local_irq_restore(flags);
+}
+
+void __init ibm440gx_l2c_setup(struct ibm44x_clocks* p)
+{
+ /* Disable L2C on rev.A, rev.B and 800MHz version of rev.C,
+ enable it on all other revisions
+ */
+ u32 pvr = mfspr(SPRN_PVR);
+ if (pvr == PVR_440GX_RA || pvr == PVR_440GX_RB ||
+ (pvr == PVR_440GX_RC && p->cpu > 667000000))
+ ibm440gx_l2c_disable();
+ else
+ ibm440gx_l2c_enable();
+}
+
+int __init ibm440gx_get_eth_grp(void)
+{
+ return (SDR_READ(DCRN_SDR_PFC1) & DCRN_SDR_PFC1_EPS) >> DCRN_SDR_PFC1_EPS_SHIFT;
+}
+
+void __init ibm440gx_set_eth_grp(int group)
+{
+ SDR_WRITE(DCRN_SDR_PFC1, (SDR_READ(DCRN_SDR_PFC1) & ~DCRN_SDR_PFC1_EPS) | (group << DCRN_SDR_PFC1_EPS_SHIFT));
+}
+
+void __init ibm440gx_tah_enable(void)
+{
+ /* Enable TAH0 and TAH1 */
+ SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) &
+ ~DCRN_SDR_MFR_TAH0);
+ SDR_WRITE(DCRN_SDR_MFR,SDR_READ(DCRN_SDR_MFR) &
+ ~DCRN_SDR_MFR_TAH1);
+}
+
+int ibm440gx_show_cpuinfo(struct seq_file *m){
+
+ u32 l2c_cfg = mfdcr(DCRN_L2C0_CFG);
+ const char* s;
+ if (l2c_cfg & L2C_CFG_L2M){
+ switch (l2c_cfg & (L2C_CFG_ICU | L2C_CFG_DCU)){
+ case L2C_CFG_ICU: s = "I-Cache only"; break;
+ case L2C_CFG_DCU: s = "D-Cache only"; break;
+ default: s = "I-Cache/D-Cache"; break;
+ }
+ }
+ else
+ s = "disabled";
+
+ seq_printf(m, "L2-Cache\t: %s (0x%08x 0x%08x)\n", s,
+ l2c_cfg, mfdcr(DCRN_L2C0_SR));
+
+ return 0;
+}
+
diff --git a/arch/ppc/syslib/ibm440gx_common.h b/arch/ppc/syslib/ibm440gx_common.h
new file mode 100644
index 000000000000..e73aa0411d35
--- /dev/null
+++ b/arch/ppc/syslib/ibm440gx_common.h
@@ -0,0 +1,57 @@
+/*
+ * arch/ppc/kernel/ibm440gx_common.h
+ *
+ * PPC440GX system library
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003, 2004 Zultys Technologies
+ *
+ * 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.
+ *
+ */
+#ifdef __KERNEL__
+#ifndef __PPC_SYSLIB_IBM440GX_COMMON_H
+#define __PPC_SYSLIB_IBM440GX_COMMON_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <syslib/ibm44x_common.h>
+
+/*
+ * Please, refer to the Figure 14.1 in 440GX user manual
+ *
+ * if internal UART clock is used, ser_clk is ignored
+ */
+void ibm440gx_get_clocks(struct ibm44x_clocks*, unsigned int sys_clk,
+ unsigned int ser_clk) __init;
+
+/* Enable L2 cache */
+void ibm440gx_l2c_enable(void) __init;
+
+/* Disable L2 cache */
+void ibm440gx_l2c_disable(void) __init;
+
+/* Enable/disable L2 cache for a particular chip revision */
+void ibm440gx_l2c_setup(struct ibm44x_clocks*) __init;
+
+/* Get Ethernet Group */
+int ibm440gx_get_eth_grp(void) __init;
+
+/* Set Ethernet Group */
+void ibm440gx_set_eth_grp(int group) __init;
+
+/* Enable TAH devices */
+void ibm440gx_tah_enable(void) __init;
+
+/* Add L2C info to /proc/cpuinfo */
+int ibm440gx_show_cpuinfo(struct seq_file*);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __PPC_SYSLIB_IBM440GX_COMMON_H */
+#endif /* __KERNEL__ */
diff --git a/arch/ppc/syslib/ibm440sp_common.c b/arch/ppc/syslib/ibm440sp_common.c
new file mode 100644
index 000000000000..417d4cff77a0
--- /dev/null
+++ b/arch/ppc/syslib/ibm440sp_common.c
@@ -0,0 +1,71 @@
+/*
+ * arch/ppc/syslib/ibm440sp_common.c
+ *
+ * PPC440SP system library
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2002-2005 MontaVista Software Inc.
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003, 2004 Zultys Technologies
+ *
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/serial.h>
+
+#include <asm/param.h>
+#include <asm/ibm44x.h>
+#include <asm/mmu.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+#include <asm/ppc4xx_pic.h>
+
+/*
+ * Read the 440SP memory controller to get size of system memory.
+ */
+unsigned long __init ibm440sp_find_end_of_memory(void)
+{
+ u32 i;
+ u32 mem_size = 0;
+
+ /* Read two bank sizes and sum */
+ for (i=0; i<2; i++)
+ switch (mfdcr(DCRN_MQ0_BS0BAS + i) & MQ0_CONFIG_SIZE_MASK) {
+ case MQ0_CONFIG_SIZE_8M:
+ mem_size += PPC44x_MEM_SIZE_8M;
+ break;
+ case MQ0_CONFIG_SIZE_16M:
+ mem_size += PPC44x_MEM_SIZE_16M;
+ break;
+ case MQ0_CONFIG_SIZE_32M:
+ mem_size += PPC44x_MEM_SIZE_32M;
+ break;
+ case MQ0_CONFIG_SIZE_64M:
+ mem_size += PPC44x_MEM_SIZE_64M;
+ break;
+ case MQ0_CONFIG_SIZE_128M:
+ mem_size += PPC44x_MEM_SIZE_128M;
+ break;
+ case MQ0_CONFIG_SIZE_256M:
+ mem_size += PPC44x_MEM_SIZE_256M;
+ break;
+ case MQ0_CONFIG_SIZE_512M:
+ mem_size += PPC44x_MEM_SIZE_512M;
+ break;
+ case MQ0_CONFIG_SIZE_1G:
+ mem_size += PPC44x_MEM_SIZE_1G;
+ break;
+ case MQ0_CONFIG_SIZE_2G:
+ mem_size += PPC44x_MEM_SIZE_2G;
+ break;
+ default:
+ break;
+ }
+ return mem_size;
+}
diff --git a/arch/ppc/syslib/ibm440sp_common.h b/arch/ppc/syslib/ibm440sp_common.h
new file mode 100644
index 000000000000..a21a9906dcc9
--- /dev/null
+++ b/arch/ppc/syslib/ibm440sp_common.h
@@ -0,0 +1,25 @@
+/*
+ * arch/ppc/syslib/ibm440sp_common.h
+ *
+ * PPC440SP system library
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2004-2005 MontaVista Software, Inc.
+ *
+ * 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.
+ *
+ */
+#ifdef __KERNEL__
+#ifndef __PPC_SYSLIB_IBM440SP_COMMON_H
+#define __PPC_SYSLIB_IBM440SP_COMMON_H
+
+#ifndef __ASSEMBLY__
+
+extern unsigned long __init ibm440sp_find_end_of_memory(void);
+
+#endif /* __ASSEMBLY__ */
+#endif /* __PPC_SYSLIB_IBM440SP_COMMON_H */
+#endif /* __KERNEL__ */
diff --git a/arch/ppc/syslib/ibm44x_common.c b/arch/ppc/syslib/ibm44x_common.c
new file mode 100644
index 000000000000..7612e0623f99
--- /dev/null
+++ b/arch/ppc/syslib/ibm44x_common.c
@@ -0,0 +1,193 @@
+/*
+ * arch/ppc/syslib/ibm44x_common.c
+ *
+ * PPC44x system library
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2002-2005 MontaVista Software Inc.
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003, 2004 Zultys Technologies
+ *
+ * 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/config.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <linux/module.h>
+
+#include <asm/ibm44x.h>
+#include <asm/mmu.h>
+#include <asm/machdep.h>
+#include <asm/time.h>
+#include <asm/ppc4xx_pic.h>
+#include <asm/param.h>
+
+#include <syslib/gen550.h>
+
+phys_addr_t fixup_bigphys_addr(phys_addr_t addr, phys_addr_t size)
+{
+ phys_addr_t page_4gb = 0;
+
+ /*
+ * Trap the least significant 32-bit portions of an
+ * address in the 440's 36-bit address space. Fix
+ * them up with the appropriate ERPN
+ */
+ if ((addr >= PPC44x_IO_LO) && (addr <= PPC44x_IO_HI))
+ page_4gb = PPC44x_IO_PAGE;
+ else if ((addr >= PPC44x_PCI0CFG_LO) && (addr <= PPC44x_PCI0CFG_HI))
+ page_4gb = PPC44x_PCICFG_PAGE;
+#ifdef CONFIG_440SP
+ else if ((addr >= PPC44x_PCI1CFG_LO) && (addr <= PPC44x_PCI1CFG_HI))
+ page_4gb = PPC44x_PCICFG_PAGE;
+ else if ((addr >= PPC44x_PCI2CFG_LO) && (addr <= PPC44x_PCI2CFG_HI))
+ page_4gb = PPC44x_PCICFG_PAGE;
+#endif
+ else if ((addr >= PPC44x_PCIMEM_LO) && (addr <= PPC44x_PCIMEM_HI))
+ page_4gb = PPC44x_PCIMEM_PAGE;
+
+ return (page_4gb | addr);
+};
+EXPORT_SYMBOL(fixup_bigphys_addr);
+
+void __init ibm44x_calibrate_decr(unsigned int freq)
+{
+ tb_ticks_per_jiffy = freq / HZ;
+ tb_to_us = mulhwu_scale_factor(freq, 1000000);
+
+ /* Set the time base to zero */
+ mtspr(SPRN_TBWL, 0);
+ mtspr(SPRN_TBWU, 0);
+
+ /* Clear any pending timer interrupts */
+ mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS);
+
+ /* Enable decrementer interrupt */
+ mtspr(SPRN_TCR, TCR_DIE);
+}
+
+extern void abort(void);
+
+static void ibm44x_restart(char *cmd)
+{
+ local_irq_disable();
+ abort();
+}
+
+static void ibm44x_power_off(void)
+{
+ local_irq_disable();
+ for(;;);
+}
+
+static void ibm44x_halt(void)
+{
+ local_irq_disable();
+ for(;;);
+}
+
+/*
+ * Read the 44x memory controller to get size of system memory.
+ */
+static unsigned long __init ibm44x_find_end_of_memory(void)
+{
+ u32 i, bank_config;
+ u32 mem_size = 0;
+
+ for (i=0; i<4; i++)
+ {
+ switch (i)
+ {
+ case 0:
+ mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B0CR);
+ break;
+ case 1:
+ mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B1CR);
+ break;
+ case 2:
+ mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B2CR);
+ break;
+ case 3:
+ mtdcr(DCRN_SDRAM0_CFGADDR, SDRAM0_B3CR);
+ break;
+ }
+
+ bank_config = mfdcr(DCRN_SDRAM0_CFGDATA);
+
+ if (!(bank_config & SDRAM_CONFIG_BANK_ENABLE))
+ continue;
+ switch (SDRAM_CONFIG_BANK_SIZE(bank_config))
+ {
+ case SDRAM_CONFIG_SIZE_8M:
+ mem_size += PPC44x_MEM_SIZE_8M;
+ break;
+ case SDRAM_CONFIG_SIZE_16M:
+ mem_size += PPC44x_MEM_SIZE_16M;
+ break;
+ case SDRAM_CONFIG_SIZE_32M:
+ mem_size += PPC44x_MEM_SIZE_32M;
+ break;
+ case SDRAM_CONFIG_SIZE_64M:
+ mem_size += PPC44x_MEM_SIZE_64M;
+ break;
+ case SDRAM_CONFIG_SIZE_128M:
+ mem_size += PPC44x_MEM_SIZE_128M;
+ break;
+ case SDRAM_CONFIG_SIZE_256M:
+ mem_size += PPC44x_MEM_SIZE_256M;
+ break;
+ case SDRAM_CONFIG_SIZE_512M:
+ mem_size += PPC44x_MEM_SIZE_512M;
+ break;
+ }
+ }
+ return mem_size;
+}
+
+void __init ibm44x_platform_init(void)
+{
+ ppc_md.init_IRQ = ppc4xx_pic_init;
+ ppc_md.find_end_of_memory = ibm44x_find_end_of_memory;
+ ppc_md.restart = ibm44x_restart;
+ ppc_md.power_off = ibm44x_power_off;
+ ppc_md.halt = ibm44x_halt;
+
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ ppc_md.progress = gen550_progress;
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+#ifdef CONFIG_KGDB
+ ppc_md.kgdb_map_scc = gen550_kgdb_map_scc;
+#endif
+
+ /*
+ * The Abatron BDI JTAG debugger does not tolerate others
+ * mucking with the debug registers.
+ */
+#if !defined(CONFIG_BDI_SWITCH)
+ /* Enable internal debug mode */
+ mtspr(SPRN_DBCR0, (DBCR0_IDM));
+
+ /* Clear any residual debug events */
+ mtspr(SPRN_DBSR, 0xffffffff);
+#endif
+}
+
+/* Called from MachineCheckException */
+void platform_machine_check(struct pt_regs *regs)
+{
+ printk("PLB0: BEAR=0x%08x%08x ACR= 0x%08x BESR= 0x%08x\n",
+ mfdcr(DCRN_PLB0_BEARH), mfdcr(DCRN_PLB0_BEARL),
+ mfdcr(DCRN_PLB0_ACR), mfdcr(DCRN_PLB0_BESR));
+ printk("POB0: BEAR=0x%08x%08x BESR0=0x%08x BESR1=0x%08x\n",
+ mfdcr(DCRN_POB0_BEARH), mfdcr(DCRN_POB0_BEARL),
+ mfdcr(DCRN_POB0_BESR0), mfdcr(DCRN_POB0_BESR1));
+ printk("OPB0: BEAR=0x%08x%08x BSTAT=0x%08x\n",
+ mfdcr(DCRN_OPB0_BEARH), mfdcr(DCRN_OPB0_BEARL),
+ mfdcr(DCRN_OPB0_BSTAT));
+}
diff --git a/arch/ppc/syslib/ibm44x_common.h b/arch/ppc/syslib/ibm44x_common.h
new file mode 100644
index 000000000000..b14eb603ce01
--- /dev/null
+++ b/arch/ppc/syslib/ibm44x_common.h
@@ -0,0 +1,42 @@
+/*
+ * arch/ppc/kernel/ibm44x_common.h
+ *
+ * PPC44x system library
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2003, 2004 Zultys Technologies
+ *
+ * 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.
+ *
+ */
+#ifdef __KERNEL__
+#ifndef __PPC_SYSLIB_IBM44x_COMMON_H
+#define __PPC_SYSLIB_IBM44x_COMMON_H
+
+#ifndef __ASSEMBLY__
+
+/*
+ * All clocks are in Hz
+ */
+struct ibm44x_clocks {
+ unsigned int vco; /* VCO, 0 if system PLL is bypassed */
+ unsigned int cpu; /* CPUCoreClk */
+ unsigned int plb; /* PLBClk */
+ unsigned int opb; /* OPBClk */
+ unsigned int ebc; /* PerClk */
+ unsigned int uart0;
+ unsigned int uart1;
+};
+
+/* common 44x platform init */
+void ibm44x_platform_init(void) __init;
+
+/* initialize decrementer and tick-related variables */
+void ibm44x_calibrate_decr(unsigned int freq) __init;
+
+#endif /* __ASSEMBLY__ */
+#endif /* __PPC_SYSLIB_IBM44x_COMMON_H */
+#endif /* __KERNEL__ */
diff --git a/arch/ppc/syslib/ibm_ocp.c b/arch/ppc/syslib/ibm_ocp.c
new file mode 100644
index 000000000000..3f6e55c79181
--- /dev/null
+++ b/arch/ppc/syslib/ibm_ocp.c
@@ -0,0 +1,9 @@
+#include <linux/module.h>
+#include <asm/ocp.h>
+
+struct ocp_sys_info_data ocp_sys_info = {
+ .opb_bus_freq = 50000000, /* OPB Bus Frequency (Hz) */
+ .ebc_bus_freq = 33333333, /* EBC Bus Frequency (Hz) */
+};
+
+EXPORT_SYMBOL(ocp_sys_info);
diff --git a/arch/ppc/syslib/indirect_pci.c b/arch/ppc/syslib/indirect_pci.c
new file mode 100644
index 000000000000..a5a752609e2c
--- /dev/null
+++ b/arch/ppc/syslib/indirect_pci.c
@@ -0,0 +1,135 @@
+/*
+ * Support for indirect PCI bridges.
+ *
+ * Copyright (C) 1998 Gabriel Paubert.
+ *
+ * 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/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+
+#ifdef CONFIG_PPC_INDIRECT_PCI_BE
+#define PCI_CFG_OUT out_be32
+#else
+#define PCI_CFG_OUT out_le32
+#endif
+
+static int
+indirect_read_config(struct pci_bus *bus, unsigned int devfn, int offset,
+ int len, u32 *val)
+{
+ struct pci_controller *hose = bus->sysdata;
+ volatile void __iomem *cfg_data;
+ u8 cfg_type = 0;
+
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (bus->number != hose->first_busno)
+ cfg_type = 1;
+
+ PCI_CFG_OUT(hose->cfg_addr,
+ (0x80000000 | ((bus->number - hose->bus_offset) << 16)
+ | (devfn << 8) | ((offset & 0xfc) | cfg_type)));
+
+ /*
+ * Note: the caller has already checked that offset is
+ * suitably aligned and that len is 1, 2 or 4.
+ */
+ cfg_data = hose->cfg_data + (offset & 3);
+ switch (len) {
+ case 1:
+ *val = in_8(cfg_data);
+ break;
+ case 2:
+ *val = in_le16(cfg_data);
+ break;
+ default:
+ *val = in_le32(cfg_data);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+indirect_write_config(struct pci_bus *bus, unsigned int devfn, int offset,
+ int len, u32 val)
+{
+ struct pci_controller *hose = bus->sysdata;
+ volatile void __iomem *cfg_data;
+ u8 cfg_type = 0;
+
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (bus->number != hose->first_busno)
+ cfg_type = 1;
+
+ PCI_CFG_OUT(hose->cfg_addr,
+ (0x80000000 | ((bus->number - hose->bus_offset) << 16)
+ | (devfn << 8) | ((offset & 0xfc) | cfg_type)));
+
+ /*
+ * Note: the caller has already checked that offset is
+ * suitably aligned and that len is 1, 2 or 4.
+ */
+ cfg_data = hose->cfg_data + (offset & 3);
+ switch (len) {
+ case 1:
+ out_8(cfg_data, val);
+ break;
+ case 2:
+ out_le16(cfg_data, val);
+ break;
+ default:
+ out_le32(cfg_data, val);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops indirect_pci_ops =
+{
+ indirect_read_config,
+ indirect_write_config
+};
+
+void __init
+setup_indirect_pci_nomap(struct pci_controller* hose, void __iomem * cfg_addr,
+ void __iomem * cfg_data)
+{
+ hose->cfg_addr = cfg_addr;
+ hose->cfg_data = cfg_data;
+ hose->ops = &indirect_pci_ops;
+}
+
+void __init
+setup_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
+{
+ unsigned long base = cfg_addr & PAGE_MASK;
+ void __iomem *mbase, *addr, *data;
+
+ mbase = ioremap(base, PAGE_SIZE);
+ addr = mbase + (cfg_addr & ~PAGE_MASK);
+ if ((cfg_data & PAGE_MASK) != base)
+ mbase = ioremap(cfg_data & PAGE_MASK, PAGE_SIZE);
+ data = mbase + (cfg_data & ~PAGE_MASK);
+ setup_indirect_pci_nomap(hose, addr, data);
+}
diff --git a/arch/ppc/syslib/ipic.c b/arch/ppc/syslib/ipic.c
new file mode 100644
index 000000000000..acb2cde3171f
--- /dev/null
+++ b/arch/ppc/syslib/ipic.c
@@ -0,0 +1,646 @@
+/*
+ * include/asm-ppc/ipic.c
+ *
+ * IPIC routines implementations.
+ *
+ * Copyright 2005 Freescale Semiconductor, Inc.
+ *
+ * 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/init.h>
+#include <linux/errno.h>
+#include <linux/reboot.h>
+#include <linux/slab.h>
+#include <linux/stddef.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/sysdev.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/ipic.h>
+#include <asm/mpc83xx.h>
+
+#include "ipic.h"
+
+static struct ipic p_ipic;
+static struct ipic * primary_ipic;
+
+static struct ipic_info ipic_info[] = {
+ [9] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_D,
+ .force = IPIC_SIFCR_H,
+ .bit = 24,
+ .prio_mask = 0,
+ },
+ [10] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_D,
+ .force = IPIC_SIFCR_H,
+ .bit = 25,
+ .prio_mask = 1,
+ },
+ [11] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_D,
+ .force = IPIC_SIFCR_H,
+ .bit = 26,
+ .prio_mask = 2,
+ },
+ [14] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_D,
+ .force = IPIC_SIFCR_H,
+ .bit = 29,
+ .prio_mask = 5,
+ },
+ [15] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_D,
+ .force = IPIC_SIFCR_H,
+ .bit = 30,
+ .prio_mask = 6,
+ },
+ [16] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_D,
+ .force = IPIC_SIFCR_H,
+ .bit = 31,
+ .prio_mask = 7,
+ },
+ [17] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SEFCR,
+ .bit = 1,
+ .prio_mask = 5,
+ },
+ [18] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SEFCR,
+ .bit = 2,
+ .prio_mask = 6,
+ },
+ [19] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SEFCR,
+ .bit = 3,
+ .prio_mask = 7,
+ },
+ [20] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SEFCR,
+ .bit = 4,
+ .prio_mask = 4,
+ },
+ [21] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SEFCR,
+ .bit = 5,
+ .prio_mask = 5,
+ },
+ [22] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SEFCR,
+ .bit = 6,
+ .prio_mask = 6,
+ },
+ [23] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SEFCR,
+ .bit = 7,
+ .prio_mask = 7,
+ },
+ [32] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 0,
+ .prio_mask = 0,
+ },
+ [33] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 1,
+ .prio_mask = 1,
+ },
+ [34] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 2,
+ .prio_mask = 2,
+ },
+ [35] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 3,
+ .prio_mask = 3,
+ },
+ [36] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 4,
+ .prio_mask = 4,
+ },
+ [37] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 5,
+ .prio_mask = 5,
+ },
+ [38] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 6,
+ .prio_mask = 6,
+ },
+ [39] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_H,
+ .prio = IPIC_SIPRR_A,
+ .force = IPIC_SIFCR_H,
+ .bit = 7,
+ .prio_mask = 7,
+ },
+ [48] = {
+ .pend = IPIC_SEPNR,
+ .mask = IPIC_SEMSR,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SEFCR,
+ .bit = 0,
+ .prio_mask = 4,
+ },
+ [64] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SIFCR_L,
+ .bit = 0,
+ .prio_mask = 0,
+ },
+ [65] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SIFCR_L,
+ .bit = 1,
+ .prio_mask = 1,
+ },
+ [66] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SIFCR_L,
+ .bit = 2,
+ .prio_mask = 2,
+ },
+ [67] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_A,
+ .force = IPIC_SIFCR_L,
+ .bit = 3,
+ .prio_mask = 3,
+ },
+ [68] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SIFCR_L,
+ .bit = 4,
+ .prio_mask = 0,
+ },
+ [69] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SIFCR_L,
+ .bit = 5,
+ .prio_mask = 1,
+ },
+ [70] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SIFCR_L,
+ .bit = 6,
+ .prio_mask = 2,
+ },
+ [71] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = IPIC_SMPRR_B,
+ .force = IPIC_SIFCR_L,
+ .bit = 7,
+ .prio_mask = 3,
+ },
+ [72] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 8,
+ },
+ [73] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 9,
+ },
+ [74] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 10,
+ },
+ [75] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 11,
+ },
+ [76] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 12,
+ },
+ [77] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 13,
+ },
+ [78] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 14,
+ },
+ [79] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 15,
+ },
+ [80] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 16,
+ },
+ [84] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 20,
+ },
+ [85] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 21,
+ },
+ [90] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 26,
+ },
+ [91] = {
+ .pend = IPIC_SIPNR_H,
+ .mask = IPIC_SIMSR_L,
+ .prio = 0,
+ .force = IPIC_SIFCR_L,
+ .bit = 27,
+ },
+};
+
+static inline u32 ipic_read(volatile u32 __iomem *base, unsigned int reg)
+{
+ return in_be32(base + (reg >> 2));
+}
+
+static inline void ipic_write(volatile u32 __iomem *base, unsigned int reg, u32 value)
+{
+ out_be32(base + (reg >> 2), value);
+}
+
+static inline struct ipic * ipic_from_irq(unsigned int irq)
+{
+ return primary_ipic;
+}
+
+static void ipic_enable_irq(unsigned int irq)
+{
+ struct ipic *ipic = ipic_from_irq(irq);
+ unsigned int src = irq - ipic->irq_offset;
+ u32 temp;
+
+ temp = ipic_read(ipic->regs, ipic_info[src].mask);
+ temp |= (1 << (31 - ipic_info[src].bit));
+ ipic_write(ipic->regs, ipic_info[src].mask, temp);
+}
+
+static void ipic_disable_irq(unsigned int irq)
+{
+ struct ipic *ipic = ipic_from_irq(irq);
+ unsigned int src = irq - ipic->irq_offset;
+ u32 temp;
+
+ temp = ipic_read(ipic->regs, ipic_info[src].mask);
+ temp &= ~(1 << (31 - ipic_info[src].bit));
+ ipic_write(ipic->regs, ipic_info[src].mask, temp);
+}
+
+static void ipic_disable_irq_and_ack(unsigned int irq)
+{
+ struct ipic *ipic = ipic_from_irq(irq);
+ unsigned int src = irq - ipic->irq_offset;
+ u32 temp;
+
+ ipic_disable_irq(irq);
+
+ temp = ipic_read(ipic->regs, ipic_info[src].pend);
+ temp |= (1 << (31 - ipic_info[src].bit));
+ ipic_write(ipic->regs, ipic_info[src].pend, temp);
+}
+
+static void ipic_end_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ ipic_enable_irq(irq);
+}
+
+struct hw_interrupt_type ipic = {
+ .typename = " IPIC ",
+ .enable = ipic_enable_irq,
+ .disable = ipic_disable_irq,
+ .ack = ipic_disable_irq_and_ack,
+ .end = ipic_end_irq,
+};
+
+void __init ipic_init(phys_addr_t phys_addr,
+ unsigned int flags,
+ unsigned int irq_offset,
+ unsigned char *senses,
+ unsigned int senses_count)
+{
+ u32 i, temp = 0;
+
+ primary_ipic = &p_ipic;
+ primary_ipic->regs = ioremap(phys_addr, MPC83xx_IPIC_SIZE);
+
+ primary_ipic->irq_offset = irq_offset;
+
+ ipic_write(primary_ipic->regs, IPIC_SICNR, 0x0);
+
+ /* default priority scheme is grouped. If spread mode is required
+ * configure SICFR accordingly */
+ if (flags & IPIC_SPREADMODE_GRP_A)
+ temp |= SICFR_IPSA;
+ if (flags & IPIC_SPREADMODE_GRP_D)
+ temp |= SICFR_IPSD;
+ if (flags & IPIC_SPREADMODE_MIX_A)
+ temp |= SICFR_MPSA;
+ if (flags & IPIC_SPREADMODE_MIX_B)
+ temp |= SICFR_MPSB;
+
+ ipic_write(primary_ipic->regs, IPIC_SICNR, temp);
+
+ /* handle MCP route */
+ temp = 0;
+ if (flags & IPIC_DISABLE_MCP_OUT)
+ temp = SERCR_MCPR;
+ ipic_write(primary_ipic->regs, IPIC_SERCR, temp);
+
+ /* handle routing of IRQ0 to MCP */
+ temp = ipic_read(primary_ipic->regs, IPIC_SEMSR);
+
+ if (flags & IPIC_IRQ0_MCP)
+ temp |= SEMSR_SIRQ0;
+ else
+ temp &= ~SEMSR_SIRQ0;
+
+ ipic_write(primary_ipic->regs, IPIC_SEMSR, temp);
+
+ for (i = 0 ; i < NR_IPIC_INTS ; i++) {
+ irq_desc[i+irq_offset].handler = &ipic;
+ irq_desc[i+irq_offset].status = IRQ_LEVEL;
+ }
+
+ temp = 0;
+ for (i = 0 ; i < senses_count ; i++) {
+ if ((senses[i] & IRQ_SENSE_MASK) == IRQ_SENSE_EDGE) {
+ temp |= 1 << (16 - i);
+ if (i != 0)
+ irq_desc[i + irq_offset + MPC83xx_IRQ_EXT1 - 1].status = 0;
+ else
+ irq_desc[irq_offset + MPC83xx_IRQ_EXT0].status = 0;
+ }
+ }
+ ipic_write(primary_ipic->regs, IPIC_SECNR, temp);
+
+ printk ("IPIC (%d IRQ sources, %d External IRQs) at %p\n", NR_IPIC_INTS,
+ senses_count, primary_ipic->regs);
+}
+
+int ipic_set_priority(unsigned int irq, unsigned int priority)
+{
+ struct ipic *ipic = ipic_from_irq(irq);
+ unsigned int src = irq - ipic->irq_offset;
+ u32 temp;
+
+ if (priority > 7)
+ return -EINVAL;
+ if (src > 127)
+ return -EINVAL;
+ if (ipic_info[src].prio == 0)
+ return -EINVAL;
+
+ temp = ipic_read(ipic->regs, ipic_info[src].prio);
+
+ if (priority < 4) {
+ temp &= ~(0x7 << (20 + (3 - priority) * 3));
+ temp |= ipic_info[src].prio_mask << (20 + (3 - priority) * 3);
+ } else {
+ temp &= ~(0x7 << (4 + (7 - priority) * 3));
+ temp |= ipic_info[src].prio_mask << (4 + (7 - priority) * 3);
+ }
+
+ ipic_write(ipic->regs, ipic_info[src].prio, temp);
+
+ return 0;
+}
+
+void ipic_set_highest_priority(unsigned int irq)
+{
+ struct ipic *ipic = ipic_from_irq(irq);
+ unsigned int src = irq - ipic->irq_offset;
+ u32 temp;
+
+ temp = ipic_read(ipic->regs, IPIC_SICFR);
+
+ /* clear and set HPI */
+ temp &= 0x7f000000;
+ temp |= (src & 0x7f) << 24;
+
+ ipic_write(ipic->regs, IPIC_SICFR, temp);
+}
+
+void ipic_set_default_priority(void)
+{
+ ipic_set_priority(MPC83xx_IRQ_TSEC1_TX, 0);
+ ipic_set_priority(MPC83xx_IRQ_TSEC1_RX, 1);
+ ipic_set_priority(MPC83xx_IRQ_TSEC1_ERROR, 2);
+ ipic_set_priority(MPC83xx_IRQ_TSEC2_TX, 3);
+ ipic_set_priority(MPC83xx_IRQ_TSEC2_RX, 4);
+ ipic_set_priority(MPC83xx_IRQ_TSEC2_ERROR, 5);
+ ipic_set_priority(MPC83xx_IRQ_USB2_DR, 6);
+ ipic_set_priority(MPC83xx_IRQ_USB2_MPH, 7);
+
+ ipic_set_priority(MPC83xx_IRQ_UART1, 0);
+ ipic_set_priority(MPC83xx_IRQ_UART2, 1);
+ ipic_set_priority(MPC83xx_IRQ_SEC2, 2);
+ ipic_set_priority(MPC83xx_IRQ_IIC1, 5);
+ ipic_set_priority(MPC83xx_IRQ_IIC2, 6);
+ ipic_set_priority(MPC83xx_IRQ_SPI, 7);
+ ipic_set_priority(MPC83xx_IRQ_RTC_SEC, 0);
+ ipic_set_priority(MPC83xx_IRQ_PIT, 1);
+ ipic_set_priority(MPC83xx_IRQ_PCI1, 2);
+ ipic_set_priority(MPC83xx_IRQ_PCI2, 3);
+ ipic_set_priority(MPC83xx_IRQ_EXT0, 4);
+ ipic_set_priority(MPC83xx_IRQ_EXT1, 5);
+ ipic_set_priority(MPC83xx_IRQ_EXT2, 6);
+ ipic_set_priority(MPC83xx_IRQ_EXT3, 7);
+ ipic_set_priority(MPC83xx_IRQ_RTC_ALR, 0);
+ ipic_set_priority(MPC83xx_IRQ_MU, 1);
+ ipic_set_priority(MPC83xx_IRQ_SBA, 2);
+ ipic_set_priority(MPC83xx_IRQ_DMA, 3);
+ ipic_set_priority(MPC83xx_IRQ_EXT4, 4);
+ ipic_set_priority(MPC83xx_IRQ_EXT5, 5);
+ ipic_set_priority(MPC83xx_IRQ_EXT6, 6);
+ ipic_set_priority(MPC83xx_IRQ_EXT7, 7);
+}
+
+void ipic_enable_mcp(enum ipic_mcp_irq mcp_irq)
+{
+ struct ipic *ipic = primary_ipic;
+ u32 temp;
+
+ temp = ipic_read(ipic->regs, IPIC_SERMR);
+ temp |= (1 << (31 - mcp_irq));
+ ipic_write(ipic->regs, IPIC_SERMR, temp);
+}
+
+void ipic_disable_mcp(enum ipic_mcp_irq mcp_irq)
+{
+ struct ipic *ipic = primary_ipic;
+ u32 temp;
+
+ temp = ipic_read(ipic->regs, IPIC_SERMR);
+ temp &= (1 << (31 - mcp_irq));
+ ipic_write(ipic->regs, IPIC_SERMR, temp);
+}
+
+u32 ipic_get_mcp_status(void)
+{
+ return ipic_read(primary_ipic->regs, IPIC_SERMR);
+}
+
+void ipic_clear_mcp_status(u32 mask)
+{
+ ipic_write(primary_ipic->regs, IPIC_SERMR, mask);
+}
+
+/* Return an interrupt vector or -1 if no interrupt is pending. */
+int ipic_get_irq(struct pt_regs *regs)
+{
+ int irq;
+
+ irq = ipic_read(primary_ipic->regs, IPIC_SIVCR) & 0x7f;
+
+ if (irq == 0) /* 0 --> no irq is pending */
+ irq = -1;
+
+ return irq;
+}
+
+static struct sysdev_class ipic_sysclass = {
+ set_kset_name("ipic"),
+};
+
+static struct sys_device device_ipic = {
+ .id = 0,
+ .cls = &ipic_sysclass,
+};
+
+static int __init init_ipic_sysfs(void)
+{
+ int rc;
+
+ if (!primary_ipic->regs)
+ return -ENODEV;
+ printk(KERN_DEBUG "Registering ipic with sysfs...\n");
+
+ rc = sysdev_class_register(&ipic_sysclass);
+ if (rc) {
+ printk(KERN_ERR "Failed registering ipic sys class\n");
+ return -ENODEV;
+ }
+ rc = sysdev_register(&device_ipic);
+ if (rc) {
+ printk(KERN_ERR "Failed registering ipic sys device\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+subsys_initcall(init_ipic_sysfs);
diff --git a/arch/ppc/syslib/ipic.h b/arch/ppc/syslib/ipic.h
new file mode 100644
index 000000000000..2b56a4fcf373
--- /dev/null
+++ b/arch/ppc/syslib/ipic.h
@@ -0,0 +1,49 @@
+/*
+ * arch/ppc/kernel/ipic.h
+ *
+ * IPIC private definitions and structure.
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+#ifndef __IPIC_H__
+#define __IPIC_H__
+
+#include <asm/ipic.h>
+
+#define MPC83xx_IPIC_SIZE (0x00100)
+
+/* System Global Interrupt Configuration Register */
+#define SICFR_IPSA 0x00010000
+#define SICFR_IPSD 0x00080000
+#define SICFR_MPSA 0x00200000
+#define SICFR_MPSB 0x00400000
+
+/* System External Interrupt Mask Register */
+#define SEMSR_SIRQ0 0x00008000
+
+/* System Error Control Register */
+#define SERCR_MCPR 0x00000001
+
+struct ipic {
+ volatile u32 __iomem *regs;
+ unsigned int irq_offset;
+};
+
+struct ipic_info {
+ u8 pend; /* pending register offset from base */
+ u8 mask; /* mask register offset from base */
+ u8 prio; /* priority register offset from base */
+ u8 force; /* force register offset from base */
+ u8 bit; /* register bit position (as per doc)
+ bit mask = 1 << (31 - bit) */
+ u8 prio_mask; /* priority mask value */
+};
+
+#endif /* __IPIC_H__ */
diff --git a/arch/ppc/syslib/m8260_pci.c b/arch/ppc/syslib/m8260_pci.c
new file mode 100644
index 000000000000..bd564fb35ab6
--- /dev/null
+++ b/arch/ppc/syslib/m8260_pci.c
@@ -0,0 +1,194 @@
+/*
+ * (C) Copyright 2003
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/immap_cpm2.h>
+#include <asm/mpc8260.h>
+
+#include "m8260_pci.h"
+
+
+/* PCI bus configuration registers.
+ */
+
+static void __init m8260_setup_pci(struct pci_controller *hose)
+{
+ volatile cpm2_map_t *immap = cpm2_immr;
+ unsigned long pocmr;
+ u16 tempShort;
+
+#ifndef CONFIG_ATC /* already done in U-Boot */
+ /*
+ * Setting required to enable IRQ1-IRQ7 (SIUMCR [DPPC]),
+ * and local bus for PCI (SIUMCR [LBPC]).
+ */
+ immap->im_siu_conf.siu_82xx.sc_siumcr = 0x00640000;
+#endif
+
+ /* Make PCI lowest priority */
+ /* Each 4 bits is a device bus request and the MS 4bits
+ is highest priority */
+ /* Bus 4bit value
+ --- ----------
+ CPM high 0b0000
+ CPM middle 0b0001
+ CPM low 0b0010
+ PCI reguest 0b0011
+ Reserved 0b0100
+ Reserved 0b0101
+ Internal Core 0b0110
+ External Master 1 0b0111
+ External Master 2 0b1000
+ External Master 3 0b1001
+ The rest are reserved */
+ immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x61207893;
+
+ /* Park bus on core while modifying PCI Bus accesses */
+ immap->im_siu_conf.siu_82xx.sc_ppc_acr = 0x6;
+
+ /*
+ * Set up master window that allows the CPU to access PCI space. This
+ * window is set up using the first SIU PCIBR registers.
+ */
+ immap->im_memctl.memc_pcimsk0 = MPC826x_PCI_MASK;
+ immap->im_memctl.memc_pcibr0 = MPC826x_PCI_BASE | PCIBR_ENABLE;
+
+ /* Disable machine check on no response or target abort */
+ immap->im_pci.pci_emr = cpu_to_le32(0x1fe7);
+ /* Release PCI RST (by default the PCI RST signal is held low) */
+ immap->im_pci.pci_gcr = cpu_to_le32(PCIGCR_PCI_BUS_EN);
+
+ /* give it some time */
+ mdelay(1);
+
+ /*
+ * Set up master window that allows the CPU to access PCI Memory (prefetch)
+ * space. This window is set up using the first set of Outbound ATU registers.
+ */
+ immap->im_pci.pci_potar0 = cpu_to_le32(MPC826x_PCI_LOWER_MEM >> 12);
+ immap->im_pci.pci_pobar0 = cpu_to_le32((MPC826x_PCI_LOWER_MEM - MPC826x_PCI_MEM_OFFSET) >> 12);
+ pocmr = ((MPC826x_PCI_UPPER_MEM - MPC826x_PCI_LOWER_MEM) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_pocmr0 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PREFETCH_EN);
+
+ /*
+ * Set up master window that allows the CPU to access PCI Memory (non-prefetch)
+ * space. This window is set up using the second set of Outbound ATU registers.
+ */
+ immap->im_pci.pci_potar1 = cpu_to_le32(MPC826x_PCI_LOWER_MMIO >> 12);
+ immap->im_pci.pci_pobar1 = cpu_to_le32((MPC826x_PCI_LOWER_MMIO - MPC826x_PCI_MMIO_OFFSET) >> 12);
+ pocmr = ((MPC826x_PCI_UPPER_MMIO - MPC826x_PCI_LOWER_MMIO) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_pocmr1 = cpu_to_le32(pocmr | POCMR_ENABLE);
+
+ /*
+ * Set up master window that allows the CPU to access PCI IO space. This window
+ * is set up using the third set of Outbound ATU registers.
+ */
+ immap->im_pci.pci_potar2 = cpu_to_le32(MPC826x_PCI_IO_BASE >> 12);
+ immap->im_pci.pci_pobar2 = cpu_to_le32(MPC826x_PCI_LOWER_IO >> 12);
+ pocmr = ((MPC826x_PCI_UPPER_IO - MPC826x_PCI_LOWER_IO) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_pocmr2 = cpu_to_le32(pocmr | POCMR_ENABLE | POCMR_PCI_IO);
+
+ /*
+ * Set up slave window that allows PCI masters to access MPC826x local memory.
+ * This window is set up using the first set of Inbound ATU registers
+ */
+
+ immap->im_pci.pci_pitar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_LOCAL >> 12);
+ immap->im_pci.pci_pibar0 = cpu_to_le32(MPC826x_PCI_SLAVE_MEM_BUS >> 12);
+ pocmr = ((MPC826x_PCI_SLAVE_MEM_SIZE-1) >> 12) ^ 0xfffff;
+ immap->im_pci.pci_picmr0 = cpu_to_le32(pocmr | PICMR_ENABLE | PICMR_PREFETCH_EN);
+
+ /* See above for description - puts PCI request as highest priority */
+ immap->im_siu_conf.siu_82xx.sc_ppc_alrh = 0x03124567;
+
+ /* Park the bus on the PCI */
+ immap->im_siu_conf.siu_82xx.sc_ppc_acr = PPC_ACR_BUS_PARK_PCI;
+
+ /* Host mode - specify the bridge as a host-PCI bridge */
+ early_write_config_word(hose, 0, 0, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_HOST);
+
+ /* Enable the host bridge to be a master on the PCI bus, and to act as a PCI memory target */
+ early_read_config_word(hose, 0, 0, PCI_COMMAND, &tempShort);
+ early_write_config_word(hose, 0, 0, PCI_COMMAND,
+ tempShort | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY);
+}
+
+void __init m8260_find_bridges(void)
+{
+ extern int pci_assign_all_busses;
+ struct pci_controller * hose;
+
+ pci_assign_all_busses = 1;
+
+ hose = pcibios_alloc_controller();
+
+ if (!hose)
+ return;
+
+ ppc_md.pci_swizzle = common_swizzle;
+
+ hose->first_busno = 0;
+ hose->bus_offset = 0;
+ hose->last_busno = 0xff;
+
+ setup_m8260_indirect_pci(hose,
+ (unsigned long)&cpm2_immr->im_pci.pci_cfg_addr,
+ (unsigned long)&cpm2_immr->im_pci.pci_cfg_data);
+
+ m8260_setup_pci(hose);
+ hose->pci_mem_offset = MPC826x_PCI_MEM_OFFSET;
+
+ isa_io_base =
+ (unsigned long) ioremap(MPC826x_PCI_IO_BASE,
+ MPC826x_PCI_IO_SIZE);
+ hose->io_base_virt = (void *) isa_io_base;
+
+ /* setup resources */
+ pci_init_resource(&hose->mem_resources[0],
+ MPC826x_PCI_LOWER_MEM,
+ MPC826x_PCI_UPPER_MEM,
+ IORESOURCE_MEM|IORESOURCE_PREFETCH, "PCI prefetchable memory");
+
+ pci_init_resource(&hose->mem_resources[1],
+ MPC826x_PCI_LOWER_MMIO,
+ MPC826x_PCI_UPPER_MMIO,
+ IORESOURCE_MEM, "PCI memory");
+
+ pci_init_resource(&hose->io_resource,
+ MPC826x_PCI_LOWER_IO,
+ MPC826x_PCI_UPPER_IO,
+ IORESOURCE_IO, "PCI I/O");
+}
diff --git a/arch/ppc/syslib/m8260_pci.h b/arch/ppc/syslib/m8260_pci.h
new file mode 100644
index 000000000000..d1352120acd7
--- /dev/null
+++ b/arch/ppc/syslib/m8260_pci.h
@@ -0,0 +1,76 @@
+
+#ifndef _PPC_KERNEL_M8260_PCI_H
+#define _PPC_KERNEL_M8260_PCI_H
+
+#include <asm/m8260_pci.h>
+
+/*
+ * Local->PCI map (from CPU) controlled by
+ * MPC826x master window
+ *
+ * 0x80000000 - 0xBFFFFFFF Total CPU2PCI space PCIBR0
+ *
+ * 0x80000000 - 0x9FFFFFFF PCI Mem with prefetch (Outbound ATU #1)
+ * 0xA0000000 - 0xAFFFFFFF PCI Mem w/o prefetch (Outbound ATU #2)
+ * 0xB0000000 - 0xB0FFFFFF 32-bit PCI IO (Outbound ATU #3)
+ *
+ * PCI->Local map (from PCI)
+ * MPC826x slave window controlled by
+ *
+ * 0x00000000 - 0x07FFFFFF MPC826x local memory (Inbound ATU #1)
+ */
+
+/*
+ * Slave window that allows PCI masters to access MPC826x local memory.
+ * This window is set up using the first set of Inbound ATU registers
+ */
+
+#ifndef MPC826x_PCI_SLAVE_MEM_LOCAL
+#define MPC826x_PCI_SLAVE_MEM_LOCAL (((struct bd_info *)__res)->bi_memstart)
+#define MPC826x_PCI_SLAVE_MEM_BUS (((struct bd_info *)__res)->bi_memstart)
+#define MPC826x_PCI_SLAVE_MEM_SIZE (((struct bd_info *)__res)->bi_memsize)
+#endif
+
+/*
+ * This is the window that allows the CPU to access PCI address space.
+ * It will be setup with the SIU PCIBR0 register. All three PCI master
+ * windows, which allow the CPU to access PCI prefetch, non prefetch,
+ * and IO space (see below), must all fit within this window.
+ */
+#ifndef MPC826x_PCI_BASE
+#define MPC826x_PCI_BASE 0x80000000
+#define MPC826x_PCI_MASK 0xc0000000
+#endif
+
+#ifndef MPC826x_PCI_LOWER_MEM
+#define MPC826x_PCI_LOWER_MEM 0x80000000
+#define MPC826x_PCI_UPPER_MEM 0x9fffffff
+#define MPC826x_PCI_MEM_OFFSET 0x00000000
+#endif
+
+#ifndef MPC826x_PCI_LOWER_MMIO
+#define MPC826x_PCI_LOWER_MMIO 0xa0000000
+#define MPC826x_PCI_UPPER_MMIO 0xafffffff
+#define MPC826x_PCI_MMIO_OFFSET 0x00000000
+#endif
+
+#ifndef MPC826x_PCI_LOWER_IO
+#define MPC826x_PCI_LOWER_IO 0x00000000
+#define MPC826x_PCI_UPPER_IO 0x00ffffff
+#define MPC826x_PCI_IO_BASE 0xb0000000
+#define MPC826x_PCI_IO_SIZE 0x01000000
+#endif
+
+#ifndef _IO_BASE
+#define _IO_BASE isa_io_base
+#endif
+
+#ifdef CONFIG_8260_PCI9
+struct pci_controller;
+extern void setup_m8260_indirect_pci(struct pci_controller* hose,
+ u32 cfg_addr, u32 cfg_data);
+#else
+#define setup_m8260_indirect_pci setup_indirect_pci
+#endif
+
+#endif /* _PPC_KERNEL_M8260_PCI_H */
diff --git a/arch/ppc/syslib/m8260_pci_erratum9.c b/arch/ppc/syslib/m8260_pci_erratum9.c
new file mode 100644
index 000000000000..9c0582d639e0
--- /dev/null
+++ b/arch/ppc/syslib/m8260_pci_erratum9.c
@@ -0,0 +1,473 @@
+/*
+ * arch/ppc/platforms/mpc8260_pci9.c
+ *
+ * Workaround for device erratum PCI 9.
+ * See Motorola's "XPC826xA Family Device Errata Reference."
+ * The erratum applies to all 8260 family Hip4 processors. It is scheduled
+ * to be fixed in HiP4 Rev C. Erratum PCI 9 states that a simultaneous PCI
+ * inbound write transaction and PCI outbound read transaction can result in a
+ * bus deadlock. The suggested workaround is to use the IDMA controller to
+ * perform all reads from PCI configuration, memory, and I/O space.
+ *
+ * Author: andy_lowe@mvista.com
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/io.h>
+#include <asm/pci-bridge.h>
+#include <asm/machdep.h>
+#include <asm/byteorder.h>
+#include <asm/mpc8260.h>
+#include <asm/immap_cpm2.h>
+#include <asm/cpm2.h>
+
+#include "m8260_pci.h"
+
+#ifdef CONFIG_8260_PCI9
+/*#include <asm/mpc8260_pci9.h>*/ /* included in asm/io.h */
+
+#define IDMA_XFER_BUF_SIZE 64 /* size of the IDMA transfer buffer */
+
+/* define a structure for the IDMA dpram usage */
+typedef struct idma_dpram_s {
+ idma_t pram; /* IDMA parameter RAM */
+ u_char xfer_buf[IDMA_XFER_BUF_SIZE]; /* IDMA transfer buffer */
+ idma_bd_t bd; /* buffer descriptor */
+} idma_dpram_t;
+
+/* define offsets relative to start of IDMA dpram */
+#define IDMA_XFER_BUF_OFFSET (sizeof(idma_t))
+#define IDMA_BD_OFFSET (sizeof(idma_t) + IDMA_XFER_BUF_SIZE)
+
+/* define globals */
+static volatile idma_dpram_t *idma_dpram;
+
+/* Exactly one of CONFIG_8260_PCI9_IDMAn must be defined,
+ * where n is 1, 2, 3, or 4. This selects the IDMA channel used for
+ * the PCI9 workaround.
+ */
+#ifdef CONFIG_8260_PCI9_IDMA1
+#define IDMA_CHAN 0
+#define PROFF_IDMA PROFF_IDMA1_BASE
+#define IDMA_PAGE CPM_CR_IDMA1_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA1_SBLOCK
+#endif
+#ifdef CONFIG_8260_PCI9_IDMA2
+#define IDMA_CHAN 1
+#define PROFF_IDMA PROFF_IDMA2_BASE
+#define IDMA_PAGE CPM_CR_IDMA2_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA2_SBLOCK
+#endif
+#ifdef CONFIG_8260_PCI9_IDMA3
+#define IDMA_CHAN 2
+#define PROFF_IDMA PROFF_IDMA3_BASE
+#define IDMA_PAGE CPM_CR_IDMA3_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA3_SBLOCK
+#endif
+#ifdef CONFIG_8260_PCI9_IDMA4
+#define IDMA_CHAN 3
+#define PROFF_IDMA PROFF_IDMA4_BASE
+#define IDMA_PAGE CPM_CR_IDMA4_PAGE
+#define IDMA_SBLOCK CPM_CR_IDMA4_SBLOCK
+#endif
+
+void idma_pci9_init(void)
+{
+ uint dpram_offset;
+ volatile idma_t *pram;
+ volatile im_idma_t *idma_reg;
+ volatile cpm2_map_t *immap = cpm2_immr;
+
+ /* allocate IDMA dpram */
+ dpram_offset = cpm_dpalloc(sizeof(idma_dpram_t), 64);
+ idma_dpram = cpm_dpram_addr(dpram_offset);
+
+ /* initialize the IDMA parameter RAM */
+ memset((void *)idma_dpram, 0, sizeof(idma_dpram_t));
+ pram = &idma_dpram->pram;
+ pram->ibase = dpram_offset + IDMA_BD_OFFSET;
+ pram->dpr_buf = dpram_offset + IDMA_XFER_BUF_OFFSET;
+ pram->ss_max = 32;
+ pram->dts = 32;
+
+ /* initialize the IDMA_BASE pointer to the IDMA parameter RAM */
+ *((ushort *) &immap->im_dprambase[PROFF_IDMA]) = dpram_offset;
+
+ /* initialize the IDMA registers */
+ idma_reg = (volatile im_idma_t *) &immap->im_sdma.sdma_idsr1;
+ idma_reg[IDMA_CHAN].idmr = 0; /* mask all IDMA interrupts */
+ idma_reg[IDMA_CHAN].idsr = 0xff; /* clear all event flags */
+
+ printk("<4>Using IDMA%d for MPC8260 device erratum PCI 9 workaround\n",
+ IDMA_CHAN + 1);
+
+ return;
+}
+
+/* Use the IDMA controller to transfer data from I/O memory to local RAM.
+ * The src address must be a physical address suitable for use by the DMA
+ * controller with no translation. The dst address must be a kernel virtual
+ * address. The dst address is translated to a physical address via
+ * virt_to_phys().
+ * The sinc argument specifies whether or not the source address is incremented
+ * by the DMA controller. The source address is incremented if and only if sinc
+ * is non-zero. The destination address is always incremented since the
+ * destination is always host RAM.
+ */
+static void
+idma_pci9_read(u8 *dst, u8 *src, int bytes, int unit_size, int sinc)
+{
+ unsigned long flags;
+ volatile idma_t *pram = &idma_dpram->pram;
+ volatile idma_bd_t *bd = &idma_dpram->bd;
+ volatile cpm2_map_t *immap = cpm2_immr;
+
+ local_irq_save(flags);
+
+ /* initialize IDMA parameter RAM for this transfer */
+ if (sinc)
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
+ | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;
+ else
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_DINC
+ | IDMA_DCM_SD_MEM2MEM;
+ pram->ibdptr = pram->ibase;
+ pram->sts = unit_size;
+ pram->istate = 0;
+
+ /* initialize the buffer descriptor */
+ bd->dst = virt_to_phys(dst);
+ bd->src = (uint) src;
+ bd->len = bytes;
+ bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL
+ | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;
+
+ /* issue the START_IDMA command to the CP */
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+ immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,
+ CPM_CR_START_IDMA) | CPM_CR_FLG;
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+
+ /* wait for transfer to complete */
+ while(bd->flags & IDMA_BD_V);
+
+ local_irq_restore(flags);
+
+ return;
+}
+
+/* Use the IDMA controller to transfer data from I/O memory to local RAM.
+ * The dst address must be a physical address suitable for use by the DMA
+ * controller with no translation. The src address must be a kernel virtual
+ * address. The src address is translated to a physical address via
+ * virt_to_phys().
+ * The dinc argument specifies whether or not the dest address is incremented
+ * by the DMA controller. The source address is incremented if and only if sinc
+ * is non-zero. The source address is always incremented since the
+ * source is always host RAM.
+ */
+static void
+idma_pci9_write(u8 *dst, u8 *src, int bytes, int unit_size, int dinc)
+{
+ unsigned long flags;
+ volatile idma_t *pram = &idma_dpram->pram;
+ volatile idma_bd_t *bd = &idma_dpram->bd;
+ volatile cpm2_map_t *immap = cpm2_immr;
+
+ local_irq_save(flags);
+
+ /* initialize IDMA parameter RAM for this transfer */
+ if (dinc)
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
+ | IDMA_DCM_DINC | IDMA_DCM_SD_MEM2MEM;
+ else
+ pram->dcm = IDMA_DCM_DMA_WRAP_64 | IDMA_DCM_SINC
+ | IDMA_DCM_SD_MEM2MEM;
+ pram->ibdptr = pram->ibase;
+ pram->sts = unit_size;
+ pram->istate = 0;
+
+ /* initialize the buffer descriptor */
+ bd->dst = (uint) dst;
+ bd->src = virt_to_phys(src);
+ bd->len = bytes;
+ bd->flags = IDMA_BD_V | IDMA_BD_W | IDMA_BD_I | IDMA_BD_L | IDMA_BD_DGBL
+ | IDMA_BD_DBO_BE | IDMA_BD_SBO_BE | IDMA_BD_SDTB;
+
+ /* issue the START_IDMA command to the CP */
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+ immap->im_cpm.cp_cpcr = mk_cr_cmd(IDMA_PAGE, IDMA_SBLOCK, 0,
+ CPM_CR_START_IDMA) | CPM_CR_FLG;
+ while (immap->im_cpm.cp_cpcr & CPM_CR_FLG);
+
+ /* wait for transfer to complete */
+ while(bd->flags & IDMA_BD_V);
+
+ local_irq_restore(flags);
+
+ return;
+}
+
+/* Same as idma_pci9_read, but 16-bit little-endian byte swapping is performed
+ * if the unit_size is 2, and 32-bit little-endian byte swapping is performed if
+ * the unit_size is 4.
+ */
+static void
+idma_pci9_read_le(u8 *dst, u8 *src, int bytes, int unit_size, int sinc)
+{
+ int i;
+ u8 *p;
+
+ idma_pci9_read(dst, src, bytes, unit_size, sinc);
+ switch(unit_size) {
+ case 2:
+ for (i = 0, p = dst; i < bytes; i += 2, p += 2)
+ swab16s((u16 *) p);
+ break;
+ case 4:
+ for (i = 0, p = dst; i < bytes; i += 4, p += 4)
+ swab32s((u32 *) p);
+ break;
+ default:
+ break;
+ }
+}
+EXPORT_SYMBOL(idma_pci9_init);
+EXPORT_SYMBOL(idma_pci9_read);
+EXPORT_SYMBOL(idma_pci9_read_le);
+
+static inline int is_pci_mem(unsigned long addr)
+{
+ if (addr >= MPC826x_PCI_LOWER_MMIO &&
+ addr <= MPC826x_PCI_UPPER_MMIO)
+ return 1;
+ if (addr >= MPC826x_PCI_LOWER_MEM &&
+ addr <= MPC826x_PCI_UPPER_MEM)
+ return 1;
+ return 0;
+}
+
+#define is_pci_mem(pa) ( (pa > 0x80000000) && (pa < 0xc0000000))
+int readb(volatile unsigned char *addr)
+{
+ u8 val;
+ unsigned long pa = iopa((unsigned long) addr);
+
+ if (!is_pci_mem(pa))
+ return in_8(addr);
+
+ idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
+ return val;
+}
+
+int readw(volatile unsigned short *addr)
+{
+ u16 val;
+ unsigned long pa = iopa((unsigned long) addr);
+
+ if (!is_pci_mem(pa))
+ return in_le16(addr);
+
+ idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
+ return swab16(val);
+}
+
+unsigned readl(volatile unsigned *addr)
+{
+ u32 val;
+ unsigned long pa = iopa((unsigned long) addr);
+
+ if (!is_pci_mem(pa))
+ return in_le32(addr);
+
+ idma_pci9_read((u8 *)&val, (u8 *)pa, sizeof(val), sizeof(val), 0);
+ return swab32(val);
+}
+
+int inb(unsigned port)
+{
+ u8 val;
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
+ return val;
+}
+
+int inw(unsigned port)
+{
+ u16 val;
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
+ return swab16(val);
+}
+
+unsigned inl(unsigned port)
+{
+ u32 val;
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)&val, (u8 *)addr, sizeof(val), sizeof(val), 0);
+ return swab32(val);
+}
+
+void insb(unsigned port, void *buf, int ns)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u8), sizeof(u8), 0);
+}
+
+void insw(unsigned port, void *buf, int ns)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);
+}
+
+void insl(unsigned port, void *buf, int nl)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);
+}
+
+void insw_ns(unsigned port, void *buf, int ns)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, ns*sizeof(u16), sizeof(u16), 0);
+}
+
+void insl_ns(unsigned port, void *buf, int nl)
+{
+ u8 *addr = (u8 *)(port + _IO_BASE);
+
+ idma_pci9_read((u8 *)buf, (u8 *)addr, nl*sizeof(u32), sizeof(u32), 0);
+}
+
+void *memcpy_fromio(void *dest, unsigned long src, size_t count)
+{
+ unsigned long pa = iopa((unsigned long) src);
+
+ if (is_pci_mem(pa))
+ idma_pci9_read((u8 *)dest, (u8 *)pa, count, 32, 1);
+ else
+ memcpy(dest, (void *)src, count);
+ return dest;
+}
+
+EXPORT_SYMBOL(readb);
+EXPORT_SYMBOL(readw);
+EXPORT_SYMBOL(readl);
+EXPORT_SYMBOL(inb);
+EXPORT_SYMBOL(inw);
+EXPORT_SYMBOL(inl);
+EXPORT_SYMBOL(insb);
+EXPORT_SYMBOL(insw);
+EXPORT_SYMBOL(insl);
+EXPORT_SYMBOL(insw_ns);
+EXPORT_SYMBOL(insl_ns);
+EXPORT_SYMBOL(memcpy_fromio);
+
+#endif /* ifdef CONFIG_8260_PCI9 */
+
+/* Indirect PCI routines adapted from arch/ppc/kernel/indirect_pci.c.
+ * Copyright (C) 1998 Gabriel Paubert.
+ */
+#ifndef CONFIG_8260_PCI9
+#define cfg_read(val, addr, type, op) *val = op((type)(addr))
+#else
+#define cfg_read(val, addr, type, op) \
+ idma_pci9_read_le((u8*)(val),(u8*)(addr),sizeof(*(val)),sizeof(*(val)),0)
+#endif
+
+#define cfg_write(val, addr, type, op) op((type *)(addr), (val))
+
+static int indirect_write_config(struct pci_bus *pbus, unsigned int devfn, int where,
+ int size, u32 value)
+{
+ struct pci_controller *hose = pbus->sysdata;
+ u8 cfg_type = 0;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(pbus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (pbus->number != hose->first_busno)
+ cfg_type = 1;
+
+ out_be32(hose->cfg_addr,
+ (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
+ | ((pbus->number - hose->bus_offset) << 8) | 0x80);
+
+ switch (size)
+ {
+ case 1:
+ cfg_write(value, hose->cfg_data + (where & 3), u8, out_8);
+ break;
+ case 2:
+ cfg_write(value, hose->cfg_data + (where & 2), u16, out_le16);
+ break;
+ case 4:
+ cfg_write(value, hose->cfg_data + (where & 0), u32, out_le32);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int indirect_read_config(struct pci_bus *pbus, unsigned int devfn, int where,
+ int size, u32 *value)
+{
+ struct pci_controller *hose = pbus->sysdata;
+ u8 cfg_type = 0;
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(pbus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ if (hose->set_cfg_type)
+ if (pbus->number != hose->first_busno)
+ cfg_type = 1;
+
+ out_be32(hose->cfg_addr,
+ (((where & 0xfc) | cfg_type) << 24) | (devfn << 16)
+ | ((pbus->number - hose->bus_offset) << 8) | 0x80);
+
+ switch (size)
+ {
+ case 1:
+ cfg_read(value, hose->cfg_data + (where & 3), u8 *, in_8);
+ break;
+ case 2:
+ cfg_read(value, hose->cfg_data + (where & 2), u16 *, in_le16);
+ break;
+ case 4:
+ cfg_read(value, hose->cfg_data + (where & 0), u32 *, in_le32);
+ break;
+ }
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops indirect_pci_ops =
+{
+ .read = indirect_read_config,
+ .write = indirect_write_config,
+};
+
+void
+setup_m8260_indirect_pci(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
+{
+ hose->ops = &indirect_pci_ops;
+ hose->cfg_addr = (unsigned int *) ioremap(cfg_addr, 4);
+ hose->cfg_data = (unsigned char *) ioremap(cfg_data, 4);
+}
diff --git a/arch/ppc/syslib/m8260_setup.c b/arch/ppc/syslib/m8260_setup.c
new file mode 100644
index 000000000000..23ea3f694de2
--- /dev/null
+++ b/arch/ppc/syslib/m8260_setup.c
@@ -0,0 +1,264 @@
+/*
+ * arch/ppc/syslib/m8260_setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
+ * Further modified for generic 8xx and 8260 by Dan.
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
+#include <linux/seq_file.h>
+#include <linux/irq.h>
+
+#include <asm/mmu.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8260.h>
+#include <asm/immap_cpm2.h>
+#include <asm/machdep.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+
+#include "cpm2_pic.h"
+
+unsigned char __res[sizeof(bd_t)];
+
+extern void cpm2_reset(void);
+extern void m8260_find_bridges(void);
+extern void idma_pci9_init(void);
+
+/* Place-holder for board-specific init */
+void __attribute__ ((weak)) __init
+m82xx_board_setup(void)
+{
+}
+
+static void __init
+m8260_setup_arch(void)
+{
+ /* Print out Vendor and Machine info. */
+ printk(KERN_INFO "%s %s port\n", CPUINFO_VENDOR, CPUINFO_MACHINE);
+
+ /* Reset the Communication Processor Module. */
+ cpm2_reset();
+#ifdef CONFIG_8260_PCI9
+ /* Initialise IDMA for PCI erratum workaround */
+ idma_pci9_init();
+#endif
+#ifdef CONFIG_PCI_8260
+ m8260_find_bridges();
+#endif
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (initrd_start)
+ ROOT_DEV = Root_RAM0;
+#endif
+ m82xx_board_setup();
+}
+
+/* The decrementer counts at the system (internal) clock frequency
+ * divided by four.
+ */
+static void __init
+m8260_calibrate_decr(void)
+{
+ bd_t *binfo = (bd_t *)__res;
+ int freq, divisor;
+
+ freq = binfo->bi_busfreq;
+ divisor = 4;
+ tb_ticks_per_jiffy = freq / HZ / divisor;
+ tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
+}
+
+/* The 8260 has an internal 1-second timer update register that
+ * we should use for this purpose.
+ */
+static uint rtc_time;
+
+static int
+m8260_set_rtc_time(unsigned long time)
+{
+ rtc_time = time;
+
+ return(0);
+}
+
+static unsigned long
+m8260_get_rtc_time(void)
+{
+ /* Get time from the RTC.
+ */
+ return((unsigned long)rtc_time);
+}
+
+#ifndef BOOTROM_RESTART_ADDR
+#warning "Using default BOOTROM_RESTART_ADDR!"
+#define BOOTROM_RESTART_ADDR 0xff000104
+#endif
+
+static void
+m8260_restart(char *cmd)
+{
+ extern void m8260_gorom(bd_t *bi, uint addr);
+ uint startaddr;
+
+ /* Most boot roms have a warmstart as the second instruction
+ * of the reset vector. If that doesn't work for you, change this
+ * or the reboot program to send a proper address.
+ */
+ startaddr = BOOTROM_RESTART_ADDR;
+ if (cmd != NULL) {
+ if (!strncmp(cmd, "startaddr=", 10))
+ startaddr = simple_strtoul(&cmd[10], NULL, 0);
+ }
+
+ m8260_gorom((void*)__pa(__res), startaddr);
+}
+
+static void
+m8260_halt(void)
+{
+ local_irq_disable();
+ while (1);
+}
+
+static void
+m8260_power_off(void)
+{
+ m8260_halt();
+}
+
+static int
+m8260_show_cpuinfo(struct seq_file *m)
+{
+ bd_t *bp = (bd_t *)__res;
+
+ seq_printf(m, "vendor\t\t: %s\n"
+ "machine\t\t: %s\n"
+ "\n"
+ "mem size\t\t: 0x%08x\n"
+ "console baud\t\t: %d\n"
+ "\n"
+ "core clock\t: %u MHz\n"
+ "CPM clock\t: %u MHz\n"
+ "bus clock\t: %u MHz\n",
+ CPUINFO_VENDOR, CPUINFO_MACHINE, bp->bi_memsize,
+ bp->bi_baudrate, bp->bi_intfreq / 1000000,
+ bp->bi_cpmfreq / 1000000, bp->bi_busfreq / 1000000);
+ return 0;
+}
+
+/* Initialize the internal interrupt controller. The number of
+ * interrupts supported can vary with the processor type, and the
+ * 8260 family can have up to 64.
+ * External interrupts can be either edge or level triggered, and
+ * need to be initialized by the appropriate driver.
+ */
+static void __init
+m8260_init_IRQ(void)
+{
+ cpm2_init_IRQ();
+
+ /* Initialize the default interrupt mapping priorities,
+ * in case the boot rom changed something on us.
+ */
+ cpm2_immr->im_intctl.ic_siprr = 0x05309770;
+}
+
+/*
+ * Same hack as 8xx
+ */
+static unsigned long __init
+m8260_find_end_of_memory(void)
+{
+ bd_t *binfo = (bd_t *)__res;
+
+ return binfo->bi_memsize;
+}
+
+/* Map the IMMR, plus anything else we can cover
+ * in that upper space according to the memory controller
+ * chip select mapping. Grab another bunch of space
+ * below that for stuff we can't cover in the upper.
+ */
+static void __init
+m8260_map_io(void)
+{
+ uint addr;
+
+ /* Map IMMR region to a 256MB BAT */
+ addr = (cpm2_immr != NULL) ? (uint)cpm2_immr : CPM_MAP_ADDR;
+ io_block_mapping(addr, addr, 0x10000000, _PAGE_IO);
+
+ /* Map I/O region to a 256MB BAT */
+ io_block_mapping(IO_VIRT_ADDR, IO_PHYS_ADDR, 0x10000000, _PAGE_IO);
+}
+
+/* Place-holder for board-specific ppc_md hooking */
+void __attribute__ ((weak)) __init
+m82xx_board_init(void)
+{
+}
+
+/* Inputs:
+ * r3 - Optional pointer to a board information structure.
+ * r4 - Optional pointer to the physical starting address of the init RAM
+ * disk.
+ * r5 - Optional pointer to the physical ending address of the init RAM
+ * disk.
+ * r6 - Optional pointer to the physical starting address of any kernel
+ * command-line parameters.
+ * r7 - Optional pointer to the physical ending address of any kernel
+ * command-line parameters.
+ */
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ parse_bootinfo(find_bootinfo());
+
+ if ( r3 )
+ memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* take care of initrd if we have one */
+ if ( r4 ) {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+ /* take care of cmd line */
+ if ( r6 ) {
+ *(char *)(r7+KERNELBASE) = 0;
+ strcpy(cmd_line, (char *)(r6+KERNELBASE));
+ }
+
+ ppc_md.setup_arch = m8260_setup_arch;
+ ppc_md.show_cpuinfo = m8260_show_cpuinfo;
+ ppc_md.init_IRQ = m8260_init_IRQ;
+ ppc_md.get_irq = cpm2_get_irq;
+
+ ppc_md.restart = m8260_restart;
+ ppc_md.power_off = m8260_power_off;
+ ppc_md.halt = m8260_halt;
+
+ ppc_md.set_rtc_time = m8260_set_rtc_time;
+ ppc_md.get_rtc_time = m8260_get_rtc_time;
+ ppc_md.calibrate_decr = m8260_calibrate_decr;
+
+ ppc_md.find_end_of_memory = m8260_find_end_of_memory;
+ ppc_md.setup_io_mappings = m8260_map_io;
+
+ /* Call back for board-specific settings and overrides. */
+ m82xx_board_init();
+}
diff --git a/arch/ppc/syslib/m8xx_setup.c b/arch/ppc/syslib/m8xx_setup.c
new file mode 100644
index 000000000000..c1db2ab1d154
--- /dev/null
+++ b/arch/ppc/syslib/m8xx_setup.c
@@ -0,0 +1,433 @@
+/*
+ * arch/ppc/kernel/setup.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Adapted from 'alpha' version by Gary Thomas
+ * Modified by Cort Dougan (cort@cs.nmt.edu)
+ * Modified for MBX using prep/chrp/pmac functions by Dan (dmalek@jlc.net)
+ * Further modified for generic 8xx by Dan.
+ */
+
+/*
+ * bootup setup stuff..
+ */
+
+#include <linux/config.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/major.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/initrd.h>
+#include <linux/ioport.h>
+#include <linux/bootmem.h>
+#include <linux/seq_file.h>
+#include <linux/root_dev.h>
+
+#include <asm/mmu.h>
+#include <asm/reg.h>
+#include <asm/residual.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/mpc8xx.h>
+#include <asm/8xx_immap.h>
+#include <asm/machdep.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+#include <asm/xmon.h>
+
+#include "ppc8xx_pic.h"
+
+static int m8xx_set_rtc_time(unsigned long time);
+static unsigned long m8xx_get_rtc_time(void);
+void m8xx_calibrate_decr(void);
+
+unsigned char __res[sizeof(bd_t)];
+
+extern void m8xx_ide_init(void);
+
+extern unsigned long find_available_memory(void);
+extern void m8xx_cpm_reset(uint cpm_page);
+extern void m8xx_wdt_handler_install(bd_t *bp);
+extern void rpxfb_alloc_pages(void);
+extern void cpm_interrupt_init(void);
+
+void __attribute__ ((weak))
+board_init(void)
+{
+}
+
+void __init
+m8xx_setup_arch(void)
+{
+ int cpm_page;
+
+ cpm_page = (int) alloc_bootmem_pages(PAGE_SIZE);
+
+ /* Reset the Communication Processor Module.
+ */
+ m8xx_cpm_reset(cpm_page);
+
+#ifdef CONFIG_FB_RPX
+ rpxfb_alloc_pages();
+#endif
+
+#ifdef notdef
+ ROOT_DEV = Root_HDA1; /* hda1 */
+#endif
+
+#ifdef CONFIG_BLK_DEV_INITRD
+#if 0
+ ROOT_DEV = Root_FD0; /* floppy */
+ rd_prompt = 1;
+ rd_doload = 1;
+ rd_image_start = 0;
+#endif
+#if 0 /* XXX this may need to be updated for the new bootmem stuff,
+ or possibly just deleted (see set_phys_avail() in init.c).
+ - paulus. */
+ /* initrd_start and size are setup by boot/head.S and kernel/head.S */
+ if ( initrd_start )
+ {
+ if (initrd_end > *memory_end_p)
+ {
+ printk("initrd extends beyond end of memory "
+ "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
+ initrd_end,*memory_end_p);
+ initrd_start = 0;
+ }
+ }
+#endif
+#endif
+ board_init();
+}
+
+void
+abort(void)
+{
+#ifdef CONFIG_XMON
+ xmon(0);
+#endif
+ machine_restart(NULL);
+
+ /* not reached */
+ for (;;);
+}
+
+/* A place holder for time base interrupts, if they are ever enabled. */
+irqreturn_t timebase_interrupt(int irq, void * dev, struct pt_regs * regs)
+{
+ printk ("timebase_interrupt()\n");
+
+ return IRQ_HANDLED;
+}
+
+static struct irqaction tbint_irqaction = {
+ .handler = timebase_interrupt,
+ .mask = CPU_MASK_NONE,
+ .name = "tbint",
+};
+
+/* The decrementer counts at the system (internal) clock frequency divided by
+ * sixteen, or external oscillator divided by four. We force the processor
+ * to use system clock divided by sixteen.
+ */
+void __init m8xx_calibrate_decr(void)
+{
+ bd_t *binfo = (bd_t *)__res;
+ int freq, fp, divisor;
+
+ /* Unlock the SCCR. */
+ ((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = ~KAPWR_KEY;
+ ((volatile immap_t *)IMAP_ADDR)->im_clkrstk.cark_sccrk = KAPWR_KEY;
+
+ /* Force all 8xx processors to use divide by 16 processor clock. */
+ ((volatile immap_t *)IMAP_ADDR)->im_clkrst.car_sccr |= 0x02000000;
+
+ /* Processor frequency is MHz.
+ * The value 'fp' is the number of decrementer ticks per second.
+ */
+ fp = binfo->bi_intfreq / 16;
+ freq = fp*60; /* try to make freq/1e6 an integer */
+ divisor = 60;
+ printk("Decrementer Frequency = %d/%d\n", freq, divisor);
+ tb_ticks_per_jiffy = freq / HZ / divisor;
+ tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
+
+ /* Perform some more timer/timebase initialization. This used
+ * to be done elsewhere, but other changes caused it to get
+ * called more than once....that is a bad thing.
+ *
+ * First, unlock all of the registers we are going to modify.
+ * To protect them from corruption during power down, registers
+ * that are maintained by keep alive power are "locked". To
+ * modify these registers we have to write the key value to
+ * the key location associated with the register.
+ * Some boards power up with these unlocked, while others
+ * are locked. Writing anything (including the unlock code?)
+ * to the unlocked registers will lock them again. So, here
+ * we guarantee the registers are locked, then we unlock them
+ * for our use.
+ */
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = ~KAPWR_KEY;
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = ~KAPWR_KEY;
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = ~KAPWR_KEY;
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbscrk = KAPWR_KEY;
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtcsck = KAPWR_KEY;
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_tbk = KAPWR_KEY;
+
+ /* Disable the RTC one second and alarm interrupts. */
+ ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc &=
+ ~(RTCSC_SIE | RTCSC_ALE);
+ /* Enable the RTC */
+ ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtcsc |=
+ (RTCSC_RTF | RTCSC_RTE);
+
+ /* Enabling the decrementer also enables the timebase interrupts
+ * (or from the other point of view, to get decrementer interrupts
+ * we have to enable the timebase). The decrementer interrupt
+ * is wired into the vector table, nothing to do here for that.
+ */
+ ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_tbscr =
+ ((mk_int_int_mask(DEC_INTERRUPT) << 8) |
+ (TBSCR_TBF | TBSCR_TBE));
+
+ if (setup_irq(DEC_INTERRUPT, &tbint_irqaction))
+ panic("Could not allocate timer IRQ!");
+
+#ifdef CONFIG_8xx_WDT
+ /* Install watchdog timer handler early because it might be
+ * already enabled by the bootloader
+ */
+ m8xx_wdt_handler_install(binfo);
+#endif
+}
+
+/* The RTC on the MPC8xx is an internal register.
+ * We want to protect this during power down, so we need to unlock,
+ * modify, and re-lock.
+ */
+static int
+m8xx_set_rtc_time(unsigned long time)
+{
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = KAPWR_KEY;
+ ((volatile immap_t *)IMAP_ADDR)->im_sit.sit_rtc = time;
+ ((volatile immap_t *)IMAP_ADDR)->im_sitk.sitk_rtck = ~KAPWR_KEY;
+ return(0);
+}
+
+static unsigned long
+m8xx_get_rtc_time(void)
+{
+ /* Get time from the RTC. */
+ return((unsigned long)(((immap_t *)IMAP_ADDR)->im_sit.sit_rtc));
+}
+
+static void
+m8xx_restart(char *cmd)
+{
+ __volatile__ unsigned char dummy;
+
+ local_irq_disable();
+ ((immap_t *)IMAP_ADDR)->im_clkrst.car_plprcr |= 0x00000080;
+
+ /* Clear the ME bit in MSR to cause checkstop on machine check
+ */
+ mtmsr(mfmsr() & ~0x1000);
+
+ dummy = ((immap_t *)IMAP_ADDR)->im_clkrst.res[0];
+ printk("Restart failed\n");
+ while(1);
+}
+
+static void
+m8xx_power_off(void)
+{
+ m8xx_restart(NULL);
+}
+
+static void
+m8xx_halt(void)
+{
+ m8xx_restart(NULL);
+}
+
+
+static int
+m8xx_show_percpuinfo(struct seq_file *m, int i)
+{
+ bd_t *bp;
+
+ bp = (bd_t *)__res;
+
+ seq_printf(m, "clock\t\t: %ldMHz\n"
+ "bus clock\t: %ldMHz\n",
+ bp->bi_intfreq / 1000000,
+ bp->bi_busfreq / 1000000);
+
+ return 0;
+}
+
+#ifdef CONFIG_PCI
+static struct irqaction mbx_i8259_irqaction = {
+ .handler = mbx_i8259_action,
+ .mask = CPU_MASK_NONE,
+ .name = "i8259 cascade",
+};
+#endif
+
+/* Initialize the internal interrupt controller. The number of
+ * interrupts supported can vary with the processor type, and the
+ * 82xx family can have up to 64.
+ * External interrupts can be either edge or level triggered, and
+ * need to be initialized by the appropriate driver.
+ */
+static void __init
+m8xx_init_IRQ(void)
+{
+ int i;
+
+ for (i = SIU_IRQ_OFFSET ; i < SIU_IRQ_OFFSET + NR_SIU_INTS ; i++)
+ irq_desc[i].handler = &ppc8xx_pic;
+
+ cpm_interrupt_init();
+
+#if defined(CONFIG_PCI)
+ for (i = I8259_IRQ_OFFSET ; i < I8259_IRQ_OFFSET + NR_8259_INTS ; i++)
+ irq_desc[i].handler = &i8259_pic;
+
+ i8259_pic_irq_offset = I8259_IRQ_OFFSET;
+ i8259_init(0);
+
+ /* The i8259 cascade interrupt must be level sensitive. */
+ ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel &=
+ ~(0x80000000 >> ISA_BRIDGE_INT);
+
+ if (setup_irq(ISA_BRIDGE_INT, &mbx_i8259_irqaction))
+ enable_irq(ISA_BRIDGE_INT);
+#endif /* CONFIG_PCI */
+}
+
+/* -------------------------------------------------------------------- */
+
+/*
+ * This is a big hack right now, but it may turn into something real
+ * someday.
+ *
+ * For the 8xx boards (at this time anyway), there is nothing to initialize
+ * associated the PROM. Rather than include all of the prom.c
+ * functions in the image just to get prom_init, all we really need right
+ * now is the initialization of the physical memory region.
+ */
+static unsigned long __init
+m8xx_find_end_of_memory(void)
+{
+ bd_t *binfo;
+ extern unsigned char __res[];
+
+ binfo = (bd_t *)__res;
+
+ return binfo->bi_memsize;
+}
+
+/*
+ * Now map in some of the I/O space that is generically needed
+ * or shared with multiple devices.
+ * All of this fits into the same 4Mbyte region, so it only
+ * requires one page table page. (or at least it used to -- paulus)
+ */
+static void __init
+m8xx_map_io(void)
+{
+ io_block_mapping(IMAP_ADDR, IMAP_ADDR, IMAP_SIZE, _PAGE_IO);
+#ifdef CONFIG_MBX
+ io_block_mapping(NVRAM_ADDR, NVRAM_ADDR, NVRAM_SIZE, _PAGE_IO);
+ io_block_mapping(MBX_CSR_ADDR, MBX_CSR_ADDR, MBX_CSR_SIZE, _PAGE_IO);
+ io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO);
+
+ /* Map some of the PCI/ISA I/O space to get the IDE interface.
+ */
+ io_block_mapping(PCI_ISA_IO_ADDR, PCI_ISA_IO_ADDR, 0x4000, _PAGE_IO);
+ io_block_mapping(PCI_IDE_ADDR, PCI_IDE_ADDR, 0x4000, _PAGE_IO);
+#endif
+#if defined(CONFIG_RPXLITE) || defined(CONFIG_RPXCLASSIC)
+ io_block_mapping(RPX_CSR_ADDR, RPX_CSR_ADDR, RPX_CSR_SIZE, _PAGE_IO);
+#if !defined(CONFIG_PCI)
+ io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO);
+#endif
+#endif
+#if defined(CONFIG_HTDMSOUND) || defined(CONFIG_RPXTOUCH) || defined(CONFIG_FB_RPX)
+ io_block_mapping(HIOX_CSR_ADDR, HIOX_CSR_ADDR, HIOX_CSR_SIZE, _PAGE_IO);
+#endif
+#ifdef CONFIG_FADS
+ io_block_mapping(BCSR_ADDR, BCSR_ADDR, BCSR_SIZE, _PAGE_IO);
+#endif
+#ifdef CONFIG_PCI
+ io_block_mapping(PCI_CSR_ADDR, PCI_CSR_ADDR, PCI_CSR_SIZE, _PAGE_IO);
+#endif
+#if defined(CONFIG_NETTA)
+ io_block_mapping(_IO_BASE,_IO_BASE,_IO_BASE_SIZE, _PAGE_IO);
+#endif
+}
+
+void __init
+platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ parse_bootinfo(find_bootinfo());
+
+ if ( r3 )
+ memcpy( (void *)__res,(void *)(r3+KERNELBASE), sizeof(bd_t) );
+
+#ifdef CONFIG_PCI
+ m8xx_setup_pci_ptrs();
+#endif
+
+#ifdef CONFIG_BLK_DEV_INITRD
+ /* take care of initrd if we have one */
+ if ( r4 )
+ {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+ /* take care of cmd line */
+ if ( r6 )
+ {
+ *(char *)(r7+KERNELBASE) = 0;
+ strcpy(cmd_line, (char *)(r6+KERNELBASE));
+ }
+
+ ppc_md.setup_arch = m8xx_setup_arch;
+ ppc_md.show_percpuinfo = m8xx_show_percpuinfo;
+ ppc_md.irq_canonicalize = NULL;
+ ppc_md.init_IRQ = m8xx_init_IRQ;
+ ppc_md.get_irq = m8xx_get_irq;
+ ppc_md.init = NULL;
+
+ ppc_md.restart = m8xx_restart;
+ ppc_md.power_off = m8xx_power_off;
+ ppc_md.halt = m8xx_halt;
+
+ ppc_md.time_init = NULL;
+ ppc_md.set_rtc_time = m8xx_set_rtc_time;
+ ppc_md.get_rtc_time = m8xx_get_rtc_time;
+ ppc_md.calibrate_decr = m8xx_calibrate_decr;
+
+ ppc_md.find_end_of_memory = m8xx_find_end_of_memory;
+ ppc_md.setup_io_mappings = m8xx_map_io;
+
+#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
+ m8xx_ide_init();
+#endif
+}
diff --git a/arch/ppc/syslib/m8xx_wdt.c b/arch/ppc/syslib/m8xx_wdt.c
new file mode 100644
index 000000000000..7838a44bfd1d
--- /dev/null
+++ b/arch/ppc/syslib/m8xx_wdt.c
@@ -0,0 +1,99 @@
+/*
+ * m8xx_wdt.c - MPC8xx watchdog driver
+ *
+ * Author: Florian Schirmer <jolt@tuxbox.org>
+ *
+ * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/8xx_immap.h>
+#include <syslib/m8xx_wdt.h>
+
+static int wdt_timeout;
+
+void m8xx_wdt_reset(void)
+{
+ volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
+
+ imap->im_siu_conf.sc_swsr = 0x556c; /* write magic1 */
+ imap->im_siu_conf.sc_swsr = 0xaa39; /* write magic2 */
+}
+
+static irqreturn_t m8xx_wdt_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+ volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
+
+ m8xx_wdt_reset();
+
+ imap->im_sit.sit_piscr |= PISCR_PS; /* clear irq */
+
+ return IRQ_HANDLED;
+}
+
+void __init m8xx_wdt_handler_install(bd_t * binfo)
+{
+ volatile immap_t *imap = (volatile immap_t *)IMAP_ADDR;
+ u32 pitc;
+ u32 sypcr;
+ u32 pitrtclk;
+
+ sypcr = imap->im_siu_conf.sc_sypcr;
+
+ if (!(sypcr & 0x04)) {
+ printk(KERN_NOTICE "m8xx_wdt: wdt disabled (SYPCR: 0x%08X)\n",
+ sypcr);
+ return;
+ }
+
+ m8xx_wdt_reset();
+
+ printk(KERN_NOTICE
+ "m8xx_wdt: active wdt found (SWTC: 0x%04X, SWP: 0x%01X)\n",
+ (sypcr >> 16), sypcr & 0x01);
+
+ wdt_timeout = (sypcr >> 16) & 0xFFFF;
+
+ if (!wdt_timeout)
+ wdt_timeout = 0xFFFF;
+
+ if (sypcr & 0x01)
+ wdt_timeout *= 2048;
+
+ /*
+ * Fire trigger if half of the wdt ticked down
+ */
+
+ if (imap->im_sit.sit_rtcsc & RTCSC_38K)
+ pitrtclk = 9600;
+ else
+ pitrtclk = 8192;
+
+ if ((wdt_timeout) > (UINT_MAX / pitrtclk))
+ pitc = wdt_timeout / binfo->bi_intfreq * pitrtclk / 2;
+ else
+ pitc = pitrtclk * wdt_timeout / binfo->bi_intfreq / 2;
+
+ imap->im_sit.sit_pitc = pitc << 16;
+ imap->im_sit.sit_piscr =
+ (mk_int_int_mask(PIT_INTERRUPT) << 8) | PISCR_PIE | PISCR_PTE;
+
+ if (request_irq(PIT_INTERRUPT, m8xx_wdt_interrupt, 0, "watchdog", NULL))
+ panic("m8xx_wdt: could not allocate watchdog irq!");
+
+ printk(KERN_NOTICE
+ "m8xx_wdt: keep-alive trigger installed (PITC: 0x%04X)\n", pitc);
+
+ wdt_timeout /= binfo->bi_intfreq;
+}
+
+int m8xx_wdt_get_timeout(void)
+{
+ return wdt_timeout;
+}
diff --git a/arch/ppc/syslib/m8xx_wdt.h b/arch/ppc/syslib/m8xx_wdt.h
new file mode 100644
index 000000000000..0d81a9f8155f
--- /dev/null
+++ b/arch/ppc/syslib/m8xx_wdt.h
@@ -0,0 +1,16 @@
+/*
+ * Author: Florian Schirmer <jolt@tuxbox.org>
+ *
+ * 2002 (c) Florian Schirmer <jolt@tuxbox.org> This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#ifndef _PPC_SYSLIB_M8XX_WDT_H
+#define _PPC_SYSLIB_M8XX_WDT_H
+
+extern void m8xx_wdt_handler_install(bd_t * binfo);
+extern int m8xx_wdt_get_timeout(void);
+extern void m8xx_wdt_reset(void);
+
+#endif /* _PPC_SYSLIB_M8XX_WDT_H */
diff --git a/arch/ppc/syslib/mpc10x_common.c b/arch/ppc/syslib/mpc10x_common.c
new file mode 100644
index 000000000000..fd93adfd464c
--- /dev/null
+++ b/arch/ppc/syslib/mpc10x_common.c
@@ -0,0 +1,510 @@
+/*
+ * arch/ppc/syslib/mpc10x_common.c
+ *
+ * Common routines for the Motorola SPS MPC106, MPC107 and MPC8240 Host bridge,
+ * Mem ctlr, EPIC, etc.
+ *
+ * Author: Mark A. Greer
+ * mgreer@mvista.com
+ *
+ * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+/*
+ * *** WARNING - A BAT MUST be set to access the PCI config addr/data regs ***
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/open_pic.h>
+#include <asm/mpc10x.h>
+#include <asm/ocp.h>
+
+/* The OCP structure is fixed by code below, before OCP initialises.
+ paddr depends on where the board places the EUMB.
+ - fixed in mpc10x_bridge_init().
+ irq depends on two things:
+ > does the board use the EPIC at all? (PCORE does not).
+ > is the EPIC in serial or parallel mode?
+ - fixed in mpc10x_set_openpic().
+*/
+
+#ifdef CONFIG_MPC10X_OPENPIC
+#ifdef CONFIG_EPIC_SERIAL_MODE
+#define EPIC_IRQ_BASE (epic_serial_mode ? 16 : 5)
+#else
+#define EPIC_IRQ_BASE 5
+#endif
+#define MPC10X_I2C_IRQ (EPIC_IRQ_BASE + NUM_8259_INTERRUPTS)
+#define MPC10X_DMA0_IRQ (EPIC_IRQ_BASE + 1 + NUM_8259_INTERRUPTS)
+#define MPC10X_DMA1_IRQ (EPIC_IRQ_BASE + 2 + NUM_8259_INTERRUPTS)
+#else
+#define MPC10X_I2C_IRQ OCP_IRQ_NA
+#define MPC10X_DMA0_IRQ OCP_IRQ_NA
+#define MPC10X_DMA1_IRQ OCP_IRQ_NA
+#endif
+
+
+struct ocp_def core_ocp[] = {
+ { .vendor = OCP_VENDOR_INVALID
+ }
+};
+
+static struct ocp_fs_i2c_data mpc10x_i2c_data = {
+ .flags = 0
+};
+static struct ocp_def mpc10x_i2c_ocp = {
+ .vendor = OCP_VENDOR_MOTOROLA,
+ .function = OCP_FUNC_IIC,
+ .index = 0,
+ .additions = &mpc10x_i2c_data
+};
+
+static struct ocp_def mpc10x_dma_ocp[2] = {
+{ .vendor = OCP_VENDOR_MOTOROLA,
+ .function = OCP_FUNC_DMA,
+ .index = 0 },
+{ .vendor = OCP_VENDOR_MOTOROLA,
+ .function = OCP_FUNC_DMA,
+ .index = 1 }
+};
+
+/* Set resources to match bridge memory map */
+void __init
+mpc10x_bridge_set_resources(int map, struct pci_controller *hose)
+{
+
+ switch (map) {
+ case MPC10X_MEM_MAP_A:
+ pci_init_resource(&hose->io_resource,
+ 0x00000000,
+ 0x3f7fffff,
+ IORESOURCE_IO,
+ "PCI host bridge");
+
+ pci_init_resource (&hose->mem_resources[0],
+ 0xc0000000,
+ 0xfeffffff,
+ IORESOURCE_MEM,
+ "PCI host bridge");
+ break;
+ case MPC10X_MEM_MAP_B:
+ pci_init_resource(&hose->io_resource,
+ 0x00000000,
+ 0x00bfffff,
+ IORESOURCE_IO,
+ "PCI host bridge");
+
+ pci_init_resource (&hose->mem_resources[0],
+ 0x80000000,
+ 0xfcffffff,
+ IORESOURCE_MEM,
+ "PCI host bridge");
+ break;
+ default:
+ printk("mpc10x_bridge_set_resources: "
+ "Invalid map specified\n");
+ if (ppc_md.progress)
+ ppc_md.progress("mpc10x:exit1", 0x100);
+ }
+}
+/*
+ * Do some initialization and put the EUMB registers at the specified address
+ * (also map the EPIC registers into virtual space--OpenPIC_Addr will be set).
+ *
+ * The EPIC is not on the 106, only the 8240 and 107.
+ */
+int __init
+mpc10x_bridge_init(struct pci_controller *hose,
+ uint current_map,
+ uint new_map,
+ uint phys_eumb_base)
+{
+ int host_bridge, picr1, picr1_bit;
+ ulong pci_config_addr, pci_config_data;
+ u_char pir, byte;
+
+ if (ppc_md.progress) ppc_md.progress("mpc10x:enter", 0x100);
+
+ /* Set up for current map so we can get at config regs */
+ switch (current_map) {
+ case MPC10X_MEM_MAP_A:
+ setup_indirect_pci(hose,
+ MPC10X_MAPA_CNFG_ADDR,
+ MPC10X_MAPA_CNFG_DATA);
+ break;
+ case MPC10X_MEM_MAP_B:
+ setup_indirect_pci(hose,
+ MPC10X_MAPB_CNFG_ADDR,
+ MPC10X_MAPB_CNFG_DATA);
+ break;
+ default:
+ printk("mpc10x_bridge_init: %s\n",
+ "Invalid current map specified");
+ if (ppc_md.progress)
+ ppc_md.progress("mpc10x:exit1", 0x100);
+ return -1;
+ }
+
+ /* Make sure it's a supported bridge */
+ early_read_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ PCI_VENDOR_ID,
+ &host_bridge);
+
+ switch (host_bridge) {
+ case MPC10X_BRIDGE_106:
+ case MPC10X_BRIDGE_8240:
+ case MPC10X_BRIDGE_107:
+ case MPC10X_BRIDGE_8245:
+ break;
+ default:
+ if (ppc_md.progress)
+ ppc_md.progress("mpc10x:exit2", 0x100);
+ return -1;
+ }
+
+ switch (new_map) {
+ case MPC10X_MEM_MAP_A:
+ MPC10X_SETUP_HOSE(hose, A);
+ pci_config_addr = MPC10X_MAPA_CNFG_ADDR;
+ pci_config_data = MPC10X_MAPA_CNFG_DATA;
+ picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_A;
+ break;
+ case MPC10X_MEM_MAP_B:
+ MPC10X_SETUP_HOSE(hose, B);
+ pci_config_addr = MPC10X_MAPB_CNFG_ADDR;
+ pci_config_data = MPC10X_MAPB_CNFG_DATA;
+ picr1_bit = MPC10X_CFG_PICR1_ADDR_MAP_B;
+ break;
+ default:
+ printk("mpc10x_bridge_init: %s\n",
+ "Invalid new map specified");
+ if (ppc_md.progress)
+ ppc_md.progress("mpc10x:exit3", 0x100);
+ return -1;
+ }
+
+ /* Make bridge use the 'new_map', if not already usng it */
+ if (current_map != new_map) {
+ early_read_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG,
+ &picr1);
+
+ picr1 = (picr1 & ~MPC10X_CFG_PICR1_ADDR_MAP_MASK) |
+ picr1_bit;
+
+ early_write_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG,
+ picr1);
+
+ asm volatile("sync");
+
+ /* Undo old mappings & map in new cfg data/addr regs */
+ iounmap((void *)hose->cfg_addr);
+ iounmap((void *)hose->cfg_data);
+
+ setup_indirect_pci(hose,
+ pci_config_addr,
+ pci_config_data);
+ }
+
+ /* Setup resources to match map */
+ mpc10x_bridge_set_resources(new_map, hose);
+
+ /*
+ * Want processor accesses of 0xFDxxxxxx to be mapped
+ * to PCI memory space at 0x00000000. Do not want
+ * host bridge to respond to PCI memory accesses of
+ * 0xFDxxxxxx. Do not want host bridge to respond
+ * to PCI memory addresses 0xFD000000-0xFDFFFFFF;
+ * want processor accesses from 0x000A0000-0x000BFFFF
+ * to be forwarded to system memory.
+ *
+ * Only valid if not in agent mode and using MAP B.
+ */
+ if (new_map == MPC10X_MEM_MAP_B) {
+ early_read_config_byte(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_MAPB_OPTIONS_REG,
+ &byte);
+
+ byte &= ~(MPC10X_CFG_MAPB_OPTIONS_PFAE |
+ MPC10X_CFG_MAPB_OPTIONS_PCICH |
+ MPC10X_CFG_MAPB_OPTIONS_PROCCH);
+
+ if (host_bridge != MPC10X_BRIDGE_106) {
+ byte |= MPC10X_CFG_MAPB_OPTIONS_CFAE;
+ }
+
+ early_write_config_byte(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_MAPB_OPTIONS_REG,
+ byte);
+ }
+
+ if (host_bridge != MPC10X_BRIDGE_106) {
+ early_read_config_byte(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_PIR_REG,
+ &pir);
+
+ if (pir != MPC10X_CFG_PIR_HOST_BRIDGE) {
+ printk("Host bridge in Agent mode\n");
+ /* Read or Set LMBAR & PCSRBAR? */
+ }
+
+ /* Set base addr of the 8240/107 EUMB. */
+ early_write_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_EUMBBAR,
+ phys_eumb_base);
+#ifdef CONFIG_MPC10X_OPENPIC
+ /* Map EPIC register part of EUMB into vitual memory - PCORE
+ uses an i8259 instead of EPIC. */
+ OpenPIC_Addr =
+ ioremap(phys_eumb_base + MPC10X_EUMB_EPIC_OFFSET,
+ MPC10X_EUMB_EPIC_SIZE);
+#endif
+ mpc10x_i2c_ocp.paddr = phys_eumb_base + MPC10X_EUMB_I2C_OFFSET;
+ mpc10x_i2c_ocp.irq = MPC10X_I2C_IRQ;
+ ocp_add_one_device(&mpc10x_i2c_ocp);
+ mpc10x_dma_ocp[0].paddr = phys_eumb_base +
+ MPC10X_EUMB_DMA_OFFSET + 0x100;
+ mpc10x_dma_ocp[0].irq = MPC10X_DMA0_IRQ;
+ ocp_add_one_device(&mpc10x_dma_ocp[0]);
+ mpc10x_dma_ocp[1].paddr = phys_eumb_base +
+ MPC10X_EUMB_DMA_OFFSET + 0x200;
+ mpc10x_dma_ocp[1].irq = MPC10X_DMA1_IRQ;
+ ocp_add_one_device(&mpc10x_dma_ocp[1]);
+ }
+
+#ifdef CONFIG_MPC10X_STORE_GATHERING
+ mpc10x_enable_store_gathering(hose);
+#else
+ mpc10x_disable_store_gathering(hose);
+#endif
+
+ /*
+ * 8240 erratum 26, 8241/8245 erratum 29, 107 erratum 23: speculative
+ * PCI reads may return stale data so turn off.
+ */
+ if ((host_bridge == MPC10X_BRIDGE_8240)
+ || (host_bridge == MPC10X_BRIDGE_8245)
+ || (host_bridge == MPC10X_BRIDGE_107)) {
+
+ early_read_config_dword(hose, 0, PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG, &picr1);
+
+ picr1 &= ~MPC10X_CFG_PICR1_SPEC_PCI_RD;
+
+ early_write_config_dword(hose, 0, PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG, picr1);
+ }
+
+ /*
+ * 8241/8245 erratum 28: PCI reads from local memory may return
+ * stale data. Workaround by setting PICR2[0] to disable copyback
+ * optimization. Oddly, the latest available user manual for the
+ * 8245 (Rev 2., dated 10/2003) says PICR2[0] is reserverd.
+ */
+ if (host_bridge == MPC10X_BRIDGE_8245) {
+ ulong picr2;
+
+ early_read_config_dword(hose, 0, PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR2_REG, &picr2);
+
+ picr2 |= MPC10X_CFG_PICR2_COPYBACK_OPT;
+
+ early_write_config_dword(hose, 0, PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR2_REG, picr2);
+ }
+
+ if (ppc_md.progress) ppc_md.progress("mpc10x:exit", 0x100);
+ return 0;
+}
+
+/*
+ * Need to make our own PCI config space access macros because
+ * mpc10x_get_mem_size() is called before the data structures are set up for
+ * the 'early_xxx' and 'indirect_xxx' routines to work.
+ * Assumes bus 0.
+ */
+#define MPC10X_CFG_read(val, addr, type, op) *val = op((type)(addr))
+#define MPC10X_CFG_write(val, addr, type, op) op((type *)(addr), (val))
+
+#define MPC10X_PCI_OP(rw, size, type, op, mask) \
+static void \
+mpc10x_##rw##_config_##size(uint *cfg_addr, uint *cfg_data, int devfn, int offset, type val) \
+{ \
+ out_be32(cfg_addr, \
+ ((offset & 0xfc) << 24) | (devfn << 16) \
+ | (0 << 8) | 0x80); \
+ MPC10X_CFG_##rw(val, cfg_data + (offset & mask), type, op); \
+ return; \
+}
+
+MPC10X_PCI_OP(read, byte, u8 *, in_8, 3)
+MPC10X_PCI_OP(read, dword, u32 *, in_le32, 0)
+#if 0 /* Not used */
+MPC10X_PCI_OP(write, byte, u8, out_8, 3)
+MPC10X_PCI_OP(read, word, u16 *, in_le16, 2)
+MPC10X_PCI_OP(write, word, u16, out_le16, 2)
+MPC10X_PCI_OP(write, dword, u32, out_le32, 0)
+#endif
+
+/*
+ * Read the memory controller registers to determine the amount of memory in
+ * the system. This assumes that the firmware has correctly set up the memory
+ * controller registers.
+ */
+unsigned long __init
+mpc10x_get_mem_size(uint mem_map)
+{
+ uint *config_addr, *config_data, val;
+ ulong start, end, total, offset;
+ int i;
+ u_char bank_enables;
+
+ switch (mem_map) {
+ case MPC10X_MEM_MAP_A:
+ config_addr = (uint *)MPC10X_MAPA_CNFG_ADDR;
+ config_data = (uint *)MPC10X_MAPA_CNFG_DATA;
+ break;
+ case MPC10X_MEM_MAP_B:
+ config_addr = (uint *)MPC10X_MAPB_CNFG_ADDR;
+ config_data = (uint *)MPC10X_MAPB_CNFG_DATA;
+ break;
+ default:
+ return 0;
+ }
+
+ mpc10x_read_config_byte(config_addr,
+ config_data,
+ PCI_DEVFN(0,0),
+ MPC10X_MCTLR_MEM_BANK_ENABLES,
+ &bank_enables);
+
+ total = 0;
+
+ for (i=0; i<8; i++) {
+ if (bank_enables & (1 << i)) {
+ offset = MPC10X_MCTLR_MEM_START_1 + ((i > 3) ? 4 : 0);
+ mpc10x_read_config_dword(config_addr,
+ config_data,
+ PCI_DEVFN(0,0),
+ offset,
+ &val);
+ start = (val >> ((i & 3) << 3)) & 0xff;
+
+ offset = MPC10X_MCTLR_EXT_MEM_START_1 + ((i>3) ? 4 : 0);
+ mpc10x_read_config_dword(config_addr,
+ config_data,
+ PCI_DEVFN(0,0),
+ offset,
+ &val);
+ val = (val >> ((i & 3) << 3)) & 0x03;
+ start = (val << 28) | (start << 20);
+
+ offset = MPC10X_MCTLR_MEM_END_1 + ((i > 3) ? 4 : 0);
+ mpc10x_read_config_dword(config_addr,
+ config_data,
+ PCI_DEVFN(0,0),
+ offset,
+ &val);
+ end = (val >> ((i & 3) << 3)) & 0xff;
+
+ offset = MPC10X_MCTLR_EXT_MEM_END_1 + ((i > 3) ? 4 : 0);
+ mpc10x_read_config_dword(config_addr,
+ config_data,
+ PCI_DEVFN(0,0),
+ offset,
+ &val);
+ val = (val >> ((i & 3) << 3)) & 0x03;
+ end = (val << 28) | (end << 20) | 0xfffff;
+
+ total += (end - start + 1);
+ }
+ }
+
+ return total;
+}
+
+int __init
+mpc10x_enable_store_gathering(struct pci_controller *hose)
+{
+ uint picr1;
+
+ early_read_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG,
+ &picr1);
+
+ picr1 |= MPC10X_CFG_PICR1_ST_GATH_EN;
+
+ early_write_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG,
+ picr1);
+
+ return 0;
+}
+
+int __init
+mpc10x_disable_store_gathering(struct pci_controller *hose)
+{
+ uint picr1;
+
+ early_read_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG,
+ &picr1);
+
+ picr1 &= ~MPC10X_CFG_PICR1_ST_GATH_EN;
+
+ early_write_config_dword(hose,
+ 0,
+ PCI_DEVFN(0,0),
+ MPC10X_CFG_PICR1_REG,
+ picr1);
+
+ return 0;
+}
+
+#ifdef CONFIG_MPC10X_OPENPIC
+void __init mpc10x_set_openpic(void)
+{
+ /* Map external IRQs */
+ openpic_set_sources(0, EPIC_IRQ_BASE, OpenPIC_Addr + 0x10200);
+ /* Skip reserved space and map i2c and DMA Ch[01] */
+ openpic_set_sources(EPIC_IRQ_BASE, 3, OpenPIC_Addr + 0x11020);
+ /* Skip reserved space and map Message Unit Interrupt (I2O) */
+ openpic_set_sources(EPIC_IRQ_BASE + 3, 1, OpenPIC_Addr + 0x110C0);
+
+ openpic_init(NUM_8259_INTERRUPTS);
+}
+#endif
diff --git a/arch/ppc/syslib/mpc52xx_devices.c b/arch/ppc/syslib/mpc52xx_devices.c
new file mode 100644
index 000000000000..ad5182efca1d
--- /dev/null
+++ b/arch/ppc/syslib/mpc52xx_devices.c
@@ -0,0 +1,318 @@
+/*
+ * arch/ppc/syslib/mpc52xx_devices.c
+ *
+ * Freescale MPC52xx device descriptions
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/fsl_devices.h>
+#include <linux/resource.h>
+#include <asm/mpc52xx.h>
+#include <asm/ppc_sys.h>
+
+
+static u64 mpc52xx_dma_mask = 0xffffffffULL;
+
+static struct fsl_i2c_platform_data mpc52xx_fsl_i2c_pdata = {
+ .device_flags = FSL_I2C_DEV_CLOCK_5200,
+};
+
+
+/* We use relative offsets for IORESOURCE_MEM to be independent from the
+ * MBAR location at compile time
+ */
+
+/* TODO Add the BestComm initiator channel to the device definitions,
+ possibly using IORESOURCE_DMA. But that's when BestComm is ready ... */
+
+struct platform_device ppc_sys_platform_devices[] = {
+ [MPC52xx_MSCAN1] = {
+ .name = "mpc52xx-mscan",
+ .id = 0,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x0900,
+ .end = 0x097f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_MSCAN1_IRQ,
+ .end = MPC52xx_MSCAN1_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_MSCAN2] = {
+ .name = "mpc52xx-mscan",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x0980,
+ .end = 0x09ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_MSCAN2_IRQ,
+ .end = MPC52xx_MSCAN2_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_SPI] = {
+ .name = "mpc52xx-spi",
+ .id = -1,
+ .num_resources = 3,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x0f00,
+ .end = 0x0f1f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "modf",
+ .start = MPC52xx_SPI_MODF_IRQ,
+ .end = MPC52xx_SPI_MODF_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "spif",
+ .start = MPC52xx_SPI_SPIF_IRQ,
+ .end = MPC52xx_SPI_SPIF_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_USB] = {
+ .name = "ppc-soc-ohci",
+ .id = -1,
+ .num_resources = 2,
+ .dev.dma_mask = &mpc52xx_dma_mask,
+ .dev.coherent_dma_mask = 0xffffffffULL,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x1000,
+ .end = 0x10ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_USB_IRQ,
+ .end = MPC52xx_USB_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_BDLC] = {
+ .name = "mpc52xx-bdlc",
+ .id = -1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x1300,
+ .end = 0x130f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_BDLC_IRQ,
+ .end = MPC52xx_BDLC_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_PSC1] = {
+ .name = "mpc52xx-psc",
+ .id = 0,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x2000,
+ .end = 0x209f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_PSC1_IRQ,
+ .end = MPC52xx_PSC1_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_PSC2] = {
+ .name = "mpc52xx-psc",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x2200,
+ .end = 0x229f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_PSC2_IRQ,
+ .end = MPC52xx_PSC2_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_PSC3] = {
+ .name = "mpc52xx-psc",
+ .id = 2,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x2400,
+ .end = 0x249f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_PSC3_IRQ,
+ .end = MPC52xx_PSC3_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_PSC4] = {
+ .name = "mpc52xx-psc",
+ .id = 3,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x2600,
+ .end = 0x269f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_PSC4_IRQ,
+ .end = MPC52xx_PSC4_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_PSC5] = {
+ .name = "mpc52xx-psc",
+ .id = 4,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x2800,
+ .end = 0x289f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_PSC5_IRQ,
+ .end = MPC52xx_PSC5_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_PSC6] = {
+ .name = "mpc52xx-psc",
+ .id = 5,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x2c00,
+ .end = 0x2c9f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_PSC6_IRQ,
+ .end = MPC52xx_PSC6_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_FEC] = {
+ .name = "mpc52xx-fec",
+ .id = -1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x3000,
+ .end = 0x33ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_FEC_IRQ,
+ .end = MPC52xx_FEC_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_ATA] = {
+ .name = "mpc52xx-ata",
+ .id = -1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x3a00,
+ .end = 0x3aff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_ATA_IRQ,
+ .end = MPC52xx_ATA_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_I2C1] = {
+ .name = "fsl-i2c",
+ .id = 0,
+ .dev.platform_data = &mpc52xx_fsl_i2c_pdata,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x3d00,
+ .end = 0x3d1f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_I2C1_IRQ,
+ .end = MPC52xx_I2C1_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC52xx_I2C2] = {
+ .name = "fsl-i2c",
+ .id = 1,
+ .dev.platform_data = &mpc52xx_fsl_i2c_pdata,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x3d40,
+ .end = 0x3d5f,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC52xx_I2C2_IRQ,
+ .end = MPC52xx_I2C2_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+};
+
+
+static int __init mach_mpc52xx_fixup(struct platform_device *pdev)
+{
+ ppc_sys_fixup_mem_resource(pdev, MPC52xx_MBAR);
+ return 0;
+}
+
+static int __init mach_mpc52xx_init(void)
+{
+ ppc_sys_device_fixup = mach_mpc52xx_fixup;
+ return 0;
+}
+
+postcore_initcall(mach_mpc52xx_init);
diff --git a/arch/ppc/syslib/mpc52xx_pci.c b/arch/ppc/syslib/mpc52xx_pci.c
new file mode 100644
index 000000000000..c723efd954a6
--- /dev/null
+++ b/arch/ppc/syslib/mpc52xx_pci.c
@@ -0,0 +1,235 @@
+/*
+ * arch/ppc/syslib/mpc52xx_pci.c
+ *
+ * PCI code for the Freescale MPC52xx embedded CPU.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/config.h>
+
+#include <asm/pci.h>
+
+#include <asm/mpc52xx.h>
+#include "mpc52xx_pci.h"
+
+#include <asm/delay.h>
+
+
+static int
+mpc52xx_pci_read_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 *val)
+{
+ struct pci_controller *hose = bus->sysdata;
+ u32 value;
+
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ out_be32(hose->cfg_addr,
+ (1 << 31) |
+ ((bus->number - hose->bus_offset) << 16) |
+ (devfn << 8) |
+ (offset & 0xfc));
+
+ value = in_le32(hose->cfg_data);
+
+ if (len != 4) {
+ value >>= ((offset & 0x3) << 3);
+ value &= 0xffffffff >> (32 - (len << 3));
+ }
+
+ *val = value;
+
+ out_be32(hose->cfg_addr, 0);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+mpc52xx_pci_write_config(struct pci_bus *bus, unsigned int devfn,
+ int offset, int len, u32 val)
+{
+ struct pci_controller *hose = bus->sysdata;
+ u32 value, mask;
+
+ if (ppc_md.pci_exclude_device)
+ if (ppc_md.pci_exclude_device(bus->number, devfn))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ out_be32(hose->cfg_addr,
+ (1 << 31) |
+ ((bus->number - hose->bus_offset) << 16) |
+ (devfn << 8) |
+ (offset & 0xfc));
+
+ if (len != 4) {
+ value = in_le32(hose->cfg_data);
+
+ offset = (offset & 0x3) << 3;
+ mask = (0xffffffff >> (32 - (len << 3)));
+ mask <<= offset;
+
+ value &= ~mask;
+ val = value | ((val << offset) & mask);
+ }
+
+ out_le32(hose->cfg_data, val);
+
+ out_be32(hose->cfg_addr, 0);
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops mpc52xx_pci_ops = {
+ .read = mpc52xx_pci_read_config,
+ .write = mpc52xx_pci_write_config
+};
+
+
+static void __init
+mpc52xx_pci_setup(struct mpc52xx_pci __iomem *pci_regs)
+{
+
+ /* Setup control regs */
+ /* Nothing to do afaik */
+
+ /* Setup windows */
+ out_be32(&pci_regs->iw0btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
+ MPC52xx_PCI_MEM_START + MPC52xx_PCI_MEM_OFFSET,
+ MPC52xx_PCI_MEM_START,
+ MPC52xx_PCI_MEM_SIZE ));
+
+ out_be32(&pci_regs->iw1btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
+ MPC52xx_PCI_MMIO_START + MPC52xx_PCI_MEM_OFFSET,
+ MPC52xx_PCI_MMIO_START,
+ MPC52xx_PCI_MMIO_SIZE ));
+
+ out_be32(&pci_regs->iw2btar, MPC52xx_PCI_IWBTAR_TRANSLATION(
+ MPC52xx_PCI_IO_BASE,
+ MPC52xx_PCI_IO_START,
+ MPC52xx_PCI_IO_SIZE ));
+
+ out_be32(&pci_regs->iwcr, MPC52xx_PCI_IWCR_PACK(
+ ( MPC52xx_PCI_IWCR_ENABLE | /* iw0btar */
+ MPC52xx_PCI_IWCR_READ_MULTI |
+ MPC52xx_PCI_IWCR_MEM ),
+ ( MPC52xx_PCI_IWCR_ENABLE | /* iw1btar */
+ MPC52xx_PCI_IWCR_READ |
+ MPC52xx_PCI_IWCR_MEM ),
+ ( MPC52xx_PCI_IWCR_ENABLE | /* iw2btar */
+ MPC52xx_PCI_IWCR_IO )
+ ));
+
+
+ out_be32(&pci_regs->tbatr0,
+ MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_IO );
+ out_be32(&pci_regs->tbatr1,
+ MPC52xx_PCI_TBATR_ENABLE | MPC52xx_PCI_TARGET_MEM );
+
+ out_be32(&pci_regs->tcr, MPC52xx_PCI_TCR_LD);
+
+ /* Reset the exteral bus ( internal PCI controller is NOT resetted ) */
+ /* Not necessary and can be a bad thing if for example the bootloader
+ is displaying a splash screen or ... Just left here for
+ documentation purpose if anyone need it */
+#if 0
+ u32 tmp;
+ tmp = in_be32(&pci_regs->gscr);
+ out_be32(&pci_regs->gscr, tmp | MPC52xx_PCI_GSCR_PR);
+ udelay(50);
+ out_be32(&pci_regs->gscr, tmp);
+#endif
+}
+
+static void __init
+mpc52xx_pci_fixup_resources(struct pci_dev *dev)
+{
+ int i;
+
+ /* We don't rely on boot loader for PCI and resets all
+ devices */
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ struct resource *res = &dev->resource[i];
+ if (res->end > res->start) { /* Only valid resources */
+ res->end -= res->start;
+ res->start = 0;
+ res->flags |= IORESOURCE_UNSET;
+ }
+ }
+
+ /* The PCI Host bridge of MPC52xx has a prefetch memory resource
+ fixed to 1Gb. Doesn't fit in the resource system so we remove it */
+ if ( (dev->vendor == PCI_VENDOR_ID_MOTOROLA) &&
+ (dev->device == PCI_DEVICE_ID_MOTOROLA_MPC5200) ) {
+ struct resource *res = &dev->resource[1];
+ res->start = res->end = res->flags = 0;
+ }
+}
+
+void __init
+mpc52xx_find_bridges(void)
+{
+ struct mpc52xx_pci __iomem *pci_regs;
+ struct pci_controller *hose;
+
+ pci_assign_all_busses = 1;
+
+ pci_regs = ioremap(MPC52xx_PA(MPC52xx_PCI_OFFSET), MPC52xx_PCI_SIZE);
+ if (!pci_regs)
+ return;
+
+ hose = pcibios_alloc_controller();
+ if (!hose) {
+ iounmap(pci_regs);
+ return;
+ }
+
+ ppc_md.pci_swizzle = common_swizzle;
+ ppc_md.pcibios_fixup_resources = mpc52xx_pci_fixup_resources;
+
+ hose->first_busno = 0;
+ hose->last_busno = 0xff;
+ hose->bus_offset = 0;
+ hose->ops = &mpc52xx_pci_ops;
+
+ mpc52xx_pci_setup(pci_regs);
+
+ hose->pci_mem_offset = MPC52xx_PCI_MEM_OFFSET;
+
+ isa_io_base =
+ (unsigned long) ioremap(MPC52xx_PCI_IO_BASE,
+ MPC52xx_PCI_IO_SIZE);
+ hose->io_base_virt = (void *) isa_io_base;
+
+ hose->cfg_addr = &pci_regs->car;
+ hose->cfg_data = (void __iomem *) isa_io_base;
+
+ /* Setup resources */
+ pci_init_resource(&hose->mem_resources[0],
+ MPC52xx_PCI_MEM_START,
+ MPC52xx_PCI_MEM_STOP,
+ IORESOURCE_MEM|IORESOURCE_PREFETCH,
+ "PCI prefetchable memory");
+
+ pci_init_resource(&hose->mem_resources[1],
+ MPC52xx_PCI_MMIO_START,
+ MPC52xx_PCI_MMIO_STOP,
+ IORESOURCE_MEM,
+ "PCI memory");
+
+ pci_init_resource(&hose->io_resource,
+ MPC52xx_PCI_IO_START,
+ MPC52xx_PCI_IO_STOP,
+ IORESOURCE_IO,
+ "PCI I/O");
+
+}
diff --git a/arch/ppc/syslib/mpc52xx_pci.h b/arch/ppc/syslib/mpc52xx_pci.h
new file mode 100644
index 000000000000..04b509a02530
--- /dev/null
+++ b/arch/ppc/syslib/mpc52xx_pci.h
@@ -0,0 +1,139 @@
+/*
+ * arch/ppc/syslib/mpc52xx_pci.h
+ *
+ * PCI Include file the Freescale MPC52xx embedded cpu chips
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Inspired from code written by Dale Farnsworth <dfarnsworth@mvista.com>
+ * for the 2.4 kernel.
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 MontaVista, Software, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef __SYSLIB_MPC52xx_PCI_H__
+#define __SYSLIB_MPC52xx_PCI_H__
+
+/* ======================================================================== */
+/* PCI windows config */
+/* ======================================================================== */
+
+/*
+ * Master windows : MPC52xx -> PCI
+ *
+ * 0x80000000 -> 0x9FFFFFFF PCI Mem prefetchable IW0BTAR
+ * 0xA0000000 -> 0xAFFFFFFF PCI Mem IW1BTAR
+ * 0xB0000000 -> 0xB0FFFFFF PCI IO IW2BTAR
+ *
+ * Slave windows : PCI -> MPC52xx
+ *
+ * 0xF0000000 -> 0xF003FFFF MPC52xx MBAR TBATR0
+ * 0x00000000 -> 0x3FFFFFFF MPC52xx local memory TBATR1
+ */
+
+#define MPC52xx_PCI_MEM_OFFSET 0x00000000 /* Offset for MEM MMIO */
+
+#define MPC52xx_PCI_MEM_START 0x80000000
+#define MPC52xx_PCI_MEM_SIZE 0x20000000
+#define MPC52xx_PCI_MEM_STOP (MPC52xx_PCI_MEM_START+MPC52xx_PCI_MEM_SIZE-1)
+
+#define MPC52xx_PCI_MMIO_START 0xa0000000
+#define MPC52xx_PCI_MMIO_SIZE 0x10000000
+#define MPC52xx_PCI_MMIO_STOP (MPC52xx_PCI_MMIO_START+MPC52xx_PCI_MMIO_SIZE-1)
+
+#define MPC52xx_PCI_IO_BASE 0xb0000000
+
+#define MPC52xx_PCI_IO_START 0x00000000
+#define MPC52xx_PCI_IO_SIZE 0x01000000
+#define MPC52xx_PCI_IO_STOP (MPC52xx_PCI_IO_START+MPC52xx_PCI_IO_SIZE-1)
+
+
+#define MPC52xx_PCI_TARGET_IO MPC52xx_MBAR
+#define MPC52xx_PCI_TARGET_MEM 0x00000000
+
+
+/* ======================================================================== */
+/* Structures mapping & Defines for PCI Unit */
+/* ======================================================================== */
+
+#define MPC52xx_PCI_GSCR_BM 0x40000000
+#define MPC52xx_PCI_GSCR_PE 0x20000000
+#define MPC52xx_PCI_GSCR_SE 0x10000000
+#define MPC52xx_PCI_GSCR_XLB2PCI_MASK 0x07000000
+#define MPC52xx_PCI_GSCR_XLB2PCI_SHIFT 24
+#define MPC52xx_PCI_GSCR_IPG2PCI_MASK 0x00070000
+#define MPC52xx_PCI_GSCR_IPG2PCI_SHIFT 16
+#define MPC52xx_PCI_GSCR_BME 0x00004000
+#define MPC52xx_PCI_GSCR_PEE 0x00002000
+#define MPC52xx_PCI_GSCR_SEE 0x00001000
+#define MPC52xx_PCI_GSCR_PR 0x00000001
+
+
+#define MPC52xx_PCI_IWBTAR_TRANSLATION(proc_ad,pci_ad,size) \
+ ( ( (proc_ad) & 0xff000000 ) | \
+ ( (((size) - 1) >> 8) & 0x00ff0000 ) | \
+ ( ((pci_ad) >> 16) & 0x0000ff00 ) )
+
+#define MPC52xx_PCI_IWCR_PACK(win0,win1,win2) (((win0) << 24) | \
+ ((win1) << 16) | \
+ ((win2) << 8))
+
+#define MPC52xx_PCI_IWCR_DISABLE 0x0
+#define MPC52xx_PCI_IWCR_ENABLE 0x1
+#define MPC52xx_PCI_IWCR_READ 0x0
+#define MPC52xx_PCI_IWCR_READ_LINE 0x2
+#define MPC52xx_PCI_IWCR_READ_MULTI 0x4
+#define MPC52xx_PCI_IWCR_MEM 0x0
+#define MPC52xx_PCI_IWCR_IO 0x8
+
+#define MPC52xx_PCI_TCR_P 0x01000000
+#define MPC52xx_PCI_TCR_LD 0x00010000
+
+#define MPC52xx_PCI_TBATR_DISABLE 0x0
+#define MPC52xx_PCI_TBATR_ENABLE 0x1
+
+
+#ifndef __ASSEMBLY__
+
+struct mpc52xx_pci {
+ u32 idr; /* PCI + 0x00 */
+ u32 scr; /* PCI + 0x04 */
+ u32 ccrir; /* PCI + 0x08 */
+ u32 cr1; /* PCI + 0x0C */
+ u32 bar0; /* PCI + 0x10 */
+ u32 bar1; /* PCI + 0x14 */
+ u8 reserved1[16]; /* PCI + 0x18 */
+ u32 ccpr; /* PCI + 0x28 */
+ u32 sid; /* PCI + 0x2C */
+ u32 erbar; /* PCI + 0x30 */
+ u32 cpr; /* PCI + 0x34 */
+ u8 reserved2[4]; /* PCI + 0x38 */
+ u32 cr2; /* PCI + 0x3C */
+ u8 reserved3[32]; /* PCI + 0x40 */
+ u32 gscr; /* PCI + 0x60 */
+ u32 tbatr0; /* PCI + 0x64 */
+ u32 tbatr1; /* PCI + 0x68 */
+ u32 tcr; /* PCI + 0x6C */
+ u32 iw0btar; /* PCI + 0x70 */
+ u32 iw1btar; /* PCI + 0x74 */
+ u32 iw2btar; /* PCI + 0x78 */
+ u8 reserved4[4]; /* PCI + 0x7C */
+ u32 iwcr; /* PCI + 0x80 */
+ u32 icr; /* PCI + 0x84 */
+ u32 isr; /* PCI + 0x88 */
+ u32 arb; /* PCI + 0x8C */
+ u8 reserved5[104]; /* PCI + 0x90 */
+ u32 car; /* PCI + 0xF8 */
+ u8 reserved6[4]; /* PCI + 0xFC */
+};
+
+#endif /* __ASSEMBLY__ */
+
+
+#endif /* __SYSLIB_MPC52xx_PCI_H__ */
diff --git a/arch/ppc/syslib/mpc52xx_pic.c b/arch/ppc/syslib/mpc52xx_pic.c
new file mode 100644
index 000000000000..4c4497e62517
--- /dev/null
+++ b/arch/ppc/syslib/mpc52xx_pic.c
@@ -0,0 +1,257 @@
+/*
+ * arch/ppc/syslib/mpc52xx_pic.c
+ *
+ * Programmable Interrupt Controller functions for the Freescale MPC52xx
+ * embedded CPU.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Based on (well, mostly copied from) the code from the 2.4 kernel by
+ * Dale Farnsworth <dfarnsworth@mvista.com> and Kent Borg.
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 Montavista Software, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/mpc52xx.h>
+
+
+static struct mpc52xx_intr __iomem *intr;
+static struct mpc52xx_sdma __iomem *sdma;
+
+static void
+mpc52xx_ic_disable(unsigned int irq)
+{
+ u32 val;
+
+ if (irq == MPC52xx_IRQ0) {
+ val = in_be32(&intr->ctrl);
+ val &= ~(1 << 11);
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_IRQ1) {
+ BUG();
+ }
+ else if (irq <= MPC52xx_IRQ3) {
+ val = in_be32(&intr->ctrl);
+ val &= ~(1 << (10 - (irq - MPC52xx_IRQ1)));
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_SDMA_IRQ_BASE) {
+ val = in_be32(&intr->main_mask);
+ val |= 1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE));
+ out_be32(&intr->main_mask, val);
+ }
+ else if (irq < MPC52xx_PERP_IRQ_BASE) {
+ val = in_be32(&sdma->IntMask);
+ val |= 1 << (irq - MPC52xx_SDMA_IRQ_BASE);
+ out_be32(&sdma->IntMask, val);
+ }
+ else {
+ val = in_be32(&intr->per_mask);
+ val |= 1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE));
+ out_be32(&intr->per_mask, val);
+ }
+}
+
+static void
+mpc52xx_ic_enable(unsigned int irq)
+{
+ u32 val;
+
+ if (irq == MPC52xx_IRQ0) {
+ val = in_be32(&intr->ctrl);
+ val |= 1 << 11;
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_IRQ1) {
+ BUG();
+ }
+ else if (irq <= MPC52xx_IRQ3) {
+ val = in_be32(&intr->ctrl);
+ val |= 1 << (10 - (irq - MPC52xx_IRQ1));
+ out_be32(&intr->ctrl, val);
+ }
+ else if (irq < MPC52xx_SDMA_IRQ_BASE) {
+ val = in_be32(&intr->main_mask);
+ val &= ~(1 << (16 - (irq - MPC52xx_MAIN_IRQ_BASE)));
+ out_be32(&intr->main_mask, val);
+ }
+ else if (irq < MPC52xx_PERP_IRQ_BASE) {
+ val = in_be32(&sdma->IntMask);
+ val &= ~(1 << (irq - MPC52xx_SDMA_IRQ_BASE));
+ out_be32(&sdma->IntMask, val);
+ }
+ else {
+ val = in_be32(&intr->per_mask);
+ val &= ~(1 << (31 - (irq - MPC52xx_PERP_IRQ_BASE)));
+ out_be32(&intr->per_mask, val);
+ }
+}
+
+static void
+mpc52xx_ic_ack(unsigned int irq)
+{
+ u32 val;
+
+ /*
+ * Only some irqs are reset here, others in interrupting hardware.
+ */
+
+ switch (irq) {
+ case MPC52xx_IRQ0:
+ val = in_be32(&intr->ctrl);
+ val |= 0x08000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ case MPC52xx_CCS_IRQ:
+ val = in_be32(&intr->enc_status);
+ val |= 0x00000400;
+ out_be32(&intr->enc_status, val);
+ break;
+ case MPC52xx_IRQ1:
+ val = in_be32(&intr->ctrl);
+ val |= 0x04000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ case MPC52xx_IRQ2:
+ val = in_be32(&intr->ctrl);
+ val |= 0x02000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ case MPC52xx_IRQ3:
+ val = in_be32(&intr->ctrl);
+ val |= 0x01000000;
+ out_be32(&intr->ctrl, val);
+ break;
+ default:
+ if (irq >= MPC52xx_SDMA_IRQ_BASE
+ && irq < (MPC52xx_SDMA_IRQ_BASE + MPC52xx_SDMA_IRQ_NUM)) {
+ out_be32(&sdma->IntPend,
+ 1 << (irq - MPC52xx_SDMA_IRQ_BASE));
+ }
+ break;
+ }
+}
+
+static void
+mpc52xx_ic_disable_and_ack(unsigned int irq)
+{
+ mpc52xx_ic_disable(irq);
+ mpc52xx_ic_ack(irq);
+}
+
+static void
+mpc52xx_ic_end(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ mpc52xx_ic_enable(irq);
+}
+
+static struct hw_interrupt_type mpc52xx_ic = {
+ .typename = " MPC52xx ",
+ .enable = mpc52xx_ic_enable,
+ .disable = mpc52xx_ic_disable,
+ .ack = mpc52xx_ic_disable_and_ack,
+ .end = mpc52xx_ic_end,
+};
+
+void __init
+mpc52xx_init_irq(void)
+{
+ int i;
+ u32 intr_ctrl;
+
+ /* Remap the necessary zones */
+ intr = ioremap(MPC52xx_PA(MPC52xx_INTR_OFFSET), MPC52xx_INTR_SIZE);
+ sdma = ioremap(MPC52xx_PA(MPC52xx_SDMA_OFFSET), MPC52xx_SDMA_SIZE);
+
+ if ((intr==NULL) || (sdma==NULL))
+ panic("Can't ioremap PIC/SDMA register for init_irq !");
+
+ /* Disable all interrupt sources. */
+ out_be32(&sdma->IntPend, 0xffffffff); /* 1 means clear pending */
+ out_be32(&sdma->IntMask, 0xffffffff); /* 1 means disabled */
+ out_be32(&intr->per_mask, 0x7ffffc00); /* 1 means disabled */
+ out_be32(&intr->main_mask, 0x00010fff); /* 1 means disabled */
+ intr_ctrl = in_be32(&intr->ctrl);
+ intr_ctrl &= 0x00ff0000; /* Keeps IRQ[0-3] config */
+ intr_ctrl |= 0x0f000000 | /* clear IRQ 0-3 */
+ 0x00001000 | /* MEE master external enable */
+ 0x00000000 | /* 0 means disable IRQ 0-3 */
+ 0x00000001; /* CEb route critical normally */
+ out_be32(&intr->ctrl, intr_ctrl);
+
+ /* Zero a bunch of the priority settings. */
+ out_be32(&intr->per_pri1, 0);
+ out_be32(&intr->per_pri2, 0);
+ out_be32(&intr->per_pri3, 0);
+ out_be32(&intr->main_pri1, 0);
+ out_be32(&intr->main_pri2, 0);
+
+ /* Initialize irq_desc[i].handler's with mpc52xx_ic. */
+ for (i = 0; i < NR_IRQS; i++) {
+ irq_desc[i].handler = &mpc52xx_ic;
+ irq_desc[i].status = IRQ_LEVEL;
+ }
+
+ #define IRQn_MODE(intr_ctrl,irq) (((intr_ctrl) >> (22-(i<<1))) & 0x03)
+ for (i=0 ; i<4 ; i++) {
+ int mode;
+ mode = IRQn_MODE(intr_ctrl,i);
+ if ((mode == 0x1) || (mode == 0x2))
+ irq_desc[i?MPC52xx_IRQ1+i-1:MPC52xx_IRQ0].status = 0;
+ }
+}
+
+int
+mpc52xx_get_irq(struct pt_regs *regs)
+{
+ u32 status;
+ int irq = -1;
+
+ status = in_be32(&intr->enc_status);
+
+ if (status & 0x00000400) { /* critical */
+ irq = (status >> 8) & 0x3;
+ if (irq == 2) /* high priority peripheral */
+ goto peripheral;
+ irq += MPC52xx_CRIT_IRQ_BASE;
+ }
+ else if (status & 0x00200000) { /* main */
+ irq = (status >> 16) & 0x1f;
+ if (irq == 4) /* low priority peripheral */
+ goto peripheral;
+ irq += MPC52xx_MAIN_IRQ_BASE;
+ }
+ else if (status & 0x20000000) { /* peripheral */
+peripheral:
+ irq = (status >> 24) & 0x1f;
+ if (irq == 0) { /* bestcomm */
+ status = in_be32(&sdma->IntPend);
+ irq = ffs(status) + MPC52xx_SDMA_IRQ_BASE-1;
+ }
+ else
+ irq += MPC52xx_PERP_IRQ_BASE;
+ }
+
+ return irq;
+}
+
diff --git a/arch/ppc/syslib/mpc52xx_setup.c b/arch/ppc/syslib/mpc52xx_setup.c
new file mode 100644
index 000000000000..bb2374585a7b
--- /dev/null
+++ b/arch/ppc/syslib/mpc52xx_setup.c
@@ -0,0 +1,230 @@
+/*
+ * arch/ppc/syslib/mpc52xx_setup.c
+ *
+ * Common code for the boards based on Freescale MPC52xx embedded CPU.
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Support for other bootloaders than UBoot by Dale Farnsworth
+ * <dfarnsworth@mvista.com>
+ *
+ * Copyright (C) 2004 Sylvain Munaut <tnt@246tNt.com>
+ * Copyright (C) 2003 Montavista Software, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/config.h>
+
+#include <asm/io.h>
+#include <asm/time.h>
+#include <asm/mpc52xx.h>
+#include <asm/mpc52xx_psc.h>
+#include <asm/pgtable.h>
+#include <asm/ppcboot.h>
+
+extern bd_t __res;
+
+static int core_mult[] = { /* CPU Frequency multiplier, taken */
+ 0, 0, 0, 10, 20, 20, 25, 45, /* from the datasheet used to compute */
+ 30, 55, 40, 50, 0, 60, 35, 0, /* CPU frequency from XLB freq and */
+ 30, 25, 65, 10, 70, 20, 75, 45, /* external jumper config */
+ 0, 55, 40, 50, 80, 60, 35, 0
+};
+
+void
+mpc52xx_restart(char *cmd)
+{
+ struct mpc52xx_gpt __iomem *gpt0 = MPC52xx_VA(MPC52xx_GPTx_OFFSET(0));
+
+ local_irq_disable();
+
+ /* Turn on the watchdog and wait for it to expire. It effectively
+ does a reset */
+ out_be32(&gpt0->count, 0x000000ff);
+ out_be32(&gpt0->mode, 0x00009004);
+
+ while (1);
+}
+
+void
+mpc52xx_halt(void)
+{
+ local_irq_disable();
+
+ while (1);
+}
+
+void
+mpc52xx_power_off(void)
+{
+ /* By default we don't have any way of shut down.
+ If a specific board wants to, it can set the power down
+ code to any hardware implementation dependent code */
+ mpc52xx_halt();
+}
+
+
+void __init
+mpc52xx_set_bat(void)
+{
+ /* Set BAT 2 to map the 0xf0000000 area */
+ /* This mapping is used during mpc52xx_progress,
+ * mpc52xx_find_end_of_memory, and UARTs/GPIO access for debug
+ */
+ mb();
+ mtspr(SPRN_DBAT2U, 0xf0001ffe);
+ mtspr(SPRN_DBAT2L, 0xf000002a);
+ mb();
+}
+
+void __init
+mpc52xx_map_io(void)
+{
+ /* Here we only map the MBAR */
+ io_block_mapping(
+ MPC52xx_MBAR_VIRT, MPC52xx_MBAR, MPC52xx_MBAR_SIZE, _PAGE_IO);
+}
+
+
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+#ifndef MPC52xx_PF_CONSOLE_PORT
+#error "mpc52xx PSC for console not selected"
+#endif
+
+static void
+mpc52xx_psc_putc(struct mpc52xx_psc __iomem *psc, unsigned char c)
+{
+ while (!(in_be16(&psc->mpc52xx_psc_status) &
+ MPC52xx_PSC_SR_TXRDY));
+ out_8(&psc->mpc52xx_psc_buffer_8, c);
+}
+
+void
+mpc52xx_progress(char *s, unsigned short hex)
+{
+ char c;
+ struct mpc52xx_psc __iomem *psc;
+
+ psc = MPC52xx_VA(MPC52xx_PSCx_OFFSET(MPC52xx_PF_CONSOLE_PORT));
+
+ while ((c = *s++) != 0) {
+ if (c == '\n')
+ mpc52xx_psc_putc(psc, '\r');
+ mpc52xx_psc_putc(psc, c);
+ }
+
+ mpc52xx_psc_putc(psc, '\r');
+ mpc52xx_psc_putc(psc, '\n');
+}
+
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+
+
+unsigned long __init
+mpc52xx_find_end_of_memory(void)
+{
+ u32 ramsize = __res.bi_memsize;
+
+ /*
+ * if bootloader passed a memsize, just use it
+ * else get size from sdram config registers
+ */
+ if (ramsize == 0) {
+ struct mpc52xx_mmap_ctl __iomem *mmap_ctl;
+ u32 sdram_config_0, sdram_config_1;
+
+ /* Temp BAT2 mapping active when this is called ! */
+ mmap_ctl = MPC52xx_VA(MPC52xx_MMAP_CTL_OFFSET);
+
+ sdram_config_0 = in_be32(&mmap_ctl->sdram0);
+ sdram_config_1 = in_be32(&mmap_ctl->sdram1);
+
+ if ((sdram_config_0 & 0x1f) >= 0x13)
+ ramsize = 1 << ((sdram_config_0 & 0xf) + 17);
+
+ if (((sdram_config_1 & 0x1f) >= 0x13) &&
+ ((sdram_config_1 & 0xfff00000) == ramsize))
+ ramsize += 1 << ((sdram_config_1 & 0xf) + 17);
+ }
+
+ return ramsize;
+}
+
+void __init
+mpc52xx_calibrate_decr(void)
+{
+ int current_time, previous_time;
+ int tbl_start, tbl_end;
+ unsigned int xlbfreq, cpufreq, ipbfreq, pcifreq, divisor;
+
+ xlbfreq = __res.bi_busfreq;
+ /* if bootloader didn't pass bus frequencies, calculate them */
+ if (xlbfreq == 0) {
+ /* Get RTC & Clock manager modules */
+ struct mpc52xx_rtc __iomem *rtc;
+ struct mpc52xx_cdm __iomem *cdm;
+
+ rtc = ioremap(MPC52xx_PA(MPC52xx_RTC_OFFSET), MPC52xx_RTC_SIZE);
+ cdm = ioremap(MPC52xx_PA(MPC52xx_CDM_OFFSET), MPC52xx_CDM_SIZE);
+
+ if ((rtc==NULL) || (cdm==NULL))
+ panic("Can't ioremap RTC/CDM while computing bus freq");
+
+ /* Count bus clock during 1/64 sec */
+ out_be32(&rtc->dividers, 0x8f1f0000); /* Set RTC 64x faster */
+ previous_time = in_be32(&rtc->time);
+ while ((current_time = in_be32(&rtc->time)) == previous_time) ;
+ tbl_start = get_tbl();
+ previous_time = current_time;
+ while ((current_time = in_be32(&rtc->time)) == previous_time) ;
+ tbl_end = get_tbl();
+ out_be32(&rtc->dividers, 0xffff0000); /* Restore RTC */
+
+ /* Compute all frequency from that & CDM settings */
+ xlbfreq = (tbl_end - tbl_start) << 8;
+ cpufreq = (xlbfreq * core_mult[in_be32(&cdm->rstcfg)&0x1f])/10;
+ ipbfreq = (in_8(&cdm->ipb_clk_sel) & 1) ?
+ xlbfreq / 2 : xlbfreq;
+ switch (in_8(&cdm->pci_clk_sel) & 3) {
+ case 0:
+ pcifreq = ipbfreq;
+ break;
+ case 1:
+ pcifreq = ipbfreq / 2;
+ break;
+ default:
+ pcifreq = xlbfreq / 4;
+ break;
+ }
+ __res.bi_busfreq = xlbfreq;
+ __res.bi_intfreq = cpufreq;
+ __res.bi_ipbfreq = ipbfreq;
+ __res.bi_pcifreq = pcifreq;
+
+ /* Release mapping */
+ iounmap(rtc);
+ iounmap(cdm);
+ }
+
+ divisor = 4;
+
+ tb_ticks_per_jiffy = xlbfreq / HZ / divisor;
+ tb_to_us = mulhwu_scale_factor(xlbfreq / divisor, 1000000);
+}
+
+int mpc52xx_match_psc_function(int psc_idx, const char *func)
+{
+ struct mpc52xx_psc_func *cf = mpc52xx_psc_functions;
+
+ while ((cf->id != -1) && (cf->func != NULL)) {
+ if ((cf->id == psc_idx) && !strcmp(cf->func,func))
+ return 1;
+ cf++;
+ }
+
+ return 0;
+}
diff --git a/arch/ppc/syslib/mpc52xx_sys.c b/arch/ppc/syslib/mpc52xx_sys.c
new file mode 100644
index 000000000000..9a0f90aa8aac
--- /dev/null
+++ b/arch/ppc/syslib/mpc52xx_sys.c
@@ -0,0 +1,38 @@
+/*
+ * arch/ppc/syslib/mpc52xx_sys.c
+ *
+ * Freescale MPC52xx system descriptions
+ *
+ *
+ * Maintainer : Sylvain Munaut <tnt@246tNt.com>
+ *
+ * Copyright (C) 2005 Sylvain Munaut <tnt@246tNt.com>
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2. This program is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <asm/ppc_sys.h>
+
+struct ppc_sys_spec *cur_ppc_sys_spec;
+struct ppc_sys_spec ppc_sys_specs[] = {
+ {
+ .ppc_sys_name = "5200",
+ .mask = 0xffff0000,
+ .value = 0x80110000,
+ .num_devices = 15,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC52xx_MSCAN1, MPC52xx_MSCAN2, MPC52xx_SPI,
+ MPC52xx_USB, MPC52xx_BDLC, MPC52xx_PSC1, MPC52xx_PSC2,
+ MPC52xx_PSC3, MPC52xx_PSC4, MPC52xx_PSC5, MPC52xx_PSC6,
+ MPC52xx_FEC, MPC52xx_ATA, MPC52xx_I2C1, MPC52xx_I2C2,
+ },
+ },
+ { /* default match */
+ .ppc_sys_name = "",
+ .mask = 0x00000000,
+ .value = 0x00000000,
+ },
+};
diff --git a/arch/ppc/syslib/mpc83xx_devices.c b/arch/ppc/syslib/mpc83xx_devices.c
new file mode 100644
index 000000000000..5c1a919eaabf
--- /dev/null
+++ b/arch/ppc/syslib/mpc83xx_devices.c
@@ -0,0 +1,237 @@
+/*
+ * arch/ppc/platforms/83xx/mpc83xx_devices.c
+ *
+ * MPC83xx Device descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor Inc.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/fsl_devices.h>
+#include <asm/mpc83xx.h>
+#include <asm/irq.h>
+#include <asm/ppc_sys.h>
+
+/* We use offsets for IORESOURCE_MEM since we do not know at compile time
+ * what IMMRBAR is, will get fixed up by mach_mpc83xx_fixup
+ */
+
+static struct gianfar_platform_data mpc83xx_tsec1_pdata = {
+ .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR,
+ .phy_reg_addr = 0x24000,
+};
+
+static struct gianfar_platform_data mpc83xx_tsec2_pdata = {
+ .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR,
+ .phy_reg_addr = 0x24000,
+};
+
+static struct fsl_i2c_platform_data mpc83xx_fsl_i2c1_pdata = {
+ .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
+};
+
+static struct fsl_i2c_platform_data mpc83xx_fsl_i2c2_pdata = {
+ .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
+};
+
+static struct plat_serial8250_port serial_platform_data[] = {
+ [0] = {
+ .mapbase = 0x4500,
+ .irq = MPC83xx_IRQ_UART1,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ },
+ [1] = {
+ .mapbase = 0x4600,
+ .irq = MPC83xx_IRQ_UART2,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ },
+};
+
+struct platform_device ppc_sys_platform_devices[] = {
+ [MPC83xx_TSEC1] = {
+ .name = "fsl-gianfar",
+ .id = 1,
+ .dev.platform_data = &mpc83xx_tsec1_pdata,
+ .num_resources = 4,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x24000,
+ .end = 0x24fff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "tx",
+ .start = MPC83xx_IRQ_TSEC1_TX,
+ .end = MPC83xx_IRQ_TSEC1_TX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "rx",
+ .start = MPC83xx_IRQ_TSEC1_RX,
+ .end = MPC83xx_IRQ_TSEC1_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "error",
+ .start = MPC83xx_IRQ_TSEC1_ERROR,
+ .end = MPC83xx_IRQ_TSEC1_ERROR,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC83xx_TSEC2] = {
+ .name = "fsl-gianfar",
+ .id = 2,
+ .dev.platform_data = &mpc83xx_tsec2_pdata,
+ .num_resources = 4,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x25000,
+ .end = 0x25fff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "tx",
+ .start = MPC83xx_IRQ_TSEC2_TX,
+ .end = MPC83xx_IRQ_TSEC2_TX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "rx",
+ .start = MPC83xx_IRQ_TSEC2_RX,
+ .end = MPC83xx_IRQ_TSEC2_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "error",
+ .start = MPC83xx_IRQ_TSEC2_ERROR,
+ .end = MPC83xx_IRQ_TSEC2_ERROR,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC83xx_IIC1] = {
+ .name = "fsl-i2c",
+ .id = 1,
+ .dev.platform_data = &mpc83xx_fsl_i2c1_pdata,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x3000,
+ .end = 0x30ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC83xx_IRQ_IIC1,
+ .end = MPC83xx_IRQ_IIC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC83xx_IIC2] = {
+ .name = "fsl-i2c",
+ .id = 2,
+ .dev.platform_data = &mpc83xx_fsl_i2c2_pdata,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x3100,
+ .end = 0x31ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC83xx_IRQ_IIC2,
+ .end = MPC83xx_IRQ_IIC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC83xx_DUART] = {
+ .name = "serial8250",
+ .id = 0,
+ .dev.platform_data = serial_platform_data,
+ },
+ [MPC83xx_SEC2] = {
+ .name = "fsl-sec2",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x30000,
+ .end = 0x3ffff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC83xx_IRQ_SEC2,
+ .end = MPC83xx_IRQ_SEC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC83xx_USB2_DR] = {
+ .name = "fsl-usb2-dr",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x22000,
+ .end = 0x22fff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC83xx_IRQ_USB2_DR,
+ .end = MPC83xx_IRQ_USB2_DR,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC83xx_USB2_MPH] = {
+ .name = "fsl-usb2-mph",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x23000,
+ .end = 0x23fff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC83xx_IRQ_USB2_MPH,
+ .end = MPC83xx_IRQ_USB2_MPH,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+};
+
+static int __init mach_mpc83xx_fixup(struct platform_device *pdev)
+{
+ ppc_sys_fixup_mem_resource(pdev, immrbar);
+ return 0;
+}
+
+static int __init mach_mpc83xx_init(void)
+{
+ if (ppc_md.progress)
+ ppc_md.progress("mach_mpc83xx_init:enter", 0);
+ ppc_sys_device_fixup = mach_mpc83xx_fixup;
+ return 0;
+}
+
+postcore_initcall(mach_mpc83xx_init);
diff --git a/arch/ppc/syslib/mpc83xx_sys.c b/arch/ppc/syslib/mpc83xx_sys.c
new file mode 100644
index 000000000000..29aa63350025
--- /dev/null
+++ b/arch/ppc/syslib/mpc83xx_sys.c
@@ -0,0 +1,100 @@
+/*
+ * arch/ppc/platforms/83xx/mpc83xx_sys.c
+ *
+ * MPC83xx System descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor Inc.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <asm/ppc_sys.h>
+
+struct ppc_sys_spec *cur_ppc_sys_spec;
+struct ppc_sys_spec ppc_sys_specs[] = {
+ {
+ .ppc_sys_name = "8349E",
+ .mask = 0xFFFF0000,
+ .value = 0x80500000,
+ .num_devices = 8,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+ MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
+ MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+ },
+ },
+ {
+ .ppc_sys_name = "8349",
+ .mask = 0xFFFF0000,
+ .value = 0x80510000,
+ .num_devices = 7,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+ MPC83xx_IIC2, MPC83xx_DUART,
+ MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+ },
+ },
+ {
+ .ppc_sys_name = "8347E",
+ .mask = 0xFFFF0000,
+ .value = 0x80520000,
+ .num_devices = 8,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+ MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
+ MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+ },
+ },
+ {
+ .ppc_sys_name = "8347",
+ .mask = 0xFFFF0000,
+ .value = 0x80530000,
+ .num_devices = 7,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+ MPC83xx_IIC2, MPC83xx_DUART,
+ MPC83xx_USB2_DR, MPC83xx_USB2_MPH
+ },
+ },
+ {
+ .ppc_sys_name = "8343E",
+ .mask = 0xFFFF0000,
+ .value = 0x80540000,
+ .num_devices = 7,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+ MPC83xx_IIC2, MPC83xx_DUART, MPC83xx_SEC2,
+ MPC83xx_USB2_DR,
+ },
+ },
+ {
+ .ppc_sys_name = "8343",
+ .mask = 0xFFFF0000,
+ .value = 0x80550000,
+ .num_devices = 6,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC83xx_TSEC1, MPC83xx_TSEC2, MPC83xx_IIC1,
+ MPC83xx_IIC2, MPC83xx_DUART,
+ MPC83xx_USB2_DR,
+ },
+ },
+ { /* default match */
+ .ppc_sys_name = "",
+ .mask = 0x00000000,
+ .value = 0x00000000,
+ },
+};
diff --git a/arch/ppc/syslib/mpc85xx_devices.c b/arch/ppc/syslib/mpc85xx_devices.c
new file mode 100644
index 000000000000..a231795ee26f
--- /dev/null
+++ b/arch/ppc/syslib/mpc85xx_devices.c
@@ -0,0 +1,552 @@
+/*
+ * arch/ppc/platforms/85xx/mpc85xx_devices.c
+ *
+ * MPC85xx Device descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor Inc.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/serial_8250.h>
+#include <linux/fsl_devices.h>
+#include <asm/mpc85xx.h>
+#include <asm/irq.h>
+#include <asm/ppc_sys.h>
+
+/* We use offsets for IORESOURCE_MEM since we do not know at compile time
+ * what CCSRBAR is, will get fixed up by mach_mpc85xx_fixup
+ */
+
+static struct gianfar_platform_data mpc85xx_tsec1_pdata = {
+ .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR,
+ .phy_reg_addr = MPC85xx_ENET1_OFFSET,
+};
+
+static struct gianfar_platform_data mpc85xx_tsec2_pdata = {
+ .device_flags = FSL_GIANFAR_DEV_HAS_GIGABIT |
+ FSL_GIANFAR_DEV_HAS_COALESCE | FSL_GIANFAR_DEV_HAS_RMON |
+ FSL_GIANFAR_DEV_HAS_MULTI_INTR,
+ .phy_reg_addr = MPC85xx_ENET1_OFFSET,
+};
+
+static struct gianfar_platform_data mpc85xx_fec_pdata = {
+ .phy_reg_addr = MPC85xx_ENET1_OFFSET,
+};
+
+static struct fsl_i2c_platform_data mpc85xx_fsl_i2c_pdata = {
+ .device_flags = FSL_I2C_DEV_SEPARATE_DFSRR,
+};
+
+static struct plat_serial8250_port serial_platform_data[] = {
+ [0] = {
+ .mapbase = 0x4500,
+ .irq = MPC85xx_IRQ_DUART,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
+ },
+ [1] = {
+ .mapbase = 0x4600,
+ .irq = MPC85xx_IRQ_DUART,
+ .iotype = UPIO_MEM,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST | UPF_SHARE_IRQ,
+ },
+};
+
+struct platform_device ppc_sys_platform_devices[] = {
+ [MPC85xx_TSEC1] = {
+ .name = "fsl-gianfar",
+ .id = 1,
+ .dev.platform_data = &mpc85xx_tsec1_pdata,
+ .num_resources = 4,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_ENET1_OFFSET,
+ .end = MPC85xx_ENET1_OFFSET +
+ MPC85xx_ENET1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "tx",
+ .start = MPC85xx_IRQ_TSEC1_TX,
+ .end = MPC85xx_IRQ_TSEC1_TX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "rx",
+ .start = MPC85xx_IRQ_TSEC1_RX,
+ .end = MPC85xx_IRQ_TSEC1_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "error",
+ .start = MPC85xx_IRQ_TSEC1_ERROR,
+ .end = MPC85xx_IRQ_TSEC1_ERROR,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_TSEC2] = {
+ .name = "fsl-gianfar",
+ .id = 2,
+ .dev.platform_data = &mpc85xx_tsec2_pdata,
+ .num_resources = 4,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_ENET2_OFFSET,
+ .end = MPC85xx_ENET2_OFFSET +
+ MPC85xx_ENET2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "tx",
+ .start = MPC85xx_IRQ_TSEC2_TX,
+ .end = MPC85xx_IRQ_TSEC2_TX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "rx",
+ .start = MPC85xx_IRQ_TSEC2_RX,
+ .end = MPC85xx_IRQ_TSEC2_RX,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "error",
+ .start = MPC85xx_IRQ_TSEC2_ERROR,
+ .end = MPC85xx_IRQ_TSEC2_ERROR,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_FEC] = {
+ .name = "fsl-gianfar",
+ .id = 3,
+ .dev.platform_data = &mpc85xx_fec_pdata,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_ENET3_OFFSET,
+ .end = MPC85xx_ENET3_OFFSET +
+ MPC85xx_ENET3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+
+ },
+ {
+ .start = MPC85xx_IRQ_FEC,
+ .end = MPC85xx_IRQ_FEC,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_IIC1] = {
+ .name = "fsl-i2c",
+ .id = 1,
+ .dev.platform_data = &mpc85xx_fsl_i2c_pdata,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_IIC1_OFFSET,
+ .end = MPC85xx_IIC1_OFFSET +
+ MPC85xx_IIC1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC85xx_IRQ_IIC1,
+ .end = MPC85xx_IRQ_IIC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_DMA0] = {
+ .name = "fsl-dma",
+ .id = 0,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_DMA0_OFFSET,
+ .end = MPC85xx_DMA0_OFFSET +
+ MPC85xx_DMA0_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC85xx_IRQ_DMA0,
+ .end = MPC85xx_IRQ_DMA0,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_DMA1] = {
+ .name = "fsl-dma",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_DMA1_OFFSET,
+ .end = MPC85xx_DMA1_OFFSET +
+ MPC85xx_DMA1_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC85xx_IRQ_DMA1,
+ .end = MPC85xx_IRQ_DMA1,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_DMA2] = {
+ .name = "fsl-dma",
+ .id = 2,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_DMA2_OFFSET,
+ .end = MPC85xx_DMA2_OFFSET +
+ MPC85xx_DMA2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC85xx_IRQ_DMA2,
+ .end = MPC85xx_IRQ_DMA2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_DMA3] = {
+ .name = "fsl-dma",
+ .id = 3,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_DMA3_OFFSET,
+ .end = MPC85xx_DMA3_OFFSET +
+ MPC85xx_DMA3_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC85xx_IRQ_DMA3,
+ .end = MPC85xx_IRQ_DMA3,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_DUART] = {
+ .name = "serial8250",
+ .id = 0,
+ .dev.platform_data = serial_platform_data,
+ },
+ [MPC85xx_PERFMON] = {
+ .name = "fsl-perfmon",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_PERFMON_OFFSET,
+ .end = MPC85xx_PERFMON_OFFSET +
+ MPC85xx_PERFMON_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC85xx_IRQ_PERFMON,
+ .end = MPC85xx_IRQ_PERFMON,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_SEC2] = {
+ .name = "fsl-sec2",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = MPC85xx_SEC2_OFFSET,
+ .end = MPC85xx_SEC2_OFFSET +
+ MPC85xx_SEC2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = MPC85xx_IRQ_SEC2,
+ .end = MPC85xx_IRQ_SEC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+#ifdef CONFIG_CPM2
+ [MPC85xx_CPM_FCC1] = {
+ .name = "fsl-cpm-fcc",
+ .id = 1,
+ .num_resources = 3,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91300,
+ .end = 0x9131F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0x91380,
+ .end = 0x9139F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_FCC1,
+ .end = SIU_INT_FCC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_FCC2] = {
+ .name = "fsl-cpm-fcc",
+ .id = 2,
+ .num_resources = 3,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91320,
+ .end = 0x9133F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0x913A0,
+ .end = 0x913CF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_FCC2,
+ .end = SIU_INT_FCC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_FCC3] = {
+ .name = "fsl-cpm-fcc",
+ .id = 3,
+ .num_resources = 3,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91340,
+ .end = 0x9135F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0x913D0,
+ .end = 0x913FF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_FCC3,
+ .end = SIU_INT_FCC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_I2C] = {
+ .name = "fsl-cpm-i2c",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91860,
+ .end = 0x918BF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_I2C,
+ .end = SIU_INT_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_SCC1] = {
+ .name = "fsl-cpm-scc",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91A00,
+ .end = 0x91A1F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_SCC1,
+ .end = SIU_INT_SCC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_SCC2] = {
+ .name = "fsl-cpm-scc",
+ .id = 2,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91A20,
+ .end = 0x91A3F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_SCC2,
+ .end = SIU_INT_SCC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_SCC3] = {
+ .name = "fsl-cpm-scc",
+ .id = 3,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91A40,
+ .end = 0x91A5F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_SCC3,
+ .end = SIU_INT_SCC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_SCC4] = {
+ .name = "fsl-cpm-scc",
+ .id = 4,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91A60,
+ .end = 0x91A7F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_SCC4,
+ .end = SIU_INT_SCC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_SPI] = {
+ .name = "fsl-cpm-spi",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91AA0,
+ .end = 0x91AFF,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_SPI,
+ .end = SIU_INT_SPI,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_MCC1] = {
+ .name = "fsl-cpm-mcc",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91B30,
+ .end = 0x91B3F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_MCC1,
+ .end = SIU_INT_MCC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_MCC2] = {
+ .name = "fsl-cpm-mcc",
+ .id = 2,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91B50,
+ .end = 0x91B5F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_MCC2,
+ .end = SIU_INT_MCC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_SMC1] = {
+ .name = "fsl-cpm-smc",
+ .id = 1,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91A80,
+ .end = 0x91A8F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_SMC1,
+ .end = SIU_INT_SMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_SMC2] = {
+ .name = "fsl-cpm-smc",
+ .id = 2,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91A90,
+ .end = 0x91A9F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_SMC2,
+ .end = SIU_INT_SMC2,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+ [MPC85xx_CPM_USB] = {
+ .name = "fsl-cpm-usb",
+ .id = 2,
+ .num_resources = 2,
+ .resource = (struct resource[]) {
+ {
+ .start = 0x91B60,
+ .end = 0x91B7F,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = SIU_INT_USB,
+ .end = SIU_INT_USB,
+ .flags = IORESOURCE_IRQ,
+ },
+ },
+ },
+#endif /* CONFIG_CPM2 */
+};
+
+static int __init mach_mpc85xx_fixup(struct platform_device *pdev)
+{
+ ppc_sys_fixup_mem_resource(pdev, CCSRBAR);
+ return 0;
+}
+
+static int __init mach_mpc85xx_init(void)
+{
+ ppc_sys_device_fixup = mach_mpc85xx_fixup;
+ return 0;
+}
+
+postcore_initcall(mach_mpc85xx_init);
diff --git a/arch/ppc/syslib/mpc85xx_sys.c b/arch/ppc/syslib/mpc85xx_sys.c
new file mode 100644
index 000000000000..d806a92a9401
--- /dev/null
+++ b/arch/ppc/syslib/mpc85xx_sys.c
@@ -0,0 +1,118 @@
+/*
+ * arch/ppc/platforms/85xx/mpc85xx_sys.c
+ *
+ * MPC85xx System descriptions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor Inc.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <asm/ppc_sys.h>
+
+struct ppc_sys_spec *cur_ppc_sys_spec;
+struct ppc_sys_spec ppc_sys_specs[] = {
+ {
+ .ppc_sys_name = "8540",
+ .mask = 0xFFFF0000,
+ .value = 0x80300000,
+ .num_devices = 10,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_FEC, MPC85xx_IIC1,
+ MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+ MPC85xx_PERFMON, MPC85xx_DUART,
+ },
+ },
+ {
+ .ppc_sys_name = "8560",
+ .mask = 0xFFFF0000,
+ .value = 0x80700000,
+ .num_devices = 19,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+ MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+ MPC85xx_PERFMON,
+ MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+ MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3, MPC85xx_CPM_SCC4,
+ MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2, MPC85xx_CPM_FCC3,
+ MPC85xx_CPM_MCC1, MPC85xx_CPM_MCC2,
+ },
+ },
+ {
+ .ppc_sys_name = "8541",
+ .mask = 0xFFFF0000,
+ .value = 0x80720000,
+ .num_devices = 13,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+ MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+ MPC85xx_PERFMON, MPC85xx_DUART,
+ MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
+ MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+ },
+ },
+ {
+ .ppc_sys_name = "8541E",
+ .mask = 0xFFFF0000,
+ .value = 0x807A0000,
+ .num_devices = 14,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+ MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+ MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2,
+ MPC85xx_CPM_SPI, MPC85xx_CPM_I2C,
+ MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+ },
+ },
+ {
+ .ppc_sys_name = "8555",
+ .mask = 0xFFFF0000,
+ .value = 0x80710000,
+ .num_devices = 19,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+ MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+ MPC85xx_PERFMON, MPC85xx_DUART,
+ MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+ MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
+ MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+ MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
+ MPC85xx_CPM_USB,
+ },
+ },
+ {
+ .ppc_sys_name = "8555E",
+ .mask = 0xFFFF0000,
+ .value = 0x80790000,
+ .num_devices = 20,
+ .device_list = (enum ppc_sys_devices[])
+ {
+ MPC85xx_TSEC1, MPC85xx_TSEC2, MPC85xx_IIC1,
+ MPC85xx_DMA0, MPC85xx_DMA1, MPC85xx_DMA2, MPC85xx_DMA3,
+ MPC85xx_PERFMON, MPC85xx_DUART, MPC85xx_SEC2,
+ MPC85xx_CPM_SPI, MPC85xx_CPM_I2C, MPC85xx_CPM_SCC1,
+ MPC85xx_CPM_SCC2, MPC85xx_CPM_SCC3,
+ MPC85xx_CPM_FCC1, MPC85xx_CPM_FCC2,
+ MPC85xx_CPM_SMC1, MPC85xx_CPM_SMC2,
+ MPC85xx_CPM_USB,
+ },
+ },
+ { /* default match */
+ .ppc_sys_name = "",
+ .mask = 0x00000000,
+ .value = 0x00000000,
+ },
+};
diff --git a/arch/ppc/syslib/mv64360_pic.c b/arch/ppc/syslib/mv64360_pic.c
new file mode 100644
index 000000000000..74d8996418e9
--- /dev/null
+++ b/arch/ppc/syslib/mv64360_pic.c
@@ -0,0 +1,426 @@
+/*
+ * arch/ppc/kernel/mv64360_pic.c
+ *
+ * Interrupt controller support for Marvell's MV64360.
+ *
+ * Author: Rabeeh Khoury <rabeeh@galileo.co.il>
+ * Based on MV64360 PIC written by
+ * Chris Zankel <chris@mvista.com>
+ * Mark A. Greer <mgreer@mvista.com>
+ *
+ * Copyright 2004 MontaVista Software, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+/*
+ * This file contains the specific functions to support the MV64360
+ * interrupt controller.
+ *
+ * The MV64360 has two main interrupt registers (high and low) that
+ * summarizes the interrupts generated by the units of the MV64360.
+ * Each bit is assigned to an interrupt number, where the low register
+ * are assigned from IRQ0 to IRQ31 and the high cause register
+ * from IRQ32 to IRQ63
+ * The GPP (General Purpose Pins) interrupts are assigned from IRQ64 (GPP0)
+ * to IRQ95 (GPP31).
+ * get_irq() returns the lowest interrupt number that is currently asserted.
+ *
+ * Note:
+ * - This driver does not initialize the GPP when used as an interrupt
+ * input.
+ */
+
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/mv64x60.h>
+
+#ifdef CONFIG_IRQ_ALL_CPUS
+#error "The mv64360 does not support distribution of IRQs on all CPUs"
+#endif
+/* ========================== forward declaration ========================== */
+
+static void mv64360_unmask_irq(unsigned int);
+static void mv64360_mask_irq(unsigned int);
+static irqreturn_t mv64360_cpu_error_int_handler(int, void *, struct pt_regs *);
+static irqreturn_t mv64360_sram_error_int_handler(int, void *,
+ struct pt_regs *);
+static irqreturn_t mv64360_pci_error_int_handler(int, void *, struct pt_regs *);
+
+/* ========================== local declarations =========================== */
+
+struct hw_interrupt_type mv64360_pic = {
+ .typename = " mv64360 ",
+ .enable = mv64360_unmask_irq,
+ .disable = mv64360_mask_irq,
+ .ack = mv64360_mask_irq,
+ .end = mv64360_unmask_irq,
+};
+
+#define CPU_INTR_STR "mv64360 cpu interface error"
+#define SRAM_INTR_STR "mv64360 internal sram error"
+#define PCI0_INTR_STR "mv64360 pci 0 error"
+#define PCI1_INTR_STR "mv64360 pci 1 error"
+
+static struct mv64x60_handle bh;
+
+u32 mv64360_irq_base = 0; /* MV64360 handles the next 96 IRQs from here */
+
+/* mv64360_init_irq()
+ *
+ * This function initializes the interrupt controller. It assigns
+ * all interrupts from IRQ0 to IRQ95 to the mv64360 interrupt controller.
+ *
+ * Input Variable(s):
+ * None.
+ *
+ * Outpu. Variable(s):
+ * None.
+ *
+ * Returns:
+ * void
+ *
+ * Note:
+ * We register all GPP inputs as interrupt source, but disable them.
+ */
+void __init
+mv64360_init_irq(void)
+{
+ int i;
+
+ if (ppc_md.progress)
+ ppc_md.progress("mv64360_init_irq: enter", 0x0);
+
+ bh.v_base = mv64x60_get_bridge_vbase();
+
+ ppc_cached_irq_mask[0] = 0;
+ ppc_cached_irq_mask[1] = 0x0f000000; /* Enable GPP intrs */
+ ppc_cached_irq_mask[2] = 0;
+
+ /* disable all interrupts and clear current interrupts */
+ mv64x60_write(&bh, MV64x60_GPP_INTR_CAUSE, 0);
+ mv64x60_write(&bh, MV64x60_GPP_INTR_MASK, ppc_cached_irq_mask[2]);
+ mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,ppc_cached_irq_mask[0]);
+ mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,ppc_cached_irq_mask[1]);
+
+ /* All interrupts are level interrupts */
+ for (i = mv64360_irq_base; i < (mv64360_irq_base + 96); i++) {
+ irq_desc[i].status |= IRQ_LEVEL;
+ irq_desc[i].handler = &mv64360_pic;
+ }
+
+ if (ppc_md.progress)
+ ppc_md.progress("mv64360_init_irq: exit", 0x0);
+}
+
+/* mv64360_get_irq()
+ *
+ * This function returns the lowest interrupt number of all interrupts that
+ * are currently asserted.
+ *
+ * Input Variable(s):
+ * struct pt_regs* not used
+ *
+ * Output Variable(s):
+ * None.
+ *
+ * Returns:
+ * int <interrupt number> or -2 (bogus interrupt)
+ *
+ */
+int
+mv64360_get_irq(struct pt_regs *regs)
+{
+ int irq;
+ int irq_gpp;
+
+#ifdef CONFIG_SMP
+ /*
+ * Second CPU gets only doorbell (message) interrupts.
+ * The doorbell interrupt is BIT28 in the main interrupt low cause reg.
+ */
+ int cpu_nr = smp_processor_id();
+ if (cpu_nr == 1) {
+ if (!(mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO) &
+ (1 << MV64x60_IRQ_DOORBELL)))
+ return -1;
+ return mv64360_irq_base + MV64x60_IRQ_DOORBELL;
+ }
+#endif
+
+ irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_LO);
+ irq = __ilog2((irq & 0x3dfffffe) & ppc_cached_irq_mask[0]);
+
+ if (irq == -1) {
+ irq = mv64x60_read(&bh, MV64360_IC_MAIN_CAUSE_HI);
+ irq = __ilog2((irq & 0x1f0003f7) & ppc_cached_irq_mask[1]);
+
+ if (irq == -1)
+ irq = -2; /* bogus interrupt, should never happen */
+ else {
+ if ((irq >= 24) && (irq < MV64x60_IRQ_DOORBELL)) {
+ irq_gpp = mv64x60_read(&bh,
+ MV64x60_GPP_INTR_CAUSE);
+ irq_gpp = __ilog2(irq_gpp &
+ ppc_cached_irq_mask[2]);
+
+ if (irq_gpp == -1)
+ irq = -2;
+ else {
+ irq = irq_gpp + 64;
+ mv64x60_write(&bh,
+ MV64x60_GPP_INTR_CAUSE,
+ ~(1 << (irq - 64)));
+ }
+ }
+ else
+ irq += 32;
+ }
+ }
+
+ (void)mv64x60_read(&bh, MV64x60_GPP_INTR_CAUSE);
+
+ if (irq < 0)
+ return (irq);
+ else
+ return (mv64360_irq_base + irq);
+}
+
+/* mv64360_unmask_irq()
+ *
+ * This function enables an interrupt.
+ *
+ * Input Variable(s):
+ * unsigned int interrupt number (IRQ0...IRQ95).
+ *
+ * Output Variable(s):
+ * None.
+ *
+ * Returns:
+ * void
+ */
+static void
+mv64360_unmask_irq(unsigned int irq)
+{
+#ifdef CONFIG_SMP
+ /* second CPU gets only doorbell interrupts */
+ if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) {
+ mv64x60_set_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO,
+ (1 << MV64x60_IRQ_DOORBELL));
+ return;
+ }
+#endif
+ irq -= mv64360_irq_base;
+
+ if (irq > 31) {
+ if (irq > 63) /* unmask GPP irq */
+ mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
+ ppc_cached_irq_mask[2] |= (1 << (irq - 64)));
+ else /* mask high interrupt register */
+ mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,
+ ppc_cached_irq_mask[1] |= (1 << (irq - 32)));
+ }
+ else /* mask low interrupt register */
+ mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,
+ ppc_cached_irq_mask[0] |= (1 << irq));
+
+ (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
+ return;
+}
+
+/* mv64360_mask_irq()
+ *
+ * This function disables the requested interrupt.
+ *
+ * Input Variable(s):
+ * unsigned int interrupt number (IRQ0...IRQ95).
+ *
+ * Output Variable(s):
+ * None.
+ *
+ * Returns:
+ * void
+ */
+static void
+mv64360_mask_irq(unsigned int irq)
+{
+#ifdef CONFIG_SMP
+ if ((irq - mv64360_irq_base) == MV64x60_IRQ_DOORBELL) {
+ mv64x60_clr_bits(&bh, MV64360_IC_CPU1_INTR_MASK_LO,
+ (1 << MV64x60_IRQ_DOORBELL));
+ return;
+ }
+#endif
+ irq -= mv64360_irq_base;
+
+ if (irq > 31) {
+ if (irq > 63) /* mask GPP irq */
+ mv64x60_write(&bh, MV64x60_GPP_INTR_MASK,
+ ppc_cached_irq_mask[2] &= ~(1 << (irq - 64)));
+ else /* mask high interrupt register */
+ mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_HI,
+ ppc_cached_irq_mask[1] &= ~(1 << (irq - 32)));
+ }
+ else /* mask low interrupt register */
+ mv64x60_write(&bh, MV64360_IC_CPU0_INTR_MASK_LO,
+ ppc_cached_irq_mask[0] &= ~(1 << irq));
+
+ (void)mv64x60_read(&bh, MV64x60_GPP_INTR_MASK);
+ return;
+}
+
+static irqreturn_t
+mv64360_cpu_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk(KERN_ERR "mv64360_cpu_error_int_handler: %s 0x%08x\n",
+ "Error on CPU interface - Cause regiser",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_CAUSE));
+ printk(KERN_ERR "\tCPU error register dump:\n");
+ printk(KERN_ERR "\tAddress low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_LO));
+ printk(KERN_ERR "\tAddress high 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_ADDR_HI));
+ printk(KERN_ERR "\tData low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_LO));
+ printk(KERN_ERR "\tData high 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_DATA_HI));
+ printk(KERN_ERR "\tParity 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_CPU_ERR_PARITY));
+ mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+mv64360_sram_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ printk(KERN_ERR "mv64360_sram_error_int_handler: %s 0x%08x\n",
+ "Error in internal SRAM - Cause register",
+ mv64x60_read(&bh, MV64360_SRAM_ERR_CAUSE));
+ printk(KERN_ERR "\tSRAM error register dump:\n");
+ printk(KERN_ERR "\tAddress Low 0x%08x\n",
+ mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_LO));
+ printk(KERN_ERR "\tAddress High 0x%08x\n",
+ mv64x60_read(&bh, MV64360_SRAM_ERR_ADDR_HI));
+ printk(KERN_ERR "\tData Low 0x%08x\n",
+ mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_LO));
+ printk(KERN_ERR "\tData High 0x%08x\n",
+ mv64x60_read(&bh, MV64360_SRAM_ERR_DATA_HI));
+ printk(KERN_ERR "\tParity 0x%08x\n",
+ mv64x60_read(&bh, MV64360_SRAM_ERR_PARITY));
+ mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+mv64360_pci_error_int_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 val;
+ unsigned int pci_bus = (unsigned int)dev_id;
+
+ if (pci_bus == 0) { /* Error on PCI 0 */
+ val = mv64x60_read(&bh, MV64x60_PCI0_ERR_CAUSE);
+ printk(KERN_ERR "%s: Error in PCI %d Interface\n",
+ "mv64360_pci_error_int_handler", pci_bus);
+ printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
+ printk(KERN_ERR "\tCause register 0x%08x\n", val);
+ printk(KERN_ERR "\tAddress Low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_LO));
+ printk(KERN_ERR "\tAddress High 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_ADDR_HI));
+ printk(KERN_ERR "\tAttribute 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_DATA_LO));
+ printk(KERN_ERR "\tCommand 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI0_ERR_CMD));
+ mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, ~val);
+ }
+ if (pci_bus == 1) { /* Error on PCI 1 */
+ val = mv64x60_read(&bh, MV64x60_PCI1_ERR_CAUSE);
+ printk(KERN_ERR "%s: Error in PCI %d Interface\n",
+ "mv64360_pci_error_int_handler", pci_bus);
+ printk(KERN_ERR "\tPCI %d error register dump:\n", pci_bus);
+ printk(KERN_ERR "\tCause register 0x%08x\n", val);
+ printk(KERN_ERR "\tAddress Low 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_LO));
+ printk(KERN_ERR "\tAddress High 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_ADDR_HI));
+ printk(KERN_ERR "\tAttribute 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_DATA_LO));
+ printk(KERN_ERR "\tCommand 0x%08x\n",
+ mv64x60_read(&bh, MV64x60_PCI1_ERR_CMD));
+ mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, ~val);
+ }
+ return IRQ_HANDLED;
+}
+
+static int __init
+mv64360_register_hdlrs(void)
+{
+ u32 mask;
+ int rc;
+
+ /* Clear old errors and register CPU interface error intr handler */
+ mv64x60_write(&bh, MV64x60_CPU_ERR_CAUSE, 0);
+ if ((rc = request_irq(MV64x60_IRQ_CPU_ERR + mv64360_irq_base,
+ mv64360_cpu_error_int_handler, SA_INTERRUPT, CPU_INTR_STR, 0)))
+ printk(KERN_WARNING "Can't register cpu error handler: %d", rc);
+
+ mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0);
+ mv64x60_write(&bh, MV64x60_CPU_ERR_MASK, 0x000000ff);
+
+ /* Clear old errors and register internal SRAM error intr handler */
+ mv64x60_write(&bh, MV64360_SRAM_ERR_CAUSE, 0);
+ if ((rc = request_irq(MV64360_IRQ_SRAM_PAR_ERR + mv64360_irq_base,
+ mv64360_sram_error_int_handler,SA_INTERRUPT,SRAM_INTR_STR, 0)))
+ printk(KERN_WARNING "Can't register SRAM error handler: %d",rc);
+
+ /*
+ * Bit 0 reserved on 64360 and erratum FEr PCI-#11 (PCI internal
+ * data parity error set incorrectly) on rev 0 & 1 of 64460 requires
+ * bit 0 to be cleared.
+ */
+ mask = 0x00a50c24;
+
+ if ((mv64x60_get_bridge_type() == MV64x60_TYPE_MV64460) &&
+ (mv64x60_get_bridge_rev() > 1))
+ mask |= 0x1; /* enable DPErr on 64460 */
+
+ /* Clear old errors and register PCI 0 error intr handler */
+ mv64x60_write(&bh, MV64x60_PCI0_ERR_CAUSE, 0);
+ if ((rc = request_irq(MV64360_IRQ_PCI0 + mv64360_irq_base,
+ mv64360_pci_error_int_handler,
+ SA_INTERRUPT, PCI0_INTR_STR, (void *)0)))
+ printk(KERN_WARNING "Can't register pci 0 error handler: %d",
+ rc);
+
+ mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, 0);
+ mv64x60_write(&bh, MV64x60_PCI0_ERR_MASK, mask);
+
+ /* Clear old errors and register PCI 1 error intr handler */
+ mv64x60_write(&bh, MV64x60_PCI1_ERR_CAUSE, 0);
+ if ((rc = request_irq(MV64360_IRQ_PCI1 + mv64360_irq_base,
+ mv64360_pci_error_int_handler,
+ SA_INTERRUPT, PCI1_INTR_STR, (void *)1)))
+ printk(KERN_WARNING "Can't register pci 1 error handler: %d",
+ rc);
+
+ mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, 0);
+ mv64x60_write(&bh, MV64x60_PCI1_ERR_MASK, mask);
+
+ return 0;
+}
+
+arch_initcall(mv64360_register_hdlrs);
diff --git a/arch/ppc/syslib/mv64x60.c b/arch/ppc/syslib/mv64x60.c
new file mode 100644
index 000000000000..7b241e7876bd
--- /dev/null
+++ b/arch/ppc/syslib/mv64x60.c
@@ -0,0 +1,2392 @@
+/*
+ * arch/ppc/syslib/mv64x60.c
+ *
+ * Common routines for the Marvell/Galileo Discovery line of host bridges
+ * (gt64260, mv64360, mv64460, ...).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2004 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/bootmem.h>
+#include <linux/spinlock.h>
+#include <linux/mv643xx.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/delay.h>
+#include <asm/mv64x60.h>
+
+
+u8 mv64x60_pci_exclude_bridge = 1;
+spinlock_t mv64x60_lock = SPIN_LOCK_UNLOCKED;
+
+static phys_addr_t mv64x60_bridge_pbase = 0;
+static void *mv64x60_bridge_vbase = 0;
+static u32 mv64x60_bridge_type = MV64x60_TYPE_INVALID;
+static u32 mv64x60_bridge_rev = 0;
+
+static u32 gt64260_translate_size(u32 base, u32 size, u32 num_bits);
+static u32 gt64260_untranslate_size(u32 base, u32 size, u32 num_bits);
+static void gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus,
+ u32 window, u32 base);
+static void gt64260_set_pci2regs_window(struct mv64x60_handle *bh,
+ struct pci_controller *hose, u32 bus, u32 base);
+static u32 gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window);
+static void gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window);
+static void gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window);
+static void gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window);
+static void gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window);
+static void gt64260_disable_all_windows(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si);
+static void gt64260a_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si);
+static void gt64260b_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si);
+
+static u32 mv64360_translate_size(u32 base, u32 size, u32 num_bits);
+static u32 mv64360_untranslate_size(u32 base, u32 size, u32 num_bits);
+static void mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus,
+ u32 window, u32 base);
+static void mv64360_set_pci2regs_window(struct mv64x60_handle *bh,
+ struct pci_controller *hose, u32 bus, u32 base);
+static u32 mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window);
+static void mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window);
+static void mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window);
+static void mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window);
+static void mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window);
+static void mv64360_disable_all_windows(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si);
+static void mv64360_config_io2mem_windows(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si,
+ u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2]);
+static void mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base);
+static void mv64360_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si);
+static void mv64460_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si);
+
+
+/*
+ * Define tables that have the chip-specific info for each type of
+ * Marvell bridge chip.
+ */
+static struct mv64x60_chip_info gt64260a_ci __initdata = { /* GT64260A */
+ .translate_size = gt64260_translate_size,
+ .untranslate_size = gt64260_untranslate_size,
+ .set_pci2mem_window = gt64260_set_pci2mem_window,
+ .set_pci2regs_window = gt64260_set_pci2regs_window,
+ .is_enabled_32bit = gt64260_is_enabled_32bit,
+ .enable_window_32bit = gt64260_enable_window_32bit,
+ .disable_window_32bit = gt64260_disable_window_32bit,
+ .enable_window_64bit = gt64260_enable_window_64bit,
+ .disable_window_64bit = gt64260_disable_window_64bit,
+ .disable_all_windows = gt64260_disable_all_windows,
+ .chip_specific_init = gt64260a_chip_specific_init,
+ .window_tab_32bit = gt64260_32bit_windows,
+ .window_tab_64bit = gt64260_64bit_windows,
+};
+
+static struct mv64x60_chip_info gt64260b_ci __initdata = { /* GT64260B */
+ .translate_size = gt64260_translate_size,
+ .untranslate_size = gt64260_untranslate_size,
+ .set_pci2mem_window = gt64260_set_pci2mem_window,
+ .set_pci2regs_window = gt64260_set_pci2regs_window,
+ .is_enabled_32bit = gt64260_is_enabled_32bit,
+ .enable_window_32bit = gt64260_enable_window_32bit,
+ .disable_window_32bit = gt64260_disable_window_32bit,
+ .enable_window_64bit = gt64260_enable_window_64bit,
+ .disable_window_64bit = gt64260_disable_window_64bit,
+ .disable_all_windows = gt64260_disable_all_windows,
+ .chip_specific_init = gt64260b_chip_specific_init,
+ .window_tab_32bit = gt64260_32bit_windows,
+ .window_tab_64bit = gt64260_64bit_windows,
+};
+
+static struct mv64x60_chip_info mv64360_ci __initdata = { /* MV64360 */
+ .translate_size = mv64360_translate_size,
+ .untranslate_size = mv64360_untranslate_size,
+ .set_pci2mem_window = mv64360_set_pci2mem_window,
+ .set_pci2regs_window = mv64360_set_pci2regs_window,
+ .is_enabled_32bit = mv64360_is_enabled_32bit,
+ .enable_window_32bit = mv64360_enable_window_32bit,
+ .disable_window_32bit = mv64360_disable_window_32bit,
+ .enable_window_64bit = mv64360_enable_window_64bit,
+ .disable_window_64bit = mv64360_disable_window_64bit,
+ .disable_all_windows = mv64360_disable_all_windows,
+ .config_io2mem_windows = mv64360_config_io2mem_windows,
+ .set_mpsc2regs_window = mv64360_set_mpsc2regs_window,
+ .chip_specific_init = mv64360_chip_specific_init,
+ .window_tab_32bit = mv64360_32bit_windows,
+ .window_tab_64bit = mv64360_64bit_windows,
+};
+
+static struct mv64x60_chip_info mv64460_ci __initdata = { /* MV64460 */
+ .translate_size = mv64360_translate_size,
+ .untranslate_size = mv64360_untranslate_size,
+ .set_pci2mem_window = mv64360_set_pci2mem_window,
+ .set_pci2regs_window = mv64360_set_pci2regs_window,
+ .is_enabled_32bit = mv64360_is_enabled_32bit,
+ .enable_window_32bit = mv64360_enable_window_32bit,
+ .disable_window_32bit = mv64360_disable_window_32bit,
+ .enable_window_64bit = mv64360_enable_window_64bit,
+ .disable_window_64bit = mv64360_disable_window_64bit,
+ .disable_all_windows = mv64360_disable_all_windows,
+ .config_io2mem_windows = mv64360_config_io2mem_windows,
+ .set_mpsc2regs_window = mv64360_set_mpsc2regs_window,
+ .chip_specific_init = mv64460_chip_specific_init,
+ .window_tab_32bit = mv64360_32bit_windows,
+ .window_tab_64bit = mv64360_64bit_windows,
+};
+
+/*
+ *****************************************************************************
+ *
+ * Platform Device Definitions
+ *
+ *****************************************************************************
+ */
+#ifdef CONFIG_SERIAL_MPSC
+static struct mpsc_shared_pdata mv64x60_mpsc_shared_pdata = {
+ .mrr_val = 0x3ffffe38,
+ .rcrr_val = 0,
+ .tcrr_val = 0,
+ .intr_cause_val = 0,
+ .intr_mask_val = 0,
+};
+
+static struct resource mv64x60_mpsc_shared_resources[] = {
+ /* Do not change the order of the IORESOURCE_MEM resources */
+ [0] = {
+ .name = "mpsc routing base",
+ .start = MV64x60_MPSC_ROUTING_OFFSET,
+ .end = MV64x60_MPSC_ROUTING_OFFSET +
+ MPSC_ROUTING_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "sdma intr base",
+ .start = MV64x60_SDMA_INTR_OFFSET,
+ .end = MV64x60_SDMA_INTR_OFFSET +
+ MPSC_SDMA_INTR_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mpsc_shared_device = { /* Shared device */
+ .name = MPSC_SHARED_NAME,
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mv64x60_mpsc_shared_resources),
+ .resource = mv64x60_mpsc_shared_resources,
+ .dev = {
+ .platform_data = &mv64x60_mpsc_shared_pdata,
+ },
+};
+
+static struct mpsc_pdata mv64x60_mpsc0_pdata = {
+ .mirror_regs = 0,
+ .cache_mgmt = 0,
+ .max_idle = 0,
+ .default_baud = 9600,
+ .default_bits = 8,
+ .default_parity = 'n',
+ .default_flow = 'n',
+ .chr_1_val = 0x00000000,
+ .chr_2_val = 0x00000000,
+ .chr_10_val = 0x00000003,
+ .mpcr_val = 0,
+ .bcr_val = 0,
+ .brg_can_tune = 0,
+ .brg_clk_src = 8, /* Default to TCLK */
+ .brg_clk_freq = 100000000, /* Default to 100 MHz */
+};
+
+static struct resource mv64x60_mpsc0_resources[] = {
+ /* Do not change the order of the IORESOURCE_MEM resources */
+ [0] = {
+ .name = "mpsc 0 base",
+ .start = MV64x60_MPSC_0_OFFSET,
+ .end = MV64x60_MPSC_0_OFFSET + MPSC_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "sdma 0 base",
+ .start = MV64x60_SDMA_0_OFFSET,
+ .end = MV64x60_SDMA_0_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .name = "brg 0 base",
+ .start = MV64x60_BRG_0_OFFSET,
+ .end = MV64x60_BRG_0_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [3] = {
+ .name = "sdma 0 irq",
+ .start = MV64x60_IRQ_SDMA_0,
+ .end = MV64x60_IRQ_SDMA_0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mpsc0_device = {
+ .name = MPSC_CTLR_NAME,
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mv64x60_mpsc0_resources),
+ .resource = mv64x60_mpsc0_resources,
+ .dev = {
+ .platform_data = &mv64x60_mpsc0_pdata,
+ },
+};
+
+static struct mpsc_pdata mv64x60_mpsc1_pdata = {
+ .mirror_regs = 0,
+ .cache_mgmt = 0,
+ .max_idle = 0,
+ .default_baud = 9600,
+ .default_bits = 8,
+ .default_parity = 'n',
+ .default_flow = 'n',
+ .chr_1_val = 0x00000000,
+ .chr_1_val = 0x00000000,
+ .chr_2_val = 0x00000000,
+ .chr_10_val = 0x00000003,
+ .mpcr_val = 0,
+ .bcr_val = 0,
+ .brg_can_tune = 0,
+ .brg_clk_src = 8, /* Default to TCLK */
+ .brg_clk_freq = 100000000, /* Default to 100 MHz */
+};
+
+static struct resource mv64x60_mpsc1_resources[] = {
+ /* Do not change the order of the IORESOURCE_MEM resources */
+ [0] = {
+ .name = "mpsc 1 base",
+ .start = MV64x60_MPSC_1_OFFSET,
+ .end = MV64x60_MPSC_1_OFFSET + MPSC_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "sdma 1 base",
+ .start = MV64x60_SDMA_1_OFFSET,
+ .end = MV64x60_SDMA_1_OFFSET + MPSC_SDMA_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [2] = {
+ .name = "brg 1 base",
+ .start = MV64x60_BRG_1_OFFSET,
+ .end = MV64x60_BRG_1_OFFSET + MPSC_BRG_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [3] = {
+ .name = "sdma 1 irq",
+ .start = MV64360_IRQ_SDMA_1,
+ .end = MV64360_IRQ_SDMA_1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device mpsc1_device = {
+ .name = MPSC_CTLR_NAME,
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mv64x60_mpsc1_resources),
+ .resource = mv64x60_mpsc1_resources,
+ .dev = {
+ .platform_data = &mv64x60_mpsc1_pdata,
+ },
+};
+#endif
+
+#ifdef CONFIG_MV643XX_ETH
+static struct resource mv64x60_eth_shared_resources[] = {
+ [0] = {
+ .name = "ethernet shared base",
+ .start = MV643XX_ETH_SHARED_REGS,
+ .end = MV643XX_ETH_SHARED_REGS +
+ MV643XX_ETH_SHARED_REGS_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device mv64x60_eth_shared_device = {
+ .name = MV643XX_ETH_SHARED_NAME,
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mv64x60_eth_shared_resources),
+ .resource = mv64x60_eth_shared_resources,
+};
+
+#ifdef CONFIG_MV643XX_ETH_0
+static struct resource mv64x60_eth0_resources[] = {
+ [0] = {
+ .name = "eth0 irq",
+ .start = MV64x60_IRQ_ETH_0,
+ .end = MV64x60_IRQ_ETH_0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mv643xx_eth_platform_data eth0_pd;
+
+static struct platform_device eth0_device = {
+ .name = MV643XX_ETH_NAME,
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mv64x60_eth0_resources),
+ .resource = mv64x60_eth0_resources,
+ .dev = {
+ .platform_data = &eth0_pd,
+ },
+};
+#endif
+
+#ifdef CONFIG_MV643XX_ETH_1
+static struct resource mv64x60_eth1_resources[] = {
+ [0] = {
+ .name = "eth1 irq",
+ .start = MV64x60_IRQ_ETH_1,
+ .end = MV64x60_IRQ_ETH_1,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mv643xx_eth_platform_data eth1_pd;
+
+static struct platform_device eth1_device = {
+ .name = MV643XX_ETH_NAME,
+ .id = 1,
+ .num_resources = ARRAY_SIZE(mv64x60_eth1_resources),
+ .resource = mv64x60_eth1_resources,
+ .dev = {
+ .platform_data = &eth1_pd,
+ },
+};
+#endif
+
+#ifdef CONFIG_MV643XX_ETH_2
+static struct resource mv64x60_eth2_resources[] = {
+ [0] = {
+ .name = "eth2 irq",
+ .start = MV64x60_IRQ_ETH_2,
+ .end = MV64x60_IRQ_ETH_2,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct mv643xx_eth_platform_data eth2_pd;
+
+static struct platform_device eth2_device = {
+ .name = MV643XX_ETH_NAME,
+ .id = 2,
+ .num_resources = ARRAY_SIZE(mv64x60_eth2_resources),
+ .resource = mv64x60_eth2_resources,
+ .dev = {
+ .platform_data = &eth2_pd,
+ },
+};
+#endif
+#endif
+
+#ifdef CONFIG_I2C_MV64XXX
+static struct mv64xxx_i2c_pdata mv64xxx_i2c_pdata = {
+ .freq_m = 8,
+ .freq_n = 3,
+ .timeout = 1000, /* Default timeout of 1 second */
+ .retries = 1,
+};
+
+static struct resource mv64xxx_i2c_resources[] = {
+ /* Do not change the order of the IORESOURCE_MEM resources */
+ [0] = {
+ .name = "mv64xxx i2c base",
+ .start = MV64XXX_I2C_OFFSET,
+ .end = MV64XXX_I2C_OFFSET + MV64XXX_I2C_REG_BLOCK_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .name = "mv64xxx i2c irq",
+ .start = MV64x60_IRQ_I2C,
+ .end = MV64x60_IRQ_I2C,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device i2c_device = {
+ .name = MV64XXX_I2C_CTLR_NAME,
+ .id = 0,
+ .num_resources = ARRAY_SIZE(mv64xxx_i2c_resources),
+ .resource = mv64xxx_i2c_resources,
+ .dev = {
+ .platform_data = &mv64xxx_i2c_pdata,
+ },
+};
+#endif
+
+static struct platform_device *mv64x60_pd_devs[] __initdata = {
+#ifdef CONFIG_SERIAL_MPSC
+ &mpsc_shared_device,
+ &mpsc0_device,
+ &mpsc1_device,
+#endif
+#ifdef CONFIG_MV643XX_ETH
+ &mv64x60_eth_shared_device,
+#endif
+#ifdef CONFIG_MV643XX_ETH_0
+ &eth0_device,
+#endif
+#ifdef CONFIG_MV643XX_ETH_1
+ &eth1_device,
+#endif
+#ifdef CONFIG_MV643XX_ETH_2
+ &eth2_device,
+#endif
+#ifdef CONFIG_I2C_MV64XXX
+ &i2c_device,
+#endif
+};
+
+/*
+ *****************************************************************************
+ *
+ * Bridge Initialization Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_init()
+ *
+ * Initialze the bridge based on setting passed in via 'si'. The bridge
+ * handle, 'bh', will be set so that it can be used to make subsequent
+ * calls to routines in this file.
+ */
+int __init
+mv64x60_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si)
+{
+ u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2];
+
+ if (ppc_md.progress)
+ ppc_md.progress("mv64x60 initialization", 0x0);
+
+ spin_lock_init(&mv64x60_lock);
+ mv64x60_early_init(bh, si);
+
+ if (mv64x60_get_type(bh) || mv64x60_setup_for_chip(bh)) {
+ iounmap(bh->v_base);
+ bh->v_base = 0;
+ if (ppc_md.progress)
+ ppc_md.progress("mv64x60_init: Can't determine chip",0);
+ return -1;
+ }
+
+ bh->ci->disable_all_windows(bh, si);
+ mv64x60_get_mem_windows(bh, mem_windows);
+ mv64x60_config_cpu2mem_windows(bh, si, mem_windows);
+
+ if (bh->ci->config_io2mem_windows)
+ bh->ci->config_io2mem_windows(bh, si, mem_windows);
+ if (bh->ci->set_mpsc2regs_window)
+ bh->ci->set_mpsc2regs_window(bh, si->phys_reg_base);
+
+ if (si->pci_1.enable_bus) {
+ bh->io_base_b = (u32)ioremap(si->pci_1.pci_io.cpu_base,
+ si->pci_1.pci_io.size);
+ isa_io_base = bh->io_base_b;
+ }
+
+ if (si->pci_0.enable_bus) {
+ bh->io_base_a = (u32)ioremap(si->pci_0.pci_io.cpu_base,
+ si->pci_0.pci_io.size);
+ isa_io_base = bh->io_base_a;
+
+ mv64x60_alloc_hose(bh, MV64x60_PCI0_CONFIG_ADDR,
+ MV64x60_PCI0_CONFIG_DATA, &bh->hose_a);
+ mv64x60_config_resources(bh->hose_a, &si->pci_0, bh->io_base_a);
+ mv64x60_config_pci_params(bh->hose_a, &si->pci_0);
+
+ mv64x60_config_cpu2pci_windows(bh, &si->pci_0, 0);
+ mv64x60_config_pci2mem_windows(bh, bh->hose_a, &si->pci_0, 0,
+ mem_windows);
+ bh->ci->set_pci2regs_window(bh, bh->hose_a, 0,
+ si->phys_reg_base);
+ }
+
+ if (si->pci_1.enable_bus) {
+ mv64x60_alloc_hose(bh, MV64x60_PCI1_CONFIG_ADDR,
+ MV64x60_PCI1_CONFIG_DATA, &bh->hose_b);
+ mv64x60_config_resources(bh->hose_b, &si->pci_1, bh->io_base_b);
+ mv64x60_config_pci_params(bh->hose_b, &si->pci_1);
+
+ mv64x60_config_cpu2pci_windows(bh, &si->pci_1, 1);
+ mv64x60_config_pci2mem_windows(bh, bh->hose_b, &si->pci_1, 1,
+ mem_windows);
+ bh->ci->set_pci2regs_window(bh, bh->hose_b, 1,
+ si->phys_reg_base);
+ }
+
+ bh->ci->chip_specific_init(bh, si);
+ mv64x60_pd_fixup(bh, mv64x60_pd_devs, ARRAY_SIZE(mv64x60_pd_devs));
+
+ return 0;
+}
+
+/*
+ * mv64x60_early_init()
+ *
+ * Do some bridge work that must take place before we start messing with
+ * the bridge for real.
+ */
+void __init
+mv64x60_early_init(struct mv64x60_handle *bh, struct mv64x60_setup_info *si)
+{
+ struct pci_controller hose_a, hose_b;
+
+ memset(bh, 0, sizeof(*bh));
+
+ bh->p_base = si->phys_reg_base;
+ bh->v_base = ioremap(bh->p_base, MV64x60_INTERNAL_SPACE_SIZE);
+
+ mv64x60_bridge_pbase = bh->p_base;
+ mv64x60_bridge_vbase = bh->v_base;
+
+ /* Assuming pci mode [reserved] bits 4:5 on 64260 are 0 */
+ bh->pci_mode_a = mv64x60_read(bh, MV64x60_PCI0_MODE) &
+ MV64x60_PCIMODE_MASK;
+ bh->pci_mode_b = mv64x60_read(bh, MV64x60_PCI1_MODE) &
+ MV64x60_PCIMODE_MASK;
+
+ /* Need temporary hose structs to call mv64x60_set_bus() */
+ memset(&hose_a, 0, sizeof(hose_a));
+ memset(&hose_b, 0, sizeof(hose_b));
+ setup_indirect_pci_nomap(&hose_a, bh->v_base + MV64x60_PCI0_CONFIG_ADDR,
+ bh->v_base + MV64x60_PCI0_CONFIG_DATA);
+ setup_indirect_pci_nomap(&hose_b, bh->v_base + MV64x60_PCI1_CONFIG_ADDR,
+ bh->v_base + MV64x60_PCI1_CONFIG_DATA);
+ bh->hose_a = &hose_a;
+ bh->hose_b = &hose_b;
+
+ mv64x60_set_bus(bh, 0, 0);
+ mv64x60_set_bus(bh, 1, 0);
+
+ bh->hose_a = NULL;
+ bh->hose_b = NULL;
+
+ /* Clear bit 0 of PCI addr decode control so PCI->CPU remap 1:1 */
+ mv64x60_clr_bits(bh, MV64x60_PCI0_PCI_DECODE_CNTL, 0x00000001);
+ mv64x60_clr_bits(bh, MV64x60_PCI1_PCI_DECODE_CNTL, 0x00000001);
+
+ /* Bit 12 MUST be 0; set bit 27--don't auto-update cpu remap regs */
+ mv64x60_clr_bits(bh, MV64x60_CPU_CONFIG, (1<<12));
+ mv64x60_set_bits(bh, MV64x60_CPU_CONFIG, (1<<27));
+
+ mv64x60_set_bits(bh, MV64x60_PCI0_TO_RETRY, 0xffff);
+ mv64x60_set_bits(bh, MV64x60_PCI1_TO_RETRY, 0xffff);
+
+ return;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Window Config Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_get_32bit_window()
+ *
+ * Determine the base address and size of a 32-bit window on the bridge.
+ */
+void __init
+mv64x60_get_32bit_window(struct mv64x60_handle *bh, u32 window,
+ u32 *base, u32 *size)
+{
+ u32 val, base_reg, size_reg, base_bits, size_bits;
+ u32 (*get_from_field)(u32 val, u32 num_bits);
+
+ base_reg = bh->ci->window_tab_32bit[window].base_reg;
+
+ if (base_reg != 0) {
+ size_reg = bh->ci->window_tab_32bit[window].size_reg;
+ base_bits = bh->ci->window_tab_32bit[window].base_bits;
+ size_bits = bh->ci->window_tab_32bit[window].size_bits;
+ get_from_field= bh->ci->window_tab_32bit[window].get_from_field;
+
+ val = mv64x60_read(bh, base_reg);
+ *base = get_from_field(val, base_bits);
+
+ if (size_reg != 0) {
+ val = mv64x60_read(bh, size_reg);
+ val = get_from_field(val, size_bits);
+ *size = bh->ci->untranslate_size(*base, val, size_bits);
+ }
+ else
+ *size = 0;
+ }
+ else {
+ *base = 0;
+ *size = 0;
+ }
+
+ pr_debug("get 32bit window: %d, base: 0x%x, size: 0x%x\n",
+ window, *base, *size);
+
+ return;
+}
+
+/*
+ * mv64x60_set_32bit_window()
+ *
+ * Set the base address and size of a 32-bit window on the bridge.
+ */
+void __init
+mv64x60_set_32bit_window(struct mv64x60_handle *bh, u32 window,
+ u32 base, u32 size, u32 other_bits)
+{
+ u32 val, base_reg, size_reg, base_bits, size_bits;
+ u32 (*map_to_field)(u32 val, u32 num_bits);
+
+ pr_debug("set 32bit window: %d, base: 0x%x, size: 0x%x, other: 0x%x\n",
+ window, base, size, other_bits);
+
+ base_reg = bh->ci->window_tab_32bit[window].base_reg;
+
+ if (base_reg != 0) {
+ size_reg = bh->ci->window_tab_32bit[window].size_reg;
+ base_bits = bh->ci->window_tab_32bit[window].base_bits;
+ size_bits = bh->ci->window_tab_32bit[window].size_bits;
+ map_to_field = bh->ci->window_tab_32bit[window].map_to_field;
+
+ val = map_to_field(base, base_bits) | other_bits;
+ mv64x60_write(bh, base_reg, val);
+
+ if (size_reg != 0) {
+ val = bh->ci->translate_size(base, size, size_bits);
+ val = map_to_field(val, size_bits);
+ mv64x60_write(bh, size_reg, val);
+ }
+
+ (void)mv64x60_read(bh, base_reg); /* Flush FIFO */
+ }
+
+ return;
+}
+
+/*
+ * mv64x60_get_64bit_window()
+ *
+ * Determine the base address and size of a 64-bit window on the bridge.
+ */
+void __init
+mv64x60_get_64bit_window(struct mv64x60_handle *bh, u32 window,
+ u32 *base_hi, u32 *base_lo, u32 *size)
+{
+ u32 val, base_lo_reg, size_reg, base_lo_bits, size_bits;
+ u32 (*get_from_field)(u32 val, u32 num_bits);
+
+ base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg;
+
+ if (base_lo_reg != 0) {
+ size_reg = bh->ci->window_tab_64bit[window].size_reg;
+ base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits;
+ size_bits = bh->ci->window_tab_64bit[window].size_bits;
+ get_from_field= bh->ci->window_tab_64bit[window].get_from_field;
+
+ *base_hi = mv64x60_read(bh,
+ bh->ci->window_tab_64bit[window].base_hi_reg);
+
+ val = mv64x60_read(bh, base_lo_reg);
+ *base_lo = get_from_field(val, base_lo_bits);
+
+ if (size_reg != 0) {
+ val = mv64x60_read(bh, size_reg);
+ val = get_from_field(val, size_bits);
+ *size = bh->ci->untranslate_size(*base_lo, val,
+ size_bits);
+ }
+ else
+ *size = 0;
+ }
+ else {
+ *base_hi = 0;
+ *base_lo = 0;
+ *size = 0;
+ }
+
+ pr_debug("get 64bit window: %d, base hi: 0x%x, base lo: 0x%x, "
+ "size: 0x%x\n", window, *base_hi, *base_lo, *size);
+
+ return;
+}
+
+/*
+ * mv64x60_set_64bit_window()
+ *
+ * Set the base address and size of a 64-bit window on the bridge.
+ */
+void __init
+mv64x60_set_64bit_window(struct mv64x60_handle *bh, u32 window,
+ u32 base_hi, u32 base_lo, u32 size, u32 other_bits)
+{
+ u32 val, base_lo_reg, size_reg, base_lo_bits, size_bits;
+ u32 (*map_to_field)(u32 val, u32 num_bits);
+
+ pr_debug("set 64bit window: %d, base hi: 0x%x, base lo: 0x%x, "
+ "size: 0x%x, other: 0x%x\n",
+ window, base_hi, base_lo, size, other_bits);
+
+ base_lo_reg = bh->ci->window_tab_64bit[window].base_lo_reg;
+
+ if (base_lo_reg != 0) {
+ size_reg = bh->ci->window_tab_64bit[window].size_reg;
+ base_lo_bits = bh->ci->window_tab_64bit[window].base_lo_bits;
+ size_bits = bh->ci->window_tab_64bit[window].size_bits;
+ map_to_field = bh->ci->window_tab_64bit[window].map_to_field;
+
+ mv64x60_write(bh, bh->ci->window_tab_64bit[window].base_hi_reg,
+ base_hi);
+
+ val = map_to_field(base_lo, base_lo_bits) | other_bits;
+ mv64x60_write(bh, base_lo_reg, val);
+
+ if (size_reg != 0) {
+ val = bh->ci->translate_size(base_lo, size, size_bits);
+ val = map_to_field(val, size_bits);
+ mv64x60_write(bh, size_reg, val);
+ }
+
+ (void)mv64x60_read(bh, base_lo_reg); /* Flush FIFO */
+ }
+
+ return;
+}
+
+/*
+ * mv64x60_mask()
+ *
+ * Take the high-order 'num_bits' of 'val' & mask off low bits.
+ */
+u32 __init
+mv64x60_mask(u32 val, u32 num_bits)
+{
+ return val & (0xffffffff << (32 - num_bits));
+}
+
+/*
+ * mv64x60_shift_left()
+ *
+ * Take the low-order 'num_bits' of 'val', shift left to align at bit 31 (MSB).
+ */
+u32 __init
+mv64x60_shift_left(u32 val, u32 num_bits)
+{
+ return val << (32 - num_bits);
+}
+
+/*
+ * mv64x60_shift_right()
+ *
+ * Take the high-order 'num_bits' of 'val', shift right to align at bit 0 (LSB).
+ */
+u32 __init
+mv64x60_shift_right(u32 val, u32 num_bits)
+{
+ return val >> (32 - num_bits);
+}
+
+/*
+ *****************************************************************************
+ *
+ * Chip Identification Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_get_type()
+ *
+ * Determine the type of bridge chip we have.
+ */
+int __init
+mv64x60_get_type(struct mv64x60_handle *bh)
+{
+ struct pci_controller hose;
+ u16 val;
+ u8 save_exclude;
+
+ memset(&hose, 0, sizeof(hose));
+ setup_indirect_pci_nomap(&hose, bh->v_base + MV64x60_PCI0_CONFIG_ADDR,
+ bh->v_base + MV64x60_PCI0_CONFIG_DATA);
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ /* Sanity check of bridge's Vendor ID */
+ early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID, &val);
+
+ if (val != PCI_VENDOR_ID_MARVELL) {
+ mv64x60_pci_exclude_bridge = save_exclude;
+ return -1;
+ }
+
+ /* Get the revision of the chip */
+ early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_CLASS_REVISION,
+ &val);
+ bh->rev = (u32)(val & 0xff);
+
+ /* Figure out the type of Marvell bridge it is */
+ early_read_config_word(&hose, 0, PCI_DEVFN(0, 0), PCI_DEVICE_ID, &val);
+ mv64x60_pci_exclude_bridge = save_exclude;
+
+ switch (val) {
+ case PCI_DEVICE_ID_MARVELL_GT64260:
+ switch (bh->rev) {
+ case GT64260_REV_A:
+ bh->type = MV64x60_TYPE_GT64260A;
+ break;
+
+ default:
+ printk(KERN_WARNING "Unsupported GT64260 rev %04x\n",
+ bh->rev);
+ /* Assume its similar to a 'B' rev and fallthru */
+ case GT64260_REV_B:
+ bh->type = MV64x60_TYPE_GT64260B;
+ break;
+ }
+ break;
+
+ case PCI_DEVICE_ID_MARVELL_MV64360:
+ /* Marvell won't tell me how to distinguish a 64361 & 64362 */
+ bh->type = MV64x60_TYPE_MV64360;
+ break;
+
+ case PCI_DEVICE_ID_MARVELL_MV64460:
+ bh->type = MV64x60_TYPE_MV64460;
+ break;
+
+ default:
+ printk(KERN_ERR "Unknown Marvell bridge type %04x\n", val);
+ return -1;
+ }
+
+ /* Hang onto bridge type & rev for PIC code */
+ mv64x60_bridge_type = bh->type;
+ mv64x60_bridge_rev = bh->rev;
+
+ return 0;
+}
+
+/*
+ * mv64x60_setup_for_chip()
+ *
+ * Set 'bh' to use the proper set of routine for the bridge chip that we have.
+ */
+int __init
+mv64x60_setup_for_chip(struct mv64x60_handle *bh)
+{
+ int rc = 0;
+
+ /* Set up chip-specific info based on the chip/bridge type */
+ switch(bh->type) {
+ case MV64x60_TYPE_GT64260A:
+ bh->ci = &gt64260a_ci;
+ break;
+
+ case MV64x60_TYPE_GT64260B:
+ bh->ci = &gt64260b_ci;
+ break;
+
+ case MV64x60_TYPE_MV64360:
+ bh->ci = &mv64360_ci;
+ break;
+
+ case MV64x60_TYPE_MV64460:
+ bh->ci = &mv64460_ci;
+ break;
+
+ case MV64x60_TYPE_INVALID:
+ default:
+ if (ppc_md.progress)
+ ppc_md.progress("mv64x60: Unsupported bridge", 0x0);
+ printk(KERN_ERR "mv64x60: Unsupported bridge\n");
+ rc = -1;
+ }
+
+ return rc;
+}
+
+/*
+ * mv64x60_get_bridge_vbase()
+ *
+ * Return the virtual address of the bridge's registers.
+ */
+void *
+mv64x60_get_bridge_vbase(void)
+{
+ return mv64x60_bridge_vbase;
+}
+
+/*
+ * mv64x60_get_bridge_type()
+ *
+ * Return the type of bridge on the platform.
+ */
+u32
+mv64x60_get_bridge_type(void)
+{
+ return mv64x60_bridge_type;
+}
+
+/*
+ * mv64x60_get_bridge_rev()
+ *
+ * Return the revision of the bridge on the platform.
+ */
+u32
+mv64x60_get_bridge_rev(void)
+{
+ return mv64x60_bridge_rev;
+}
+
+/*
+ *****************************************************************************
+ *
+ * System Memory Window Related Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_get_mem_size()
+ *
+ * Calculate the amount of memory that the memory controller is set up for.
+ * This should only be used by board-specific code if there is no other
+ * way to determine the amount of memory in the system.
+ */
+u32 __init
+mv64x60_get_mem_size(u32 bridge_base, u32 chip_type)
+{
+ struct mv64x60_handle bh;
+ u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2];
+ u32 rc = 0;
+
+ memset(&bh, 0, sizeof(bh));
+
+ bh.type = chip_type;
+ bh.v_base = (void *)bridge_base;
+
+ if (!mv64x60_setup_for_chip(&bh)) {
+ mv64x60_get_mem_windows(&bh, mem_windows);
+ rc = mv64x60_calc_mem_size(&bh, mem_windows);
+ }
+
+ return rc;
+}
+
+/*
+ * mv64x60_get_mem_windows()
+ *
+ * Get the values in the memory controller & return in the 'mem_windows' array.
+ */
+void __init
+mv64x60_get_mem_windows(struct mv64x60_handle *bh,
+ u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+ u32 i, win;
+
+ for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++)
+ if (bh->ci->is_enabled_32bit(bh, win))
+ mv64x60_get_32bit_window(bh, win,
+ &mem_windows[i][0], &mem_windows[i][1]);
+ else {
+ mem_windows[i][0] = 0;
+ mem_windows[i][1] = 0;
+ }
+
+ return;
+}
+
+/*
+ * mv64x60_calc_mem_size()
+ *
+ * Using the memory controller register values in 'mem_windows', determine
+ * how much memory it is set up for.
+ */
+u32 __init
+mv64x60_calc_mem_size(struct mv64x60_handle *bh,
+ u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+ u32 i, total = 0;
+
+ for (i=0; i<MV64x60_CPU2MEM_WINDOWS; i++)
+ total += mem_windows[i][1];
+
+ return total;
+}
+
+/*
+ *****************************************************************************
+ *
+ * CPU->System MEM, PCI Config Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_config_cpu2mem_windows()
+ *
+ * Configure CPU->Memory windows on the bridge.
+ */
+static u32 prot_tab[] __initdata = {
+ MV64x60_CPU_PROT_0_WIN, MV64x60_CPU_PROT_1_WIN,
+ MV64x60_CPU_PROT_2_WIN, MV64x60_CPU_PROT_3_WIN
+};
+
+static u32 cpu_snoop_tab[] __initdata = {
+ MV64x60_CPU_SNOOP_0_WIN, MV64x60_CPU_SNOOP_1_WIN,
+ MV64x60_CPU_SNOOP_2_WIN, MV64x60_CPU_SNOOP_3_WIN
+};
+
+void __init
+mv64x60_config_cpu2mem_windows(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si,
+ u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+ u32 i, win;
+
+ /* Set CPU protection & snoop windows */
+ for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++)
+ if (bh->ci->is_enabled_32bit(bh, win)) {
+ mv64x60_set_32bit_window(bh, prot_tab[i],
+ mem_windows[i][0], mem_windows[i][1],
+ si->cpu_prot_options[i]);
+ bh->ci->enable_window_32bit(bh, prot_tab[i]);
+
+ if (bh->ci->window_tab_32bit[cpu_snoop_tab[i]].
+ base_reg != 0) {
+ mv64x60_set_32bit_window(bh, cpu_snoop_tab[i],
+ mem_windows[i][0], mem_windows[i][1],
+ si->cpu_snoop_options[i]);
+ bh->ci->enable_window_32bit(bh,
+ cpu_snoop_tab[i]);
+ }
+
+ }
+
+ return;
+}
+
+/*
+ * mv64x60_config_cpu2pci_windows()
+ *
+ * Configure the CPU->PCI windows for one of the PCI buses.
+ */
+static u32 win_tab[2][4] __initdata = {
+ { MV64x60_CPU2PCI0_IO_WIN, MV64x60_CPU2PCI0_MEM_0_WIN,
+ MV64x60_CPU2PCI0_MEM_1_WIN, MV64x60_CPU2PCI0_MEM_2_WIN },
+ { MV64x60_CPU2PCI1_IO_WIN, MV64x60_CPU2PCI1_MEM_0_WIN,
+ MV64x60_CPU2PCI1_MEM_1_WIN, MV64x60_CPU2PCI1_MEM_2_WIN },
+};
+
+static u32 remap_tab[2][4] __initdata = {
+ { MV64x60_CPU2PCI0_IO_REMAP_WIN, MV64x60_CPU2PCI0_MEM_0_REMAP_WIN,
+ MV64x60_CPU2PCI0_MEM_1_REMAP_WIN, MV64x60_CPU2PCI0_MEM_2_REMAP_WIN },
+ { MV64x60_CPU2PCI1_IO_REMAP_WIN, MV64x60_CPU2PCI1_MEM_0_REMAP_WIN,
+ MV64x60_CPU2PCI1_MEM_1_REMAP_WIN, MV64x60_CPU2PCI1_MEM_2_REMAP_WIN }
+};
+
+void __init
+mv64x60_config_cpu2pci_windows(struct mv64x60_handle *bh,
+ struct mv64x60_pci_info *pi, u32 bus)
+{
+ int i;
+
+ if (pi->pci_io.size > 0) {
+ mv64x60_set_32bit_window(bh, win_tab[bus][0],
+ pi->pci_io.cpu_base, pi->pci_io.size, pi->pci_io.swap);
+ mv64x60_set_32bit_window(bh, remap_tab[bus][0],
+ pi->pci_io.pci_base_lo, 0, 0);
+ bh->ci->enable_window_32bit(bh, win_tab[bus][0]);
+ }
+ else /* Actually, the window should already be disabled */
+ bh->ci->disable_window_32bit(bh, win_tab[bus][0]);
+
+ for (i=0; i<3; i++)
+ if (pi->pci_mem[i].size > 0) {
+ mv64x60_set_32bit_window(bh, win_tab[bus][i+1],
+ pi->pci_mem[i].cpu_base, pi->pci_mem[i].size,
+ pi->pci_mem[i].swap);
+ mv64x60_set_64bit_window(bh, remap_tab[bus][i+1],
+ pi->pci_mem[i].pci_base_hi,
+ pi->pci_mem[i].pci_base_lo, 0, 0);
+ bh->ci->enable_window_32bit(bh, win_tab[bus][i+1]);
+ }
+ else /* Actually, the window should already be disabled */
+ bh->ci->disable_window_32bit(bh, win_tab[bus][i+1]);
+
+ return;
+}
+
+/*
+ *****************************************************************************
+ *
+ * PCI->System MEM Config Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_config_pci2mem_windows()
+ *
+ * Configure the PCI->Memory windows on the bridge.
+ */
+static u32 pci_acc_tab[2][4] __initdata = {
+ { MV64x60_PCI02MEM_ACC_CNTL_0_WIN, MV64x60_PCI02MEM_ACC_CNTL_1_WIN,
+ MV64x60_PCI02MEM_ACC_CNTL_2_WIN, MV64x60_PCI02MEM_ACC_CNTL_3_WIN },
+ { MV64x60_PCI12MEM_ACC_CNTL_0_WIN, MV64x60_PCI12MEM_ACC_CNTL_1_WIN,
+ MV64x60_PCI12MEM_ACC_CNTL_2_WIN, MV64x60_PCI12MEM_ACC_CNTL_3_WIN }
+};
+
+static u32 pci_snoop_tab[2][4] __initdata = {
+ { MV64x60_PCI02MEM_SNOOP_0_WIN, MV64x60_PCI02MEM_SNOOP_1_WIN,
+ MV64x60_PCI02MEM_SNOOP_2_WIN, MV64x60_PCI02MEM_SNOOP_3_WIN },
+ { MV64x60_PCI12MEM_SNOOP_0_WIN, MV64x60_PCI12MEM_SNOOP_1_WIN,
+ MV64x60_PCI12MEM_SNOOP_2_WIN, MV64x60_PCI12MEM_SNOOP_3_WIN }
+};
+
+static u32 pci_size_tab[2][4] __initdata = {
+ { MV64x60_PCI0_MEM_0_SIZE, MV64x60_PCI0_MEM_1_SIZE,
+ MV64x60_PCI0_MEM_2_SIZE, MV64x60_PCI0_MEM_3_SIZE },
+ { MV64x60_PCI1_MEM_0_SIZE, MV64x60_PCI1_MEM_1_SIZE,
+ MV64x60_PCI1_MEM_2_SIZE, MV64x60_PCI1_MEM_3_SIZE }
+};
+
+void __init
+mv64x60_config_pci2mem_windows(struct mv64x60_handle *bh,
+ struct pci_controller *hose, struct mv64x60_pci_info *pi,
+ u32 bus, u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+ u32 i, win;
+
+ /*
+ * Set the access control, snoop, BAR size, and window base addresses.
+ * PCI->MEM windows base addresses will match exactly what the
+ * CPU->MEM windows are.
+ */
+ for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++)
+ if (bh->ci->is_enabled_32bit(bh, win)) {
+ mv64x60_set_64bit_window(bh,
+ pci_acc_tab[bus][i], 0,
+ mem_windows[i][0], mem_windows[i][1],
+ pi->acc_cntl_options[i]);
+ bh->ci->enable_window_64bit(bh, pci_acc_tab[bus][i]);
+
+ if (bh->ci->window_tab_64bit[
+ pci_snoop_tab[bus][i]].base_lo_reg != 0) {
+
+ mv64x60_set_64bit_window(bh,
+ pci_snoop_tab[bus][i], 0,
+ mem_windows[i][0], mem_windows[i][1],
+ pi->snoop_options[i]);
+ bh->ci->enable_window_64bit(bh,
+ pci_snoop_tab[bus][i]);
+ }
+
+ bh->ci->set_pci2mem_window(hose, bus, i,
+ mem_windows[i][0]);
+ mv64x60_write(bh, pci_size_tab[bus][i],
+ mv64x60_mask(mem_windows[i][1] - 1, 20));
+
+ /* Enable the window */
+ mv64x60_clr_bits(bh, ((bus == 0) ?
+ MV64x60_PCI0_BAR_ENABLE :
+ MV64x60_PCI1_BAR_ENABLE), (1 << i));
+ }
+
+ return;
+}
+
+/*
+ *****************************************************************************
+ *
+ * Hose & Resource Alloc/Init Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_alloc_hoses()
+ *
+ * Allocate the PCI hose structures for the bridge's PCI buses.
+ */
+void __init
+mv64x60_alloc_hose(struct mv64x60_handle *bh, u32 cfg_addr, u32 cfg_data,
+ struct pci_controller **hose)
+{
+ *hose = pcibios_alloc_controller();
+ setup_indirect_pci_nomap(*hose, bh->v_base + cfg_addr,
+ bh->v_base + cfg_data);
+ return;
+}
+
+/*
+ * mv64x60_config_resources()
+ *
+ * Calculate the offsets, etc. for the hose structures to reflect all of
+ * the address remapping that happens as you go from CPU->PCI and PCI->MEM.
+ */
+void __init
+mv64x60_config_resources(struct pci_controller *hose,
+ struct mv64x60_pci_info *pi, u32 io_base)
+{
+ int i;
+ /* 2 hoses; 4 resources/hose; string <= 64 bytes */
+ static char s[2][4][64];
+
+ if (pi->pci_io.size != 0) {
+ sprintf(s[hose->index][0], "PCI hose %d I/O Space",
+ hose->index);
+ pci_init_resource(&hose->io_resource, io_base - isa_io_base,
+ io_base - isa_io_base + pi->pci_io.size - 1,
+ IORESOURCE_IO, s[hose->index][0]);
+ hose->io_space.start = pi->pci_io.pci_base_lo;
+ hose->io_space.end = pi->pci_io.pci_base_lo + pi->pci_io.size-1;
+ hose->io_base_phys = pi->pci_io.cpu_base;
+ hose->io_base_virt = (void *)isa_io_base;
+ }
+
+ for (i=0; i<3; i++)
+ if (pi->pci_mem[i].size != 0) {
+ sprintf(s[hose->index][i+1], "PCI hose %d MEM Space %d",
+ hose->index, i);
+ pci_init_resource(&hose->mem_resources[i],
+ pi->pci_mem[i].cpu_base,
+ pi->pci_mem[i].cpu_base + pi->pci_mem[i].size-1,
+ IORESOURCE_MEM, s[hose->index][i+1]);
+ }
+
+ hose->mem_space.end = pi->pci_mem[0].pci_base_lo +
+ pi->pci_mem[0].size - 1;
+ hose->pci_mem_offset = pi->pci_mem[0].cpu_base -
+ pi->pci_mem[0].pci_base_lo;
+ return;
+}
+
+/*
+ * mv64x60_config_pci_params()
+ *
+ * Configure a hose's PCI config space parameters.
+ */
+void __init
+mv64x60_config_pci_params(struct pci_controller *hose,
+ struct mv64x60_pci_info *pi)
+{
+ u32 devfn;
+ u16 u16_val;
+ u8 save_exclude;
+
+ devfn = PCI_DEVFN(0,0);
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+
+ /* Set class code to indicate host bridge */
+ u16_val = PCI_CLASS_BRIDGE_HOST; /* 0x0600 (host bridge) */
+ early_write_config_word(hose, 0, devfn, PCI_CLASS_DEVICE, u16_val);
+
+ /* Enable bridge to be PCI master & respond to PCI MEM cycles */
+ early_read_config_word(hose, 0, devfn, PCI_COMMAND, &u16_val);
+ u16_val &= ~(PCI_COMMAND_IO | PCI_COMMAND_INVALIDATE |
+ PCI_COMMAND_PARITY | PCI_COMMAND_SERR | PCI_COMMAND_FAST_BACK);
+ u16_val |= pi->pci_cmd_bits | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ early_write_config_word(hose, 0, devfn, PCI_COMMAND, u16_val);
+
+ /* Set latency timer, cache line size, clear BIST */
+ u16_val = (pi->latency_timer << 8) | (L1_CACHE_LINE_SIZE >> 2);
+ early_write_config_word(hose, 0, devfn, PCI_CACHE_LINE_SIZE, u16_val);
+
+ mv64x60_pci_exclude_bridge = save_exclude;
+ return;
+}
+
+/*
+ *****************************************************************************
+ *
+ * PCI Related Routine
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64x60_set_bus()
+ *
+ * Set the bus number for the hose directly under the bridge.
+ */
+void __init
+mv64x60_set_bus(struct mv64x60_handle *bh, u32 bus, u32 child_bus)
+{
+ struct pci_controller *hose;
+ u32 pci_mode, p2p_cfg, pci_cfg_offset, val;
+ u8 save_exclude;
+
+ if (bus == 0) {
+ pci_mode = bh->pci_mode_a;
+ p2p_cfg = MV64x60_PCI0_P2P_CONFIG;
+ pci_cfg_offset = 0x64;
+ hose = bh->hose_a;
+ }
+ else {
+ pci_mode = bh->pci_mode_b;
+ p2p_cfg = MV64x60_PCI1_P2P_CONFIG;
+ pci_cfg_offset = 0xe4;
+ hose = bh->hose_b;
+ }
+
+ child_bus &= 0xff;
+ val = mv64x60_read(bh, p2p_cfg);
+
+ if (pci_mode == MV64x60_PCIMODE_CONVENTIONAL) {
+ val &= 0xe0000000; /* Force dev num to 0, turn off P2P bridge */
+ val |= (child_bus << 16) | 0xff;
+ mv64x60_write(bh, p2p_cfg, val);
+ (void)mv64x60_read(bh, p2p_cfg); /* Flush FIFO */
+ }
+ else { /* PCI-X */
+ /*
+ * Need to use the current bus/dev number (that's in the
+ * P2P CONFIG reg) to access the bridge's pci config space.
+ */
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ early_write_config_dword(hose, (val & 0x00ff0000) >> 16,
+ PCI_DEVFN(((val & 0x1f000000) >> 24), 0),
+ pci_cfg_offset, child_bus << 8);
+ mv64x60_pci_exclude_bridge = save_exclude;
+ }
+
+ return;
+}
+
+/*
+ * mv64x60_pci_exclude_device()
+ *
+ * This routine is used to make the bridge not appear when the
+ * PCI subsystem is accessing PCI devices (in PCI config space).
+ */
+int
+mv64x60_pci_exclude_device(u8 bus, u8 devfn)
+{
+ struct pci_controller *hose;
+
+ hose = pci_bus_to_hose(bus);
+
+ /* Skip slot 0 on both hoses */
+ if ((mv64x60_pci_exclude_bridge == 1) && (PCI_SLOT(devfn) == 0) &&
+ (hose->first_busno == bus))
+
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ else
+ return PCIBIOS_SUCCESSFUL;
+} /* mv64x60_pci_exclude_device() */
+
+/*
+ *****************************************************************************
+ *
+ * Platform Device Routines
+ *
+ *****************************************************************************
+ */
+
+/*
+ * mv64x60_pd_fixup()
+ *
+ * Need to add the base addr of where the bridge's regs are mapped in the
+ * physical addr space so drivers can ioremap() them.
+ */
+void __init
+mv64x60_pd_fixup(struct mv64x60_handle *bh, struct platform_device *pd_devs[],
+ u32 entries)
+{
+ struct resource *r;
+ u32 i, j;
+
+ for (i=0; i<entries; i++) {
+ j = 0;
+
+ while ((r = platform_get_resource(pd_devs[i],IORESOURCE_MEM,j))
+ != NULL) {
+
+ r->start += bh->p_base;
+ r->end += bh->p_base;
+ j++;
+ }
+ }
+
+ return;
+}
+
+/*
+ * mv64x60_add_pds()
+ *
+ * Add the mv64x60 platform devices to the list of platform devices.
+ */
+static int __init
+mv64x60_add_pds(void)
+{
+ return platform_add_devices(mv64x60_pd_devs,
+ ARRAY_SIZE(mv64x60_pd_devs));
+}
+arch_initcall(mv64x60_add_pds);
+
+/*
+ *****************************************************************************
+ *
+ * GT64260-Specific Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * gt64260_translate_size()
+ *
+ * On the GT64260, the size register is really the "top" address of the window.
+ */
+static u32 __init
+gt64260_translate_size(u32 base, u32 size, u32 num_bits)
+{
+ return base + mv64x60_mask(size - 1, num_bits);
+}
+
+/*
+ * gt64260_untranslate_size()
+ *
+ * Translate the top address of a window into a window size.
+ */
+static u32 __init
+gt64260_untranslate_size(u32 base, u32 size, u32 num_bits)
+{
+ if (size >= base)
+ size = size - base + (1 << (32 - num_bits));
+ else
+ size = 0;
+
+ return size;
+}
+
+/*
+ * gt64260_set_pci2mem_window()
+ *
+ * The PCI->MEM window registers are actually in PCI config space so need
+ * to set them by setting the correct config space BARs.
+ */
+static u32 gt64260_reg_addrs[2][4] __initdata = {
+ { 0x10, 0x14, 0x18, 0x1c }, { 0x90, 0x94, 0x98, 0x9c }
+};
+
+static void __init
+gt64260_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window,
+ u32 base)
+{
+ u8 save_exclude;
+
+ pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window,
+ hose->index, base);
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ early_write_config_dword(hose, 0, PCI_DEVFN(0, 0),
+ gt64260_reg_addrs[bus][window], mv64x60_mask(base, 20) | 0x8);
+ mv64x60_pci_exclude_bridge = save_exclude;
+
+ return;
+}
+
+/*
+ * gt64260_set_pci2regs_window()
+ *
+ * Set where the bridge's registers appear in PCI MEM space.
+ */
+static u32 gt64260_offset[2] __initdata = {0x20, 0xa0};
+
+static void __init
+gt64260_set_pci2regs_window(struct mv64x60_handle *bh,
+ struct pci_controller *hose, u32 bus, u32 base)
+{
+ u8 save_exclude;
+
+ pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index,
+ base);
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ early_write_config_dword(hose, 0, PCI_DEVFN(0,0), gt64260_offset[bus],
+ (base << 16));
+ mv64x60_pci_exclude_bridge = save_exclude;
+
+ return;
+}
+
+/*
+ * gt64260_is_enabled_32bit()
+ *
+ * On a GT64260, a window is enabled iff its top address is >= to its base
+ * address.
+ */
+static u32 __init
+gt64260_is_enabled_32bit(struct mv64x60_handle *bh, u32 window)
+{
+ u32 rc = 0;
+
+ if ((gt64260_32bit_windows[window].base_reg != 0) &&
+ (gt64260_32bit_windows[window].size_reg != 0) &&
+ ((mv64x60_read(bh, gt64260_32bit_windows[window].size_reg) &
+ ((1 << gt64260_32bit_windows[window].size_bits) - 1)) >=
+ (mv64x60_read(bh, gt64260_32bit_windows[window].base_reg) &
+ ((1 << gt64260_32bit_windows[window].base_bits) - 1))))
+
+ rc = 1;
+
+ return rc;
+}
+
+/*
+ * gt64260_enable_window_32bit()
+ *
+ * On the GT64260, a window is enabled iff the top address is >= to the base
+ * address of the window. Since the window has already been configured by
+ * the time this routine is called, we have nothing to do here.
+ */
+static void __init
+gt64260_enable_window_32bit(struct mv64x60_handle *bh, u32 window)
+{
+ pr_debug("enable 32bit window: %d\n", window);
+ return;
+}
+
+/*
+ * gt64260_disable_window_32bit()
+ *
+ * On a GT64260, you disable a window by setting its top address to be less
+ * than its base address.
+ */
+static void __init
+gt64260_disable_window_32bit(struct mv64x60_handle *bh, u32 window)
+{
+ pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+ window, gt64260_32bit_windows[window].base_reg,
+ gt64260_32bit_windows[window].size_reg);
+
+ if ((gt64260_32bit_windows[window].base_reg != 0) &&
+ (gt64260_32bit_windows[window].size_reg != 0)) {
+
+ /* To disable, make bottom reg higher than top reg */
+ mv64x60_write(bh, gt64260_32bit_windows[window].base_reg,0xfff);
+ mv64x60_write(bh, gt64260_32bit_windows[window].size_reg, 0);
+ }
+
+ return;
+}
+
+/*
+ * gt64260_enable_window_64bit()
+ *
+ * On the GT64260, a window is enabled iff the top address is >= to the base
+ * address of the window. Since the window has already been configured by
+ * the time this routine is called, we have nothing to do here.
+ */
+static void __init
+gt64260_enable_window_64bit(struct mv64x60_handle *bh, u32 window)
+{
+ pr_debug("enable 64bit window: %d\n", window);
+ return; /* Enabled when window configured (i.e., when top >= base) */
+}
+
+/*
+ * gt64260_disable_window_64bit()
+ *
+ * On a GT64260, you disable a window by setting its top address to be less
+ * than its base address.
+ */
+static void __init
+gt64260_disable_window_64bit(struct mv64x60_handle *bh, u32 window)
+{
+ pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+ window, gt64260_64bit_windows[window].base_lo_reg,
+ gt64260_64bit_windows[window].size_reg);
+
+ if ((gt64260_64bit_windows[window].base_lo_reg != 0) &&
+ (gt64260_64bit_windows[window].size_reg != 0)) {
+
+ /* To disable, make bottom reg higher than top reg */
+ mv64x60_write(bh, gt64260_64bit_windows[window].base_lo_reg,
+ 0xfff);
+ mv64x60_write(bh, gt64260_64bit_windows[window].base_hi_reg, 0);
+ mv64x60_write(bh, gt64260_64bit_windows[window].size_reg, 0);
+ }
+
+ return;
+}
+
+/*
+ * gt64260_disable_all_windows()
+ *
+ * The GT64260 has several windows that aren't represented in the table of
+ * windows at the top of this file. This routine turns all of them off
+ * except for the memory controller windows, of course.
+ */
+static void __init
+gt64260_disable_all_windows(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si)
+{
+ u32 i, preserve;
+
+ /* Disable 32bit windows (don't disable cpu->mem windows) */
+ for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) {
+ if (i < 32)
+ preserve = si->window_preserve_mask_32_lo & (1 << i);
+ else
+ preserve = si->window_preserve_mask_32_hi & (1<<(i-32));
+
+ if (!preserve)
+ gt64260_disable_window_32bit(bh, i);
+ }
+
+ /* Disable 64bit windows */
+ for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++)
+ if (!(si->window_preserve_mask_64 & (1<<i)))
+ gt64260_disable_window_64bit(bh, i);
+
+ /* Turn off cpu protection windows not in gt64260_32bit_windows[] */
+ mv64x60_write(bh, GT64260_CPU_PROT_BASE_4, 0xfff);
+ mv64x60_write(bh, GT64260_CPU_PROT_SIZE_4, 0);
+ mv64x60_write(bh, GT64260_CPU_PROT_BASE_5, 0xfff);
+ mv64x60_write(bh, GT64260_CPU_PROT_SIZE_5, 0);
+ mv64x60_write(bh, GT64260_CPU_PROT_BASE_6, 0xfff);
+ mv64x60_write(bh, GT64260_CPU_PROT_SIZE_6, 0);
+ mv64x60_write(bh, GT64260_CPU_PROT_BASE_7, 0xfff);
+ mv64x60_write(bh, GT64260_CPU_PROT_SIZE_7, 0);
+
+ /* Turn off PCI->MEM access cntl wins not in gt64260_64bit_windows[] */
+ mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0xfff);
+ mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_HI, 0);
+ mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_4_SIZE, 0);
+ mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0xfff);
+ mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_HI, 0);
+ mv64x60_write(bh, MV64x60_PCI0_ACC_CNTL_5_SIZE, 0);
+ mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_LO, 0xfff);
+ mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_BASE_HI, 0);
+ mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_6_SIZE, 0);
+ mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_LO, 0xfff);
+ mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_BASE_HI, 0);
+ mv64x60_write(bh, GT64260_PCI0_ACC_CNTL_7_SIZE, 0);
+
+ mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0xfff);
+ mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_HI, 0);
+ mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_4_SIZE, 0);
+ mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0xfff);
+ mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_HI, 0);
+ mv64x60_write(bh, MV64x60_PCI1_ACC_CNTL_5_SIZE, 0);
+ mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_LO, 0xfff);
+ mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_BASE_HI, 0);
+ mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_6_SIZE, 0);
+ mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_LO, 0xfff);
+ mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_BASE_HI, 0);
+ mv64x60_write(bh, GT64260_PCI1_ACC_CNTL_7_SIZE, 0);
+
+ /* Disable all PCI-><whatever> windows */
+ mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x07fffdff);
+ mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x07fffdff);
+
+ /*
+ * Some firmwares enable a bunch of intr sources
+ * for the PCI INT output pins.
+ */
+ mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_LO, 0);
+ mv64x60_write(bh, GT64260_IC_CPU_INTR_MASK_HI, 0);
+ mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_LO, 0);
+ mv64x60_write(bh, GT64260_IC_PCI0_INTR_MASK_HI, 0);
+ mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_LO, 0);
+ mv64x60_write(bh, GT64260_IC_PCI1_INTR_MASK_HI, 0);
+ mv64x60_write(bh, GT64260_IC_CPU_INT_0_MASK, 0);
+ mv64x60_write(bh, GT64260_IC_CPU_INT_1_MASK, 0);
+ mv64x60_write(bh, GT64260_IC_CPU_INT_2_MASK, 0);
+ mv64x60_write(bh, GT64260_IC_CPU_INT_3_MASK, 0);
+
+ return;
+}
+
+/*
+ * gt64260a_chip_specific_init()
+ *
+ * Implement errata work arounds for the GT64260A.
+ */
+static void __init
+gt64260a_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si)
+{
+#ifdef CONFIG_SERIAL_MPSC
+ struct resource *r;
+#endif
+#if !defined(CONFIG_NOT_COHERENT_CACHE)
+ u32 val;
+ u8 save_exclude;
+#endif
+
+ if (si->pci_0.enable_bus)
+ mv64x60_set_bits(bh, MV64x60_PCI0_CMD,
+ ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+
+ if (si->pci_1.enable_bus)
+ mv64x60_set_bits(bh, MV64x60_PCI1_CMD,
+ ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+
+ /*
+ * Dave Wilhardt found that bit 4 in the PCI Command registers must
+ * be set if you are using cache coherency.
+ */
+#if !defined(CONFIG_NOT_COHERENT_CACHE)
+ /* Res #MEM-4 -- cpu read buffer to buffer 1 */
+ if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40)
+ mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26));
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ if (si->pci_0.enable_bus) {
+ early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, &val);
+ val |= PCI_COMMAND_INVALIDATE;
+ early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, val);
+ }
+
+ if (si->pci_1.enable_bus) {
+ early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, &val);
+ val |= PCI_COMMAND_INVALIDATE;
+ early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, val);
+ }
+ mv64x60_pci_exclude_bridge = save_exclude;
+#endif
+
+ /* Disable buffer/descriptor snooping */
+ mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30));
+ mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30));
+
+#ifdef CONFIG_SERIAL_MPSC
+ mv64x60_mpsc0_pdata.mirror_regs = 1;
+ mv64x60_mpsc0_pdata.cache_mgmt = 1;
+ mv64x60_mpsc1_pdata.mirror_regs = 1;
+ mv64x60_mpsc1_pdata.cache_mgmt = 1;
+
+ if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0))
+ != NULL) {
+
+ r->start = MV64x60_IRQ_SDMA_0;
+ r->end = MV64x60_IRQ_SDMA_0;
+ }
+#endif
+
+ return;
+}
+
+/*
+ * gt64260b_chip_specific_init()
+ *
+ * Implement errata work arounds for the GT64260B.
+ */
+static void __init
+gt64260b_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si)
+{
+#ifdef CONFIG_SERIAL_MPSC
+ struct resource *r;
+#endif
+#if !defined(CONFIG_NOT_COHERENT_CACHE)
+ u32 val;
+ u8 save_exclude;
+#endif
+
+ if (si->pci_0.enable_bus)
+ mv64x60_set_bits(bh, MV64x60_PCI0_CMD,
+ ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+
+ if (si->pci_1.enable_bus)
+ mv64x60_set_bits(bh, MV64x60_PCI1_CMD,
+ ((1<<4) | (1<<5) | (1<<9) | (1<<13)));
+
+ /*
+ * Dave Wilhardt found that bit 4 in the PCI Command registers must
+ * be set if you are using cache coherency.
+ */
+#if !defined(CONFIG_NOT_COHERENT_CACHE)
+ mv64x60_set_bits(bh, GT64260_CPU_WB_PRIORITY_BUFFER_DEPTH, 0xf);
+
+ /* Res #MEM-4 -- cpu read buffer to buffer 1 */
+ if ((mv64x60_read(bh, MV64x60_CPU_MODE) & 0xf0) == 0x40)
+ mv64x60_set_bits(bh, GT64260_SDRAM_CONFIG, (1<<26));
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ if (si->pci_0.enable_bus) {
+ early_read_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, &val);
+ val |= PCI_COMMAND_INVALIDATE;
+ early_write_config_dword(bh->hose_a, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, val);
+ }
+
+ if (si->pci_1.enable_bus) {
+ early_read_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, &val);
+ val |= PCI_COMMAND_INVALIDATE;
+ early_write_config_dword(bh->hose_b, 0, PCI_DEVFN(0,0),
+ PCI_COMMAND, val);
+ }
+ mv64x60_pci_exclude_bridge = save_exclude;
+#endif
+
+ /* Disable buffer/descriptor snooping */
+ mv64x60_clr_bits(bh, 0xf280, (1<< 6) | (1<<14) | (1<<22) | (1<<30));
+ mv64x60_clr_bits(bh, 0xf2c0, (1<< 6) | (1<<14) | (1<<22) | (1<<30));
+
+#ifdef CONFIG_SERIAL_MPSC
+ /*
+ * The 64260B is not supposed to have the bug where the MPSC & ENET
+ * can't access cache coherent regions. However, testing has shown
+ * that the MPSC, at least, still has this bug.
+ */
+ mv64x60_mpsc0_pdata.cache_mgmt = 1;
+ mv64x60_mpsc1_pdata.cache_mgmt = 1;
+
+ if ((r = platform_get_resource(&mpsc1_device, IORESOURCE_IRQ, 0))
+ != NULL) {
+
+ r->start = MV64x60_IRQ_SDMA_0;
+ r->end = MV64x60_IRQ_SDMA_0;
+ }
+#endif
+
+ return;
+}
+
+/*
+ *****************************************************************************
+ *
+ * MV64360-Specific Routines
+ *
+ *****************************************************************************
+ */
+/*
+ * mv64360_translate_size()
+ *
+ * On the MV64360, the size register is set similar to the size you get
+ * from a pci config space BAR register. That is, programmed from LSB to MSB
+ * as a sequence of 1's followed by a sequence of 0's. IOW, "size -1" with the
+ * assumption that the size is a power of 2.
+ */
+static u32 __init
+mv64360_translate_size(u32 base_addr, u32 size, u32 num_bits)
+{
+ return mv64x60_mask(size - 1, num_bits);
+}
+
+/*
+ * mv64360_untranslate_size()
+ *
+ * Translate the size register value of a window into a window size.
+ */
+static u32 __init
+mv64360_untranslate_size(u32 base_addr, u32 size, u32 num_bits)
+{
+ if (size > 0) {
+ size >>= (32 - num_bits);
+ size++;
+ size <<= (32 - num_bits);
+ }
+
+ return size;
+}
+
+/*
+ * mv64360_set_pci2mem_window()
+ *
+ * The PCI->MEM window registers are actually in PCI config space so need
+ * to set them by setting the correct config space BARs.
+ */
+struct {
+ u32 fcn;
+ u32 base_hi_bar;
+ u32 base_lo_bar;
+} static mv64360_reg_addrs[2][4] __initdata = {
+ {{ 0, 0x14, 0x10 }, { 0, 0x1c, 0x18 },
+ { 1, 0x14, 0x10 }, { 1, 0x1c, 0x18 }},
+ {{ 0, 0x94, 0x90 }, { 0, 0x9c, 0x98 },
+ { 1, 0x94, 0x90 }, { 1, 0x9c, 0x98 }}
+};
+
+static void __init
+mv64360_set_pci2mem_window(struct pci_controller *hose, u32 bus, u32 window,
+ u32 base)
+{
+ u8 save_exclude;
+
+ pr_debug("set pci->mem window: %d, hose: %d, base: 0x%x\n", window,
+ hose->index, base);
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ early_write_config_dword(hose, 0,
+ PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn),
+ mv64360_reg_addrs[bus][window].base_hi_bar, 0);
+ early_write_config_dword(hose, 0,
+ PCI_DEVFN(0, mv64360_reg_addrs[bus][window].fcn),
+ mv64360_reg_addrs[bus][window].base_lo_bar,
+ mv64x60_mask(base,20) | 0xc);
+ mv64x60_pci_exclude_bridge = save_exclude;
+
+ return;
+}
+
+/*
+ * mv64360_set_pci2regs_window()
+ *
+ * Set where the bridge's registers appear in PCI MEM space.
+ */
+static u32 mv64360_offset[2][2] __initdata = {{0x20, 0x24}, {0xa0, 0xa4}};
+
+static void __init
+mv64360_set_pci2regs_window(struct mv64x60_handle *bh,
+ struct pci_controller *hose, u32 bus, u32 base)
+{
+ u8 save_exclude;
+
+ pr_debug("set pci->internal regs hose: %d, base: 0x%x\n", hose->index,
+ base);
+
+ save_exclude = mv64x60_pci_exclude_bridge;
+ mv64x60_pci_exclude_bridge = 0;
+ early_write_config_dword(hose, 0, PCI_DEVFN(0,0),
+ mv64360_offset[bus][0], (base << 16));
+ early_write_config_dword(hose, 0, PCI_DEVFN(0,0),
+ mv64360_offset[bus][1], 0);
+ mv64x60_pci_exclude_bridge = save_exclude;
+
+ return;
+}
+
+/*
+ * mv64360_is_enabled_32bit()
+ *
+ * On a MV64360, a window is enabled by either clearing a bit in the
+ * CPU BAR Enable reg or setting a bit in the window's base reg.
+ * Note that this doesn't work for windows on the PCI slave side but we don't
+ * check those so its okay.
+ */
+static u32 __init
+mv64360_is_enabled_32bit(struct mv64x60_handle *bh, u32 window)
+{
+ u32 extra, rc = 0;
+
+ if (((mv64360_32bit_windows[window].base_reg != 0) &&
+ (mv64360_32bit_windows[window].size_reg != 0)) ||
+ (window == MV64x60_CPU2SRAM_WIN)) {
+
+ extra = mv64360_32bit_windows[window].extra;
+
+ switch (extra & MV64x60_EXTRA_MASK) {
+ case MV64x60_EXTRA_CPUWIN_ENAB:
+ rc = (mv64x60_read(bh, MV64360_CPU_BAR_ENABLE) &
+ (1 << (extra & 0x1f))) == 0;
+ break;
+
+ case MV64x60_EXTRA_CPUPROT_ENAB:
+ rc = (mv64x60_read(bh,
+ mv64360_32bit_windows[window].base_reg) &
+ (1 << (extra & 0x1f))) != 0;
+ break;
+
+ case MV64x60_EXTRA_ENET_ENAB:
+ rc = (mv64x60_read(bh, MV64360_ENET2MEM_BAR_ENABLE) &
+ (1 << (extra & 0x7))) == 0;
+ break;
+
+ case MV64x60_EXTRA_MPSC_ENAB:
+ rc = (mv64x60_read(bh, MV64360_MPSC2MEM_BAR_ENABLE) &
+ (1 << (extra & 0x3))) == 0;
+ break;
+
+ case MV64x60_EXTRA_IDMA_ENAB:
+ rc = (mv64x60_read(bh, MV64360_IDMA2MEM_BAR_ENABLE) &
+ (1 << (extra & 0x7))) == 0;
+ break;
+
+ default:
+ printk(KERN_ERR "mv64360_is_enabled: %s\n",
+ "32bit table corrupted");
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * mv64360_enable_window_32bit()
+ *
+ * On a MV64360, a window is enabled by either clearing a bit in the
+ * CPU BAR Enable reg or setting a bit in the window's base reg.
+ */
+static void __init
+mv64360_enable_window_32bit(struct mv64x60_handle *bh, u32 window)
+{
+ u32 extra;
+
+ pr_debug("enable 32bit window: %d\n", window);
+
+ if (((mv64360_32bit_windows[window].base_reg != 0) &&
+ (mv64360_32bit_windows[window].size_reg != 0)) ||
+ (window == MV64x60_CPU2SRAM_WIN)) {
+
+ extra = mv64360_32bit_windows[window].extra;
+
+ switch (extra & MV64x60_EXTRA_MASK) {
+ case MV64x60_EXTRA_CPUWIN_ENAB:
+ mv64x60_clr_bits(bh, MV64360_CPU_BAR_ENABLE,
+ (1 << (extra & 0x1f)));
+ break;
+
+ case MV64x60_EXTRA_CPUPROT_ENAB:
+ mv64x60_set_bits(bh,
+ mv64360_32bit_windows[window].base_reg,
+ (1 << (extra & 0x1f)));
+ break;
+
+ case MV64x60_EXTRA_ENET_ENAB:
+ mv64x60_clr_bits(bh, MV64360_ENET2MEM_BAR_ENABLE,
+ (1 << (extra & 0x7)));
+ break;
+
+ case MV64x60_EXTRA_MPSC_ENAB:
+ mv64x60_clr_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE,
+ (1 << (extra & 0x3)));
+ break;
+
+ case MV64x60_EXTRA_IDMA_ENAB:
+ mv64x60_clr_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE,
+ (1 << (extra & 0x7)));
+ break;
+
+ default:
+ printk(KERN_ERR "mv64360_enable: %s\n",
+ "32bit table corrupted");
+ }
+ }
+
+ return;
+}
+
+/*
+ * mv64360_disable_window_32bit()
+ *
+ * On a MV64360, a window is disabled by either setting a bit in the
+ * CPU BAR Enable reg or clearing a bit in the window's base reg.
+ */
+static void __init
+mv64360_disable_window_32bit(struct mv64x60_handle *bh, u32 window)
+{
+ u32 extra;
+
+ pr_debug("disable 32bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+ window, mv64360_32bit_windows[window].base_reg,
+ mv64360_32bit_windows[window].size_reg);
+
+ if (((mv64360_32bit_windows[window].base_reg != 0) &&
+ (mv64360_32bit_windows[window].size_reg != 0)) ||
+ (window == MV64x60_CPU2SRAM_WIN)) {
+
+ extra = mv64360_32bit_windows[window].extra;
+
+ switch (extra & MV64x60_EXTRA_MASK) {
+ case MV64x60_EXTRA_CPUWIN_ENAB:
+ mv64x60_set_bits(bh, MV64360_CPU_BAR_ENABLE,
+ (1 << (extra & 0x1f)));
+ break;
+
+ case MV64x60_EXTRA_CPUPROT_ENAB:
+ mv64x60_clr_bits(bh,
+ mv64360_32bit_windows[window].base_reg,
+ (1 << (extra & 0x1f)));
+ break;
+
+ case MV64x60_EXTRA_ENET_ENAB:
+ mv64x60_set_bits(bh, MV64360_ENET2MEM_BAR_ENABLE,
+ (1 << (extra & 0x7)));
+ break;
+
+ case MV64x60_EXTRA_MPSC_ENAB:
+ mv64x60_set_bits(bh, MV64360_MPSC2MEM_BAR_ENABLE,
+ (1 << (extra & 0x3)));
+ break;
+
+ case MV64x60_EXTRA_IDMA_ENAB:
+ mv64x60_set_bits(bh, MV64360_IDMA2MEM_BAR_ENABLE,
+ (1 << (extra & 0x7)));
+ break;
+
+ default:
+ printk(KERN_ERR "mv64360_disable: %s\n",
+ "32bit table corrupted");
+ }
+ }
+
+ return;
+}
+
+/*
+ * mv64360_enable_window_64bit()
+ *
+ * On the MV64360, a 64-bit window is enabled by setting a bit in the window's
+ * base reg.
+ */
+static void __init
+mv64360_enable_window_64bit(struct mv64x60_handle *bh, u32 window)
+{
+ pr_debug("enable 64bit window: %d\n", window);
+
+ if ((mv64360_64bit_windows[window].base_lo_reg!= 0) &&
+ (mv64360_64bit_windows[window].size_reg != 0)) {
+
+ if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK)
+ == MV64x60_EXTRA_PCIACC_ENAB)
+
+ mv64x60_set_bits(bh,
+ mv64360_64bit_windows[window].base_lo_reg,
+ (1 << (mv64360_64bit_windows[window].extra &
+ 0x1f)));
+ else
+ printk(KERN_ERR "mv64360_enable: %s\n",
+ "64bit table corrupted");
+ }
+
+ return;
+}
+
+/*
+ * mv64360_disable_window_64bit()
+ *
+ * On a MV64360, a 64-bit window is disabled by clearing a bit in the window's
+ * base reg.
+ */
+static void __init
+mv64360_disable_window_64bit(struct mv64x60_handle *bh, u32 window)
+{
+ pr_debug("disable 64bit window: %d, base_reg: 0x%x, size_reg: 0x%x\n",
+ window, mv64360_64bit_windows[window].base_lo_reg,
+ mv64360_64bit_windows[window].size_reg);
+
+ if ((mv64360_64bit_windows[window].base_lo_reg != 0) &&
+ (mv64360_64bit_windows[window].size_reg != 0)) {
+
+ if ((mv64360_64bit_windows[window].extra & MV64x60_EXTRA_MASK)
+ == MV64x60_EXTRA_PCIACC_ENAB)
+
+ mv64x60_clr_bits(bh,
+ mv64360_64bit_windows[window].base_lo_reg,
+ (1 << (mv64360_64bit_windows[window].extra &
+ 0x1f)));
+ else
+ printk(KERN_ERR "mv64360_disable: %s\n",
+ "64bit table corrupted");
+ }
+
+ return;
+}
+
+/*
+ * mv64360_disable_all_windows()
+ *
+ * The MV64360 has a few windows that aren't represented in the table of
+ * windows at the top of this file. This routine turns all of them off
+ * except for the memory controller windows, of course.
+ */
+static void __init
+mv64360_disable_all_windows(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si)
+{
+ u32 preserve, i;
+
+ /* Disable 32bit windows (don't disable cpu->mem windows) */
+ for (i=MV64x60_CPU2DEV_0_WIN; i<MV64x60_32BIT_WIN_COUNT; i++) {
+ if (i < 32)
+ preserve = si->window_preserve_mask_32_lo & (1 << i);
+ else
+ preserve = si->window_preserve_mask_32_hi & (1<<(i-32));
+
+ if (!preserve)
+ mv64360_disable_window_32bit(bh, i);
+ }
+
+ /* Disable 64bit windows */
+ for (i=0; i<MV64x60_64BIT_WIN_COUNT; i++)
+ if (!(si->window_preserve_mask_64 & (1<<i)))
+ mv64360_disable_window_64bit(bh, i);
+
+ /* Turn off PCI->MEM access cntl wins not in mv64360_64bit_windows[] */
+ mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_4_BASE_LO, 0);
+ mv64x60_clr_bits(bh, MV64x60_PCI0_ACC_CNTL_5_BASE_LO, 0);
+ mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_4_BASE_LO, 0);
+ mv64x60_clr_bits(bh, MV64x60_PCI1_ACC_CNTL_5_BASE_LO, 0);
+
+ /* Disable all PCI-><whatever> windows */
+ mv64x60_set_bits(bh, MV64x60_PCI0_BAR_ENABLE, 0x0000f9ff);
+ mv64x60_set_bits(bh, MV64x60_PCI1_BAR_ENABLE, 0x0000f9ff);
+
+ return;
+}
+
+/*
+ * mv64360_config_io2mem_windows()
+ *
+ * ENET, MPSC, and IDMA ctlrs on the MV64[34]60 have separate windows that
+ * must be set up so that the respective ctlr can access system memory.
+ */
+static u32 enet_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = {
+ MV64x60_ENET2MEM_0_WIN, MV64x60_ENET2MEM_1_WIN,
+ MV64x60_ENET2MEM_2_WIN, MV64x60_ENET2MEM_3_WIN,
+};
+
+static u32 mpsc_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = {
+ MV64x60_MPSC2MEM_0_WIN, MV64x60_MPSC2MEM_1_WIN,
+ MV64x60_MPSC2MEM_2_WIN, MV64x60_MPSC2MEM_3_WIN,
+};
+
+static u32 idma_tab[MV64x60_CPU2MEM_WINDOWS] __initdata = {
+ MV64x60_IDMA2MEM_0_WIN, MV64x60_IDMA2MEM_1_WIN,
+ MV64x60_IDMA2MEM_2_WIN, MV64x60_IDMA2MEM_3_WIN,
+};
+
+static u32 dram_selects[MV64x60_CPU2MEM_WINDOWS] __initdata =
+ { 0xe, 0xd, 0xb, 0x7 };
+
+static void __init
+mv64360_config_io2mem_windows(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si,
+ u32 mem_windows[MV64x60_CPU2MEM_WINDOWS][2])
+{
+ u32 i, win;
+
+ pr_debug("config_io2regs_windows: enet, mpsc, idma -> bridge regs\n");
+
+ mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_0, 0);
+ mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_1, 0);
+ mv64x60_write(bh, MV64360_ENET2MEM_ACC_PROT_2, 0);
+
+ mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_0, 0);
+ mv64x60_write(bh, MV64360_MPSC2MEM_ACC_PROT_1, 0);
+
+ mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_0, 0);
+ mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_1, 0);
+ mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_2, 0);
+ mv64x60_write(bh, MV64360_IDMA2MEM_ACC_PROT_3, 0);
+
+ /* Assume that mem ctlr has no more windows than embedded I/O ctlr */
+ for (win=MV64x60_CPU2MEM_0_WIN,i=0;win<=MV64x60_CPU2MEM_3_WIN;win++,i++)
+ if (bh->ci->is_enabled_32bit(bh, win)) {
+ mv64x60_set_32bit_window(bh, enet_tab[i],
+ mem_windows[i][0], mem_windows[i][1],
+ (dram_selects[i] << 8) |
+ (si->enet_options[i] & 0x3000));
+ bh->ci->enable_window_32bit(bh, enet_tab[i]);
+
+ /* Give enet r/w access to memory region */
+ mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_0,
+ (0x3 << (i << 1)));
+ mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_1,
+ (0x3 << (i << 1)));
+ mv64x60_set_bits(bh, MV64360_ENET2MEM_ACC_PROT_2,
+ (0x3 << (i << 1)));
+
+ mv64x60_set_32bit_window(bh, mpsc_tab[i],
+ mem_windows[i][0], mem_windows[i][1],
+ (dram_selects[i] << 8) |
+ (si->mpsc_options[i] & 0x3000));
+ bh->ci->enable_window_32bit(bh, mpsc_tab[i]);
+
+ /* Give mpsc r/w access to memory region */
+ mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_0,
+ (0x3 << (i << 1)));
+ mv64x60_set_bits(bh, MV64360_MPSC2MEM_ACC_PROT_1,
+ (0x3 << (i << 1)));
+
+ mv64x60_set_32bit_window(bh, idma_tab[i],
+ mem_windows[i][0], mem_windows[i][1],
+ (dram_selects[i] << 8) |
+ (si->idma_options[i] & 0x3000));
+ bh->ci->enable_window_32bit(bh, idma_tab[i]);
+
+ /* Give idma r/w access to memory region */
+ mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_0,
+ (0x3 << (i << 1)));
+ mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_1,
+ (0x3 << (i << 1)));
+ mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_2,
+ (0x3 << (i << 1)));
+ mv64x60_set_bits(bh, MV64360_IDMA2MEM_ACC_PROT_3,
+ (0x3 << (i << 1)));
+ }
+
+ return;
+}
+
+/*
+ * mv64360_set_mpsc2regs_window()
+ *
+ * MPSC has a window to the bridge's internal registers. Call this routine
+ * to change that window so it doesn't conflict with the windows mapping the
+ * mpsc to system memory.
+ */
+static void __init
+mv64360_set_mpsc2regs_window(struct mv64x60_handle *bh, u32 base)
+{
+ pr_debug("set mpsc->internal regs, base: 0x%x\n", base);
+
+ mv64x60_write(bh, MV64360_MPSC2REGS_BASE, base & 0xffff0000);
+ return;
+}
+
+/*
+ * mv64360_chip_specific_init()
+ *
+ * No errata work arounds for the MV64360 implemented at this point.
+ */
+static void __init
+mv64360_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si)
+{
+#ifdef CONFIG_SERIAL_MPSC
+ mv64x60_mpsc0_pdata.brg_can_tune = 1;
+ mv64x60_mpsc0_pdata.cache_mgmt = 1;
+ mv64x60_mpsc1_pdata.brg_can_tune = 1;
+ mv64x60_mpsc1_pdata.cache_mgmt = 1;
+#endif
+
+ return;
+}
+
+/*
+ * mv64460_chip_specific_init()
+ *
+ * No errata work arounds for the MV64460 implemented at this point.
+ */
+static void __init
+mv64460_chip_specific_init(struct mv64x60_handle *bh,
+ struct mv64x60_setup_info *si)
+{
+#ifdef CONFIG_SERIAL_MPSC
+ mv64x60_mpsc0_pdata.brg_can_tune = 1;
+ mv64x60_mpsc1_pdata.brg_can_tune = 1;
+#endif
+ return;
+}
diff --git a/arch/ppc/syslib/mv64x60_dbg.c b/arch/ppc/syslib/mv64x60_dbg.c
new file mode 100644
index 000000000000..2927c7adf5e5
--- /dev/null
+++ b/arch/ppc/syslib/mv64x60_dbg.c
@@ -0,0 +1,123 @@
+/*
+ * arch/ppc/syslib/mv64x60_dbg.c
+ *
+ * KGDB and progress routines for the Marvell/Galileo MV64x60 (Discovery).
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+/*
+ *****************************************************************************
+ *
+ * Low-level MPSC/UART I/O routines
+ *
+ *****************************************************************************
+ */
+
+
+#include <linux/config.h>
+#include <linux/irq.h>
+#include <asm/delay.h>
+#include <asm/mv64x60.h>
+
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG)
+
+#define MPSC_CHR_1 0x000c
+#define MPSC_CHR_2 0x0010
+
+static struct mv64x60_handle mv64x60_dbg_bh;
+
+void
+mv64x60_progress_init(u32 base)
+{
+ mv64x60_dbg_bh.v_base = base;
+ return;
+}
+
+static void
+mv64x60_polled_putc(int chan, char c)
+{
+ u32 offset;
+
+ if (chan == 0)
+ offset = 0x8000;
+ else
+ offset = 0x9000;
+
+ mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_1, (u32)c);
+ mv64x60_write(&mv64x60_dbg_bh, offset + MPSC_CHR_2, 0x200);
+ udelay(2000);
+}
+
+void
+mv64x60_mpsc_progress(char *s, unsigned short hex)
+{
+ volatile char c;
+
+ mv64x60_polled_putc(0, '\r');
+
+ while ((c = *s++) != 0)
+ mv64x60_polled_putc(0, c);
+
+ mv64x60_polled_putc(0, '\n');
+ mv64x60_polled_putc(0, '\r');
+
+ return;
+}
+#endif /* CONFIG_SERIAL_TEXT_DEBUG */
+
+
+#if defined(CONFIG_KGDB)
+
+#if defined(CONFIG_KGDB_TTYS0)
+#define KGDB_PORT 0
+#elif defined(CONFIG_KGDB_TTYS1)
+#define KGDB_PORT 1
+#else
+#error "Invalid kgdb_tty port"
+#endif
+
+void
+putDebugChar(unsigned char c)
+{
+ mv64x60_polled_putc(KGDB_PORT, (char)c);
+}
+
+int
+getDebugChar(void)
+{
+ unsigned char c;
+
+ while (!mv64x60_polled_getc(KGDB_PORT, &c));
+ return (int)c;
+}
+
+void
+putDebugString(char* str)
+{
+ while (*str != '\0') {
+ putDebugChar(*str);
+ str++;
+ }
+ putDebugChar('\r');
+ return;
+}
+
+void
+kgdb_interruptible(int enable)
+{
+}
+
+void
+kgdb_map_scc(void)
+{
+ if (ppc_md.early_serial_map)
+ ppc_md.early_serial_map();
+}
+#endif /* CONFIG_KGDB */
diff --git a/arch/ppc/syslib/mv64x60_win.c b/arch/ppc/syslib/mv64x60_win.c
new file mode 100644
index 000000000000..b6f0f5dcf6ee
--- /dev/null
+++ b/arch/ppc/syslib/mv64x60_win.c
@@ -0,0 +1,1168 @@
+/*
+ * arch/ppc/syslib/mv64x60_win.c
+ *
+ * Tables with info on how to manipulate the 32 & 64 bit windows on the
+ * various types of Marvell bridge chips.
+ *
+ * Author: Mark A. Greer <mgreer@mvista.com>
+ *
+ * 2004 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/bootmem.h>
+#include <linux/mv643xx.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+#include <asm/delay.h>
+#include <asm/mv64x60.h>
+
+
+/*
+ *****************************************************************************
+ *
+ * Tables describing how to set up windows on each type of bridge
+ *
+ *****************************************************************************
+ */
+struct mv64x60_32bit_window
+ gt64260_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = {
+ /* CPU->MEM Windows */
+ [MV64x60_CPU2MEM_0_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_0_BASE,
+ .size_reg = MV64x60_CPU2MEM_0_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2MEM_1_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_1_BASE,
+ .size_reg = MV64x60_CPU2MEM_1_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2MEM_2_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_2_BASE,
+ .size_reg = MV64x60_CPU2MEM_2_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2MEM_3_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_3_BASE,
+ .size_reg = MV64x60_CPU2MEM_3_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->Device Windows */
+ [MV64x60_CPU2DEV_0_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_0_BASE,
+ .size_reg = MV64x60_CPU2DEV_0_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2DEV_1_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_1_BASE,
+ .size_reg = MV64x60_CPU2DEV_1_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2DEV_2_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_2_BASE,
+ .size_reg = MV64x60_CPU2DEV_2_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2DEV_3_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_3_BASE,
+ .size_reg = MV64x60_CPU2DEV_3_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->Boot Window */
+ [MV64x60_CPU2BOOT_WIN] = {
+ .base_reg = MV64x60_CPU2BOOT_0_BASE,
+ .size_reg = MV64x60_CPU2BOOT_0_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->PCI 0 Windows */
+ [MV64x60_CPU2PCI0_IO_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_IO_BASE,
+ .size_reg = MV64x60_CPU2PCI0_IO_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_0_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_0_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_0_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_1_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_1_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_1_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_2_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_2_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_2_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_3_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_3_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_3_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->PCI 1 Windows */
+ [MV64x60_CPU2PCI1_IO_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_IO_BASE,
+ .size_reg = MV64x60_CPU2PCI1_IO_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_0_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_0_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_0_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_1_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_1_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_1_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_2_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_2_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_2_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_3_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_3_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_3_SIZE,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->SRAM Window (64260 has no integrated SRAM) */
+ /* CPU->PCI 0 Remap I/O Window */
+ [MV64x60_CPU2PCI0_IO_REMAP_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_IO_REMAP,
+ .size_reg = 0,
+ .base_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->PCI 1 Remap I/O Window */
+ [MV64x60_CPU2PCI1_IO_REMAP_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_IO_REMAP,
+ .size_reg = 0,
+ .base_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU Memory Protection Windows */
+ [MV64x60_CPU_PROT_0_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_0,
+ .size_reg = MV64x60_CPU_PROT_SIZE_0,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU_PROT_1_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_1,
+ .size_reg = MV64x60_CPU_PROT_SIZE_1,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU_PROT_2_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_2,
+ .size_reg = MV64x60_CPU_PROT_SIZE_2,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU_PROT_3_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_3,
+ .size_reg = MV64x60_CPU_PROT_SIZE_3,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU Snoop Windows */
+ [MV64x60_CPU_SNOOP_0_WIN] = {
+ .base_reg = GT64260_CPU_SNOOP_BASE_0,
+ .size_reg = GT64260_CPU_SNOOP_SIZE_0,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU_SNOOP_1_WIN] = {
+ .base_reg = GT64260_CPU_SNOOP_BASE_1,
+ .size_reg = GT64260_CPU_SNOOP_SIZE_1,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU_SNOOP_2_WIN] = {
+ .base_reg = GT64260_CPU_SNOOP_BASE_2,
+ .size_reg = GT64260_CPU_SNOOP_SIZE_2,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU_SNOOP_3_WIN] = {
+ .base_reg = GT64260_CPU_SNOOP_BASE_3,
+ .size_reg = GT64260_CPU_SNOOP_SIZE_3,
+ .base_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* PCI 0->System Memory Remap Windows */
+ [MV64x60_PCI02MEM_REMAP_0_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_0_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_REMAP_1_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_REMAP_2_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_REMAP_3_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ /* PCI 1->System Memory Remap Windows */
+ [MV64x60_PCI12MEM_REMAP_0_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_0_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_REMAP_1_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_REMAP_2_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_REMAP_3_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ /* ENET->SRAM Window (64260 doesn't have separate windows) */
+ /* MPSC->SRAM Window (64260 doesn't have separate windows) */
+ /* IDMA->SRAM Window (64260 doesn't have separate windows) */
+};
+
+struct mv64x60_64bit_window
+ gt64260_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = {
+ /* CPU->PCI 0 MEM Remap Windows */
+ [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->PCI 1 MEM Remap Windows */
+ [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 12,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* PCI 0->MEM Access Control Windows */
+ [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_0_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_1_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_2_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_3_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* PCI 1->MEM Access Control Windows */
+ [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_0_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_1_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_2_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_3_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* PCI 0->MEM Snoop Windows */
+ [MV64x60_PCI02MEM_SNOOP_0_WIN] = {
+ .base_hi_reg = GT64260_PCI0_SNOOP_0_BASE_HI,
+ .base_lo_reg = GT64260_PCI0_SNOOP_0_BASE_LO,
+ .size_reg = GT64260_PCI0_SNOOP_0_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_SNOOP_1_WIN] = {
+ .base_hi_reg = GT64260_PCI0_SNOOP_1_BASE_HI,
+ .base_lo_reg = GT64260_PCI0_SNOOP_1_BASE_LO,
+ .size_reg = GT64260_PCI0_SNOOP_1_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_SNOOP_2_WIN] = {
+ .base_hi_reg = GT64260_PCI0_SNOOP_2_BASE_HI,
+ .base_lo_reg = GT64260_PCI0_SNOOP_2_BASE_LO,
+ .size_reg = GT64260_PCI0_SNOOP_2_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_SNOOP_3_WIN] = {
+ .base_hi_reg = GT64260_PCI0_SNOOP_3_BASE_HI,
+ .base_lo_reg = GT64260_PCI0_SNOOP_3_BASE_LO,
+ .size_reg = GT64260_PCI0_SNOOP_3_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* PCI 1->MEM Snoop Windows */
+ [MV64x60_PCI12MEM_SNOOP_0_WIN] = {
+ .base_hi_reg = GT64260_PCI1_SNOOP_0_BASE_HI,
+ .base_lo_reg = GT64260_PCI1_SNOOP_0_BASE_LO,
+ .size_reg = GT64260_PCI1_SNOOP_0_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_SNOOP_1_WIN] = {
+ .base_hi_reg = GT64260_PCI1_SNOOP_1_BASE_HI,
+ .base_lo_reg = GT64260_PCI1_SNOOP_1_BASE_LO,
+ .size_reg = GT64260_PCI1_SNOOP_1_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_SNOOP_2_WIN] = {
+ .base_hi_reg = GT64260_PCI1_SNOOP_2_BASE_HI,
+ .base_lo_reg = GT64260_PCI1_SNOOP_2_BASE_LO,
+ .size_reg = GT64260_PCI1_SNOOP_2_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_SNOOP_3_WIN] = {
+ .base_hi_reg = GT64260_PCI1_SNOOP_3_BASE_HI,
+ .base_lo_reg = GT64260_PCI1_SNOOP_3_BASE_LO,
+ .size_reg = GT64260_PCI1_SNOOP_3_SIZE,
+ .base_lo_bits = 12,
+ .size_bits = 12,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+};
+
+struct mv64x60_32bit_window
+ mv64360_32bit_windows[MV64x60_32BIT_WIN_COUNT] __initdata = {
+ /* CPU->MEM Windows */
+ [MV64x60_CPU2MEM_0_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_0_BASE,
+ .size_reg = MV64x60_CPU2MEM_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 0 },
+ [MV64x60_CPU2MEM_1_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_1_BASE,
+ .size_reg = MV64x60_CPU2MEM_1_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 1 },
+ [MV64x60_CPU2MEM_2_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_2_BASE,
+ .size_reg = MV64x60_CPU2MEM_2_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 2 },
+ [MV64x60_CPU2MEM_3_WIN] = {
+ .base_reg = MV64x60_CPU2MEM_3_BASE,
+ .size_reg = MV64x60_CPU2MEM_3_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 3 },
+ /* CPU->Device Windows */
+ [MV64x60_CPU2DEV_0_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_0_BASE,
+ .size_reg = MV64x60_CPU2DEV_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 4 },
+ [MV64x60_CPU2DEV_1_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_1_BASE,
+ .size_reg = MV64x60_CPU2DEV_1_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 5 },
+ [MV64x60_CPU2DEV_2_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_2_BASE,
+ .size_reg = MV64x60_CPU2DEV_2_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 6 },
+ [MV64x60_CPU2DEV_3_WIN] = {
+ .base_reg = MV64x60_CPU2DEV_3_BASE,
+ .size_reg = MV64x60_CPU2DEV_3_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 7 },
+ /* CPU->Boot Window */
+ [MV64x60_CPU2BOOT_WIN] = {
+ .base_reg = MV64x60_CPU2BOOT_0_BASE,
+ .size_reg = MV64x60_CPU2BOOT_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 8 },
+ /* CPU->PCI 0 Windows */
+ [MV64x60_CPU2PCI0_IO_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_IO_BASE,
+ .size_reg = MV64x60_CPU2PCI0_IO_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 9 },
+ [MV64x60_CPU2PCI0_MEM_0_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_0_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 10 },
+ [MV64x60_CPU2PCI0_MEM_1_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_1_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_1_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 11 },
+ [MV64x60_CPU2PCI0_MEM_2_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_2_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_2_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 12 },
+ [MV64x60_CPU2PCI0_MEM_3_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_MEM_3_BASE,
+ .size_reg = MV64x60_CPU2PCI0_MEM_3_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 13 },
+ /* CPU->PCI 1 Windows */
+ [MV64x60_CPU2PCI1_IO_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_IO_BASE,
+ .size_reg = MV64x60_CPU2PCI1_IO_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 14 },
+ [MV64x60_CPU2PCI1_MEM_0_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_0_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 15 },
+ [MV64x60_CPU2PCI1_MEM_1_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_1_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_1_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 16 },
+ [MV64x60_CPU2PCI1_MEM_2_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_2_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_2_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 17 },
+ [MV64x60_CPU2PCI1_MEM_3_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_MEM_3_BASE,
+ .size_reg = MV64x60_CPU2PCI1_MEM_3_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 18 },
+ /* CPU->SRAM Window */
+ [MV64x60_CPU2SRAM_WIN] = {
+ .base_reg = MV64360_CPU2SRAM_BASE,
+ .size_reg = 0,
+ .base_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUWIN_ENAB | 19 },
+ /* CPU->PCI 0 Remap I/O Window */
+ [MV64x60_CPU2PCI0_IO_REMAP_WIN] = {
+ .base_reg = MV64x60_CPU2PCI0_IO_REMAP,
+ .size_reg = 0,
+ .base_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->PCI 1 Remap I/O Window */
+ [MV64x60_CPU2PCI1_IO_REMAP_WIN] = {
+ .base_reg = MV64x60_CPU2PCI1_IO_REMAP,
+ .size_reg = 0,
+ .base_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU Memory Protection Windows */
+ [MV64x60_CPU_PROT_0_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_0,
+ .size_reg = MV64x60_CPU_PROT_SIZE_0,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+ [MV64x60_CPU_PROT_1_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_1,
+ .size_reg = MV64x60_CPU_PROT_SIZE_1,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+ [MV64x60_CPU_PROT_2_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_2,
+ .size_reg = MV64x60_CPU_PROT_SIZE_2,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+ [MV64x60_CPU_PROT_3_WIN] = {
+ .base_reg = MV64x60_CPU_PROT_BASE_3,
+ .size_reg = MV64x60_CPU_PROT_SIZE_3,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = MV64x60_EXTRA_CPUPROT_ENAB | 31 },
+ /* CPU Snoop Windows -- don't exist on 64360 */
+ /* PCI 0->System Memory Remap Windows */
+ [MV64x60_PCI02MEM_REMAP_0_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_0_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_REMAP_1_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_REMAP_2_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI02MEM_REMAP_3_WIN] = {
+ .base_reg = MV64x60_PCI0_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ /* PCI 1->System Memory Remap Windows */
+ [MV64x60_PCI12MEM_REMAP_0_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_0_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_REMAP_1_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_REMAP_2_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ [MV64x60_PCI12MEM_REMAP_3_WIN] = {
+ .base_reg = MV64x60_PCI1_SLAVE_MEM_1_REMAP,
+ .size_reg = 0,
+ .base_bits = 20,
+ .size_bits = 0,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = 0 },
+ /* ENET->System Memory Windows */
+ [MV64x60_ENET2MEM_0_WIN] = {
+ .base_reg = MV64360_ENET2MEM_0_BASE,
+ .size_reg = MV64360_ENET2MEM_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_ENET_ENAB | 0 },
+ [MV64x60_ENET2MEM_1_WIN] = {
+ .base_reg = MV64360_ENET2MEM_1_BASE,
+ .size_reg = MV64360_ENET2MEM_1_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_ENET_ENAB | 1 },
+ [MV64x60_ENET2MEM_2_WIN] = {
+ .base_reg = MV64360_ENET2MEM_2_BASE,
+ .size_reg = MV64360_ENET2MEM_2_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_ENET_ENAB | 2 },
+ [MV64x60_ENET2MEM_3_WIN] = {
+ .base_reg = MV64360_ENET2MEM_3_BASE,
+ .size_reg = MV64360_ENET2MEM_3_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_ENET_ENAB | 3 },
+ [MV64x60_ENET2MEM_4_WIN] = {
+ .base_reg = MV64360_ENET2MEM_4_BASE,
+ .size_reg = MV64360_ENET2MEM_4_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_ENET_ENAB | 4 },
+ [MV64x60_ENET2MEM_5_WIN] = {
+ .base_reg = MV64360_ENET2MEM_5_BASE,
+ .size_reg = MV64360_ENET2MEM_5_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_ENET_ENAB | 5 },
+ /* MPSC->System Memory Windows */
+ [MV64x60_MPSC2MEM_0_WIN] = {
+ .base_reg = MV64360_MPSC2MEM_0_BASE,
+ .size_reg = MV64360_MPSC2MEM_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_MPSC_ENAB | 0 },
+ [MV64x60_MPSC2MEM_1_WIN] = {
+ .base_reg = MV64360_MPSC2MEM_1_BASE,
+ .size_reg = MV64360_MPSC2MEM_1_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_MPSC_ENAB | 1 },
+ [MV64x60_MPSC2MEM_2_WIN] = {
+ .base_reg = MV64360_MPSC2MEM_2_BASE,
+ .size_reg = MV64360_MPSC2MEM_2_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_MPSC_ENAB | 2 },
+ [MV64x60_MPSC2MEM_3_WIN] = {
+ .base_reg = MV64360_MPSC2MEM_3_BASE,
+ .size_reg = MV64360_MPSC2MEM_3_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_MPSC_ENAB | 3 },
+ /* IDMA->System Memory Windows */
+ [MV64x60_IDMA2MEM_0_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_0_BASE,
+ .size_reg = MV64360_IDMA2MEM_0_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 0 },
+ [MV64x60_IDMA2MEM_1_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_1_BASE,
+ .size_reg = MV64360_IDMA2MEM_1_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 1 },
+ [MV64x60_IDMA2MEM_2_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_2_BASE,
+ .size_reg = MV64360_IDMA2MEM_2_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 2 },
+ [MV64x60_IDMA2MEM_3_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_3_BASE,
+ .size_reg = MV64360_IDMA2MEM_3_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 3 },
+ [MV64x60_IDMA2MEM_4_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_4_BASE,
+ .size_reg = MV64360_IDMA2MEM_4_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 4 },
+ [MV64x60_IDMA2MEM_5_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_5_BASE,
+ .size_reg = MV64360_IDMA2MEM_5_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 5 },
+ [MV64x60_IDMA2MEM_6_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_6_BASE,
+ .size_reg = MV64360_IDMA2MEM_6_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 6 },
+ [MV64x60_IDMA2MEM_7_WIN] = {
+ .base_reg = MV64360_IDMA2MEM_7_BASE,
+ .size_reg = MV64360_IDMA2MEM_7_SIZE,
+ .base_bits = 16,
+ .size_bits = 16,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_IDMA_ENAB | 7 },
+};
+
+struct mv64x60_64bit_window
+ mv64360_64bit_windows[MV64x60_64BIT_WIN_COUNT] __initdata = {
+ /* CPU->PCI 0 MEM Remap Windows */
+ [MV64x60_CPU2PCI0_MEM_0_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_0_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_1_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_1_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_2_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_2_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI0_MEM_3_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI0_MEM_3_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* CPU->PCI 1 MEM Remap Windows */
+ [MV64x60_CPU2PCI1_MEM_0_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_0_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_1_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_1_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_2_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_2_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ [MV64x60_CPU2PCI1_MEM_3_REMAP_WIN] = {
+ .base_hi_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_HI,
+ .base_lo_reg = MV64x60_CPU2PCI1_MEM_3_REMAP_LO,
+ .size_reg = 0,
+ .base_lo_bits = 16,
+ .size_bits = 0,
+ .get_from_field = mv64x60_shift_left,
+ .map_to_field = mv64x60_shift_right,
+ .extra = 0 },
+ /* PCI 0->MEM Access Control Windows */
+ [MV64x60_PCI02MEM_ACC_CNTL_0_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_0_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_0_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ [MV64x60_PCI02MEM_ACC_CNTL_1_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_1_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_1_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ [MV64x60_PCI02MEM_ACC_CNTL_2_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_2_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_2_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ [MV64x60_PCI02MEM_ACC_CNTL_3_WIN] = {
+ .base_hi_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_HI,
+ .base_lo_reg = MV64x60_PCI0_ACC_CNTL_3_BASE_LO,
+ .size_reg = MV64x60_PCI0_ACC_CNTL_3_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ /* PCI 1->MEM Access Control Windows */
+ [MV64x60_PCI12MEM_ACC_CNTL_0_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_0_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_0_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ [MV64x60_PCI12MEM_ACC_CNTL_1_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_1_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_1_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ [MV64x60_PCI12MEM_ACC_CNTL_2_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_2_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_2_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ [MV64x60_PCI12MEM_ACC_CNTL_3_WIN] = {
+ .base_hi_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_HI,
+ .base_lo_reg = MV64x60_PCI1_ACC_CNTL_3_BASE_LO,
+ .size_reg = MV64x60_PCI1_ACC_CNTL_3_SIZE,
+ .base_lo_bits = 20,
+ .size_bits = 20,
+ .get_from_field = mv64x60_mask,
+ .map_to_field = mv64x60_mask,
+ .extra = MV64x60_EXTRA_PCIACC_ENAB | 0 },
+ /* PCI 0->MEM Snoop Windows -- don't exist on 64360 */
+ /* PCI 1->MEM Snoop Windows -- don't exist on 64360 */
+};
diff --git a/arch/ppc/syslib/ocp.c b/arch/ppc/syslib/ocp.c
new file mode 100644
index 000000000000..a5156c5179a6
--- /dev/null
+++ b/arch/ppc/syslib/ocp.c
@@ -0,0 +1,485 @@
+/*
+ * ocp.c
+ *
+ * (c) Benjamin Herrenschmidt (benh@kernel.crashing.org)
+ * Mipsys - France
+ *
+ * Derived from work (c) Armin Kuster akuster@pacbell.net
+ *
+ * Additional support and port to 2.6 LDM/sysfs by
+ * Matt Porter <mporter@kernel.crashing.org>
+ * Copyright 2004 MontaVista Software, Inc.
+ *
+ * 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.
+ *
+ * OCP (On Chip Peripheral) is a software emulated "bus" with a
+ * pseudo discovery method for dumb peripherals. Usually these type
+ * of peripherals are found on embedded SoC (System On a Chip)
+ * processors or highly integrated system controllers that have
+ * a host bridge and many peripherals. Common examples where
+ * this is already used include the PPC4xx, PPC85xx, MPC52xx,
+ * and MV64xxx parts.
+ *
+ * This subsystem creates a standard OCP bus type within the
+ * device model. The devices on the OCP bus are seeded by an
+ * an initial OCP device array created by the arch-specific
+ * Device entries can be added/removed/modified through OCP
+ * helper functions to accomodate system and board-specific
+ * parameters commonly found in embedded systems. OCP also
+ * provides a standard method for devices to describe extended
+ * attributes about themselves to the system. A standard access
+ * method allows OCP drivers to obtain the information, both
+ * SoC-specific and system/board-specific, needed for operation.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/list.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/bootmem.h>
+#include <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/ocp.h>
+#include <asm/errno.h>
+#include <asm/rwsem.h>
+#include <asm/semaphore.h>
+
+//#define DBG(x) printk x
+#define DBG(x)
+
+extern int mem_init_done;
+
+extern struct ocp_def core_ocp[]; /* Static list of devices, provided by
+ CPU core */
+
+LIST_HEAD(ocp_devices); /* List of all OCP devices */
+DECLARE_RWSEM(ocp_devices_sem); /* Global semaphores for those lists */
+
+static int ocp_inited;
+
+/* Sysfs support */
+#define OCP_DEF_ATTR(field, format_string) \
+static ssize_t \
+show_##field(struct device *dev, char *buf) \
+{ \
+ struct ocp_device *odev = to_ocp_dev(dev); \
+ \
+ return sprintf(buf, format_string, odev->def->field); \
+} \
+static DEVICE_ATTR(field, S_IRUGO, show_##field, NULL);
+
+OCP_DEF_ATTR(vendor, "0x%04x\n");
+OCP_DEF_ATTR(function, "0x%04x\n");
+OCP_DEF_ATTR(index, "0x%04x\n");
+#ifdef CONFIG_PTE_64BIT
+OCP_DEF_ATTR(paddr, "0x%016Lx\n");
+#else
+OCP_DEF_ATTR(paddr, "0x%08lx\n");
+#endif
+OCP_DEF_ATTR(irq, "%d\n");
+OCP_DEF_ATTR(pm, "%lu\n");
+
+void ocp_create_sysfs_dev_files(struct ocp_device *odev)
+{
+ struct device *dev = &odev->dev;
+
+ /* Current OCP device def attributes */
+ device_create_file(dev, &dev_attr_vendor);
+ device_create_file(dev, &dev_attr_function);
+ device_create_file(dev, &dev_attr_index);
+ device_create_file(dev, &dev_attr_paddr);
+ device_create_file(dev, &dev_attr_irq);
+ device_create_file(dev, &dev_attr_pm);
+ /* Current OCP device additions attributes */
+ if (odev->def->additions && odev->def->show)
+ odev->def->show(dev);
+}
+
+/**
+ * ocp_device_match - Match one driver to one device
+ * @drv: driver to match
+ * @dev: device to match
+ *
+ * This function returns 0 if the driver and device don't match
+ */
+static int
+ocp_device_match(struct device *dev, struct device_driver *drv)
+{
+ struct ocp_device *ocp_dev = to_ocp_dev(dev);
+ struct ocp_driver *ocp_drv = to_ocp_drv(drv);
+ const struct ocp_device_id *ids = ocp_drv->id_table;
+
+ if (!ids)
+ return 0;
+
+ while (ids->vendor || ids->function) {
+ if ((ids->vendor == OCP_ANY_ID
+ || ids->vendor == ocp_dev->def->vendor)
+ && (ids->function == OCP_ANY_ID
+ || ids->function == ocp_dev->def->function))
+ return 1;
+ ids++;
+ }
+ return 0;
+}
+
+static int
+ocp_device_probe(struct device *dev)
+{
+ int error = 0;
+ struct ocp_driver *drv;
+ struct ocp_device *ocp_dev;
+
+ drv = to_ocp_drv(dev->driver);
+ ocp_dev = to_ocp_dev(dev);
+
+ if (drv->probe) {
+ error = drv->probe(ocp_dev);
+ if (error >= 0) {
+ ocp_dev->driver = drv;
+ error = 0;
+ }
+ }
+ return error;
+}
+
+static int
+ocp_device_remove(struct device *dev)
+{
+ struct ocp_device *ocp_dev = to_ocp_dev(dev);
+
+ if (ocp_dev->driver) {
+ if (ocp_dev->driver->remove)
+ ocp_dev->driver->remove(ocp_dev);
+ ocp_dev->driver = NULL;
+ }
+ return 0;
+}
+
+static int
+ocp_device_suspend(struct device *dev, u32 state)
+{
+ struct ocp_device *ocp_dev = to_ocp_dev(dev);
+ struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver);
+
+ if (dev->driver && ocp_drv->suspend)
+ return ocp_drv->suspend(ocp_dev, state);
+ return 0;
+}
+
+static int
+ocp_device_resume(struct device *dev)
+{
+ struct ocp_device *ocp_dev = to_ocp_dev(dev);
+ struct ocp_driver *ocp_drv = to_ocp_drv(dev->driver);
+
+ if (dev->driver && ocp_drv->resume)
+ return ocp_drv->resume(ocp_dev);
+ return 0;
+}
+
+struct bus_type ocp_bus_type = {
+ .name = "ocp",
+ .match = ocp_device_match,
+ .suspend = ocp_device_suspend,
+ .resume = ocp_device_resume,
+};
+
+/**
+ * ocp_register_driver - Register an OCP driver
+ * @drv: pointer to statically defined ocp_driver structure
+ *
+ * The driver's probe() callback is called either recursively
+ * by this function or upon later call of ocp_driver_init
+ *
+ * NOTE: Detection of devices is a 2 pass step on this implementation,
+ * hotswap isn't supported. First, all OCP devices are put in the device
+ * list, _then_ all drivers are probed on each match.
+ */
+int
+ocp_register_driver(struct ocp_driver *drv)
+{
+ /* initialize common driver fields */
+ drv->driver.name = drv->name;
+ drv->driver.bus = &ocp_bus_type;
+ drv->driver.probe = ocp_device_probe;
+ drv->driver.remove = ocp_device_remove;
+
+ /* register with core */
+ return driver_register(&drv->driver);
+}
+
+/**
+ * ocp_unregister_driver - Unregister an OCP driver
+ * @drv: pointer to statically defined ocp_driver structure
+ *
+ * The driver's remove() callback is called recursively
+ * by this function for any device already registered
+ */
+void
+ocp_unregister_driver(struct ocp_driver *drv)
+{
+ DBG(("ocp: ocp_unregister_driver(%s)...\n", drv->name));
+
+ driver_unregister(&drv->driver);
+
+ DBG(("ocp: ocp_unregister_driver(%s)... done.\n", drv->name));
+}
+
+/* Core of ocp_find_device(). Caller must hold ocp_devices_sem */
+static struct ocp_device *
+__ocp_find_device(unsigned int vendor, unsigned int function, int index)
+{
+ struct list_head *entry;
+ struct ocp_device *dev, *found = NULL;
+
+ DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index));
+
+ list_for_each(entry, &ocp_devices) {
+ dev = list_entry(entry, struct ocp_device, link);
+ if (vendor != OCP_ANY_ID && vendor != dev->def->vendor)
+ continue;
+ if (function != OCP_ANY_ID && function != dev->def->function)
+ continue;
+ if (index != OCP_ANY_INDEX && index != dev->def->index)
+ continue;
+ found = dev;
+ break;
+ }
+
+ DBG(("ocp: __ocp_find_device(vendor: %x, function: %x, index: %d)... done\n", vendor, function, index));
+
+ return found;
+}
+
+/**
+ * ocp_find_device - Find a device by function & index
+ * @vendor: vendor ID of the device (or OCP_ANY_ID)
+ * @function: function code of the device (or OCP_ANY_ID)
+ * @idx: index of the device (or OCP_ANY_INDEX)
+ *
+ * This function allows a lookup of a given function by it's
+ * index, it's typically used to find the MAL or ZMII associated
+ * with an EMAC or similar horrors.
+ * You can pass vendor, though you usually want OCP_ANY_ID there...
+ */
+struct ocp_device *
+ocp_find_device(unsigned int vendor, unsigned int function, int index)
+{
+ struct ocp_device *dev;
+
+ down_read(&ocp_devices_sem);
+ dev = __ocp_find_device(vendor, function, index);
+ up_read(&ocp_devices_sem);
+
+ return dev;
+}
+
+/**
+ * ocp_get_one_device - Find a def by function & index
+ * @vendor: vendor ID of the device (or OCP_ANY_ID)
+ * @function: function code of the device (or OCP_ANY_ID)
+ * @idx: index of the device (or OCP_ANY_INDEX)
+ *
+ * This function allows a lookup of a given ocp_def by it's
+ * vendor, function, and index. The main purpose for is to
+ * allow modification of the def before binding to the driver
+ */
+struct ocp_def *
+ocp_get_one_device(unsigned int vendor, unsigned int function, int index)
+{
+ struct ocp_device *dev;
+ struct ocp_def *found = NULL;
+
+ DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)...\n",
+ vendor, function, index));
+
+ dev = ocp_find_device(vendor, function, index);
+
+ if (dev)
+ found = dev->def;
+
+ DBG(("ocp: ocp_get_one_device(vendor: %x, function: %x, index: %d)... done.\n",
+ vendor, function, index));
+
+ return found;
+}
+
+/**
+ * ocp_add_one_device - Add a device
+ * @def: static device definition structure
+ *
+ * This function adds a device definition to the
+ * device list. It may only be called before
+ * ocp_driver_init() and will return an error
+ * otherwise.
+ */
+int
+ocp_add_one_device(struct ocp_def *def)
+{
+ struct ocp_device *dev;
+
+ DBG(("ocp: ocp_add_one_device()...\n"));
+
+ /* Can't be called after ocp driver init */
+ if (ocp_inited)
+ return 1;
+
+ if (mem_init_done)
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ else
+ dev = alloc_bootmem(sizeof(*dev));
+
+ if (dev == NULL)
+ return 1;
+ memset(dev, 0, sizeof(*dev));
+ dev->def = def;
+ dev->current_state = 4;
+ sprintf(dev->name, "OCP device %04x:%04x:%04x",
+ dev->def->vendor, dev->def->function, dev->def->index);
+ down_write(&ocp_devices_sem);
+ list_add_tail(&dev->link, &ocp_devices);
+ up_write(&ocp_devices_sem);
+
+ DBG(("ocp: ocp_add_one_device()...done\n"));
+
+ return 0;
+}
+
+/**
+ * ocp_remove_one_device - Remove a device by function & index
+ * @vendor: vendor ID of the device (or OCP_ANY_ID)
+ * @function: function code of the device (or OCP_ANY_ID)
+ * @idx: index of the device (or OCP_ANY_INDEX)
+ *
+ * This function allows removal of a given function by its
+ * index. It may only be called before ocp_driver_init()
+ * and will return an error otherwise.
+ */
+int
+ocp_remove_one_device(unsigned int vendor, unsigned int function, int index)
+{
+ struct ocp_device *dev;
+
+ DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)...\n", vendor, function, index));
+
+ /* Can't be called after ocp driver init */
+ if (ocp_inited)
+ return 1;
+
+ down_write(&ocp_devices_sem);
+ dev = __ocp_find_device(vendor, function, index);
+ list_del((struct list_head *)dev);
+ up_write(&ocp_devices_sem);
+
+ DBG(("ocp: ocp_remove_one_device(vendor: %x, function: %x, index: %d)... done.\n", vendor, function, index));
+
+ return 0;
+}
+
+/**
+ * ocp_for_each_device - Iterate over OCP devices
+ * @callback: routine to execute for each ocp device.
+ * @arg: user data to be passed to callback routine.
+ *
+ * This routine holds the ocp_device semaphore, so the
+ * callback routine cannot modify the ocp_device list.
+ */
+void
+ocp_for_each_device(void(*callback)(struct ocp_device *, void *arg), void *arg)
+{
+ struct list_head *entry;
+
+ if (callback) {
+ down_read(&ocp_devices_sem);
+ list_for_each(entry, &ocp_devices)
+ callback(list_entry(entry, struct ocp_device, link),
+ arg);
+ up_read(&ocp_devices_sem);
+ }
+}
+
+/**
+ * ocp_early_init - Init OCP device management
+ *
+ * This function builds the list of devices before setup_arch.
+ * This allows platform code to modify the device lists before
+ * they are bound to drivers (changes to paddr, removing devices
+ * etc)
+ */
+int __init
+ocp_early_init(void)
+{
+ struct ocp_def *def;
+
+ DBG(("ocp: ocp_early_init()...\n"));
+
+ /* Fill the devices list */
+ for (def = core_ocp; def->vendor != OCP_VENDOR_INVALID; def++)
+ ocp_add_one_device(def);
+
+ DBG(("ocp: ocp_early_init()... done.\n"));
+
+ return 0;
+}
+
+/**
+ * ocp_driver_init - Init OCP device management
+ *
+ * This function is meant to be called via OCP bus registration.
+ */
+static int __init
+ocp_driver_init(void)
+{
+ int ret = 0, index = 0;
+ struct device *ocp_bus;
+ struct list_head *entry;
+ struct ocp_device *dev;
+
+ if (ocp_inited)
+ return ret;
+ ocp_inited = 1;
+
+ DBG(("ocp: ocp_driver_init()...\n"));
+
+ /* Allocate/register primary OCP bus */
+ ocp_bus = kmalloc(sizeof(struct device), GFP_KERNEL);
+ if (ocp_bus == NULL)
+ return 1;
+ memset(ocp_bus, 0, sizeof(struct device));
+ strcpy(ocp_bus->bus_id, "ocp");
+
+ bus_register(&ocp_bus_type);
+
+ device_register(ocp_bus);
+
+ /* Put each OCP device into global device list */
+ list_for_each(entry, &ocp_devices) {
+ dev = list_entry(entry, struct ocp_device, link);
+ sprintf(dev->dev.bus_id, "%2.2x", index);
+ dev->dev.parent = ocp_bus;
+ dev->dev.bus = &ocp_bus_type;
+ device_register(&dev->dev);
+ ocp_create_sysfs_dev_files(dev);
+ index++;
+ }
+
+ DBG(("ocp: ocp_driver_init()... done.\n"));
+
+ return 0;
+}
+
+postcore_initcall(ocp_driver_init);
+
+EXPORT_SYMBOL(ocp_bus_type);
+EXPORT_SYMBOL(ocp_find_device);
+EXPORT_SYMBOL(ocp_register_driver);
+EXPORT_SYMBOL(ocp_unregister_driver);
diff --git a/arch/ppc/syslib/of_device.c b/arch/ppc/syslib/of_device.c
new file mode 100644
index 000000000000..46269ed21aee
--- /dev/null
+++ b/arch/ppc/syslib/of_device.c
@@ -0,0 +1,273 @@
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/of_device.h>
+
+/**
+ * of_match_device - Tell if an of_device structure has a matching
+ * of_match structure
+ * @ids: array of of device match structures to search in
+ * @dev: the of device structure to match against
+ *
+ * Used by a driver to check whether an of_device present in the
+ * system is in its list of supported devices.
+ */
+const struct of_match * of_match_device(const struct of_match *matches,
+ const struct of_device *dev)
+{
+ if (!dev->node)
+ return NULL;
+ while (matches->name || matches->type || matches->compatible) {
+ int match = 1;
+ if (matches->name && matches->name != OF_ANY_MATCH)
+ match &= dev->node->name
+ && !strcmp(matches->name, dev->node->name);
+ if (matches->type && matches->type != OF_ANY_MATCH)
+ match &= dev->node->type
+ && !strcmp(matches->type, dev->node->type);
+ if (matches->compatible && matches->compatible != OF_ANY_MATCH)
+ match &= device_is_compatible(dev->node,
+ matches->compatible);
+ if (match)
+ return matches;
+ matches++;
+ }
+ return NULL;
+}
+
+static int of_platform_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * of_drv = to_of_platform_driver(drv);
+ const struct of_match * matches = of_drv->match_table;
+
+ if (!matches)
+ return 0;
+
+ return of_match_device(matches, of_dev) != NULL;
+}
+
+struct of_device *of_dev_get(struct of_device *dev)
+{
+ struct device *tmp;
+
+ if (!dev)
+ return NULL;
+ tmp = get_device(&dev->dev);
+ if (tmp)
+ return to_of_device(tmp);
+ else
+ return NULL;
+}
+
+void of_dev_put(struct of_device *dev)
+{
+ if (dev)
+ put_device(&dev->dev);
+}
+
+
+static int of_device_probe(struct device *dev)
+{
+ int error = -ENODEV;
+ struct of_platform_driver *drv;
+ struct of_device *of_dev;
+ const struct of_match *match;
+
+ drv = to_of_platform_driver(dev->driver);
+ of_dev = to_of_device(dev);
+
+ if (!drv->probe)
+ return error;
+
+ of_dev_get(of_dev);
+
+ match = of_match_device(drv->match_table, of_dev);
+ if (match)
+ error = drv->probe(of_dev, match);
+ if (error)
+ of_dev_put(of_dev);
+
+ return error;
+}
+
+static int of_device_remove(struct device *dev)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
+
+ if (dev->driver && drv->remove)
+ drv->remove(of_dev);
+ return 0;
+}
+
+static int of_device_suspend(struct device *dev, u32 state)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
+ int error = 0;
+
+ if (dev->driver && drv->suspend)
+ error = drv->suspend(of_dev, state);
+ return error;
+}
+
+static int of_device_resume(struct device * dev)
+{
+ struct of_device * of_dev = to_of_device(dev);
+ struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
+ int error = 0;
+
+ if (dev->driver && drv->resume)
+ error = drv->resume(of_dev);
+ return error;
+}
+
+struct bus_type of_platform_bus_type = {
+ .name = "of_platform",
+ .match = of_platform_bus_match,
+ .suspend = of_device_suspend,
+ .resume = of_device_resume,
+};
+
+static int __init of_bus_driver_init(void)
+{
+ return bus_register(&of_platform_bus_type);
+}
+
+postcore_initcall(of_bus_driver_init);
+
+int of_register_driver(struct of_platform_driver *drv)
+{
+ int count = 0;
+
+ /* initialize common driver fields */
+ drv->driver.name = drv->name;
+ drv->driver.bus = &of_platform_bus_type;
+ drv->driver.probe = of_device_probe;
+ drv->driver.remove = of_device_remove;
+
+ /* register with core */
+ count = driver_register(&drv->driver);
+ return count ? count : 1;
+}
+
+void of_unregister_driver(struct of_platform_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+
+
+static ssize_t dev_show_devspec(struct device *dev, char *buf)
+{
+ struct of_device *ofdev;
+
+ ofdev = to_of_device(dev);
+ return sprintf(buf, "%s", ofdev->node->full_name);
+}
+
+static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL);
+
+/**
+ * of_release_dev - free an of device structure when all users of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this of device are
+ * done.
+ */
+void of_release_dev(struct device *dev)
+{
+ struct of_device *ofdev;
+
+ ofdev = to_of_device(dev);
+ of_node_put(ofdev->node);
+ kfree(ofdev);
+}
+
+int of_device_register(struct of_device *ofdev)
+{
+ int rc;
+ struct of_device **odprop;
+
+ BUG_ON(ofdev->node == NULL);
+
+ odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL);
+ if (!odprop) {
+ struct property *new_prop;
+
+ new_prop = kmalloc(sizeof(struct property) + sizeof(struct of_device *),
+ GFP_KERNEL);
+ if (new_prop == NULL)
+ return -ENOMEM;
+ new_prop->name = "linux,device";
+ new_prop->length = sizeof(sizeof(struct of_device *));
+ new_prop->value = (unsigned char *)&new_prop[1];
+ odprop = (struct of_device **)new_prop->value;
+ *odprop = NULL;
+ prom_add_property(ofdev->node, new_prop);
+ }
+ *odprop = ofdev;
+
+ rc = device_register(&ofdev->dev);
+ if (rc)
+ return rc;
+
+ device_create_file(&ofdev->dev, &dev_attr_devspec);
+
+ return 0;
+}
+
+void of_device_unregister(struct of_device *ofdev)
+{
+ struct of_device **odprop;
+
+ device_remove_file(&ofdev->dev, &dev_attr_devspec);
+
+ odprop = (struct of_device **)get_property(ofdev->node, "linux,device", NULL);
+ if (odprop)
+ *odprop = NULL;
+
+ device_unregister(&ofdev->dev);
+}
+
+struct of_device* of_platform_device_create(struct device_node *np, const char *bus_id)
+{
+ struct of_device *dev;
+ u32 *reg;
+
+ dev = kmalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+ memset(dev, 0, sizeof(*dev));
+
+ dev->node = of_node_get(np);
+ dev->dma_mask = 0xffffffffUL;
+ dev->dev.dma_mask = &dev->dma_mask;
+ dev->dev.parent = NULL;
+ dev->dev.bus = &of_platform_bus_type;
+ dev->dev.release = of_release_dev;
+
+ reg = (u32 *)get_property(np, "reg", NULL);
+ strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);
+
+ if (of_device_register(dev) != 0) {
+ kfree(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+EXPORT_SYMBOL(of_match_device);
+EXPORT_SYMBOL(of_platform_bus_type);
+EXPORT_SYMBOL(of_register_driver);
+EXPORT_SYMBOL(of_unregister_driver);
+EXPORT_SYMBOL(of_device_register);
+EXPORT_SYMBOL(of_device_unregister);
+EXPORT_SYMBOL(of_dev_get);
+EXPORT_SYMBOL(of_dev_put);
+EXPORT_SYMBOL(of_platform_device_create);
+EXPORT_SYMBOL(of_release_dev);
diff --git a/arch/ppc/syslib/open_pic.c b/arch/ppc/syslib/open_pic.c
new file mode 100644
index 000000000000..406f36a8a681
--- /dev/null
+++ b/arch/ppc/syslib/open_pic.c
@@ -0,0 +1,1083 @@
+/*
+ * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/sysdev.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/open_pic.h>
+#include <asm/i8259.h>
+
+#include "open_pic_defs.h"
+
+#if defined(CONFIG_PRPMC800) || defined(CONFIG_85xx)
+#define OPENPIC_BIG_ENDIAN
+#endif
+
+void __iomem *OpenPIC_Addr;
+static volatile struct OpenPIC __iomem *OpenPIC = NULL;
+
+/*
+ * We define OpenPIC_InitSenses table thusly:
+ * bit 0x1: sense, 0 for edge and 1 for level.
+ * bit 0x2: polarity, 0 for negative, 1 for positive.
+ */
+u_int OpenPIC_NumInitSenses __initdata = 0;
+u_char *OpenPIC_InitSenses __initdata = NULL;
+extern int use_of_interrupt_tree;
+
+static u_int NumProcessors;
+static u_int NumSources;
+static int open_pic_irq_offset;
+static volatile OpenPIC_Source __iomem *ISR[NR_IRQS];
+static int openpic_cascade_irq = -1;
+static int (*openpic_cascade_fn)(struct pt_regs *);
+
+/* Global Operations */
+static void openpic_disable_8259_pass_through(void);
+static void openpic_set_spurious(u_int vector);
+
+#ifdef CONFIG_SMP
+/* Interprocessor Interrupts */
+static void openpic_initipi(u_int ipi, u_int pri, u_int vector);
+static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *);
+#endif
+
+/* Timer Interrupts */
+static void openpic_inittimer(u_int timer, u_int pri, u_int vector);
+static void openpic_maptimer(u_int timer, cpumask_t cpumask);
+
+/* Interrupt Sources */
+static void openpic_enable_irq(u_int irq);
+static void openpic_disable_irq(u_int irq);
+static void openpic_initirq(u_int irq, u_int pri, u_int vector, int polarity,
+ int is_level);
+static void openpic_mapirq(u_int irq, cpumask_t cpumask, cpumask_t keepmask);
+
+/*
+ * These functions are not used but the code is kept here
+ * for completeness and future reference.
+ */
+#ifdef notused
+static void openpic_enable_8259_pass_through(void);
+static u_int openpic_get_priority(void);
+static u_int openpic_get_spurious(void);
+static void openpic_set_sense(u_int irq, int sense);
+#endif /* notused */
+
+/*
+ * Description of the openpic for the higher-level irq code
+ */
+static void openpic_end_irq(unsigned int irq_nr);
+static void openpic_ack_irq(unsigned int irq_nr);
+static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask);
+
+struct hw_interrupt_type open_pic = {
+ .typename = " OpenPIC ",
+ .enable = openpic_enable_irq,
+ .disable = openpic_disable_irq,
+ .ack = openpic_ack_irq,
+ .end = openpic_end_irq,
+ .set_affinity = openpic_set_affinity,
+};
+
+#ifdef CONFIG_SMP
+static void openpic_end_ipi(unsigned int irq_nr);
+static void openpic_ack_ipi(unsigned int irq_nr);
+static void openpic_enable_ipi(unsigned int irq_nr);
+static void openpic_disable_ipi(unsigned int irq_nr);
+
+struct hw_interrupt_type open_pic_ipi = {
+ .typename = " OpenPIC ",
+ .enable = openpic_enable_ipi,
+ .disable = openpic_disable_ipi,
+ .ack = openpic_ack_ipi,
+ .end = openpic_end_ipi,
+};
+#endif /* CONFIG_SMP */
+
+/*
+ * Accesses to the current processor's openpic registers
+ */
+#ifdef CONFIG_SMP
+#define THIS_CPU Processor[cpu]
+#define DECL_THIS_CPU int cpu = smp_hw_index[smp_processor_id()]
+#define CHECK_THIS_CPU check_arg_cpu(cpu)
+#else
+#define THIS_CPU Processor[0]
+#define DECL_THIS_CPU
+#define CHECK_THIS_CPU
+#endif /* CONFIG_SMP */
+
+#if 1
+#define check_arg_ipi(ipi) \
+ if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \
+ printk("open_pic.c:%d: invalid ipi %d\n", __LINE__, ipi);
+#define check_arg_timer(timer) \
+ if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \
+ printk("open_pic.c:%d: invalid timer %d\n", __LINE__, timer);
+#define check_arg_vec(vec) \
+ if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \
+ printk("open_pic.c:%d: invalid vector %d\n", __LINE__, vec);
+#define check_arg_pri(pri) \
+ if (pri < 0 || pri >= OPENPIC_NUM_PRI) \
+ printk("open_pic.c:%d: invalid priority %d\n", __LINE__, pri);
+/*
+ * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's
+ * data has probably been corrupted and we're going to panic or deadlock later
+ * anyway --Troy
+ */
+#define check_arg_irq(irq) \
+ if (irq < open_pic_irq_offset || irq >= NumSources+open_pic_irq_offset \
+ || ISR[irq - open_pic_irq_offset] == 0) { \
+ printk("open_pic.c:%d: invalid irq %d\n", __LINE__, irq); \
+ dump_stack(); }
+#define check_arg_cpu(cpu) \
+ if (cpu < 0 || cpu >= NumProcessors){ \
+ printk("open_pic.c:%d: invalid cpu %d\n", __LINE__, cpu); \
+ dump_stack(); }
+#else
+#define check_arg_ipi(ipi) do {} while (0)
+#define check_arg_timer(timer) do {} while (0)
+#define check_arg_vec(vec) do {} while (0)
+#define check_arg_pri(pri) do {} while (0)
+#define check_arg_irq(irq) do {} while (0)
+#define check_arg_cpu(cpu) do {} while (0)
+#endif
+
+u_int openpic_read(volatile u_int __iomem *addr)
+{
+ u_int val;
+
+#ifdef OPENPIC_BIG_ENDIAN
+ val = in_be32(addr);
+#else
+ val = in_le32(addr);
+#endif
+ return val;
+}
+
+static inline void openpic_write(volatile u_int __iomem *addr, u_int val)
+{
+#ifdef OPENPIC_BIG_ENDIAN
+ out_be32(addr, val);
+#else
+ out_le32(addr, val);
+#endif
+}
+
+static inline u_int openpic_readfield(volatile u_int __iomem *addr, u_int mask)
+{
+ u_int val = openpic_read(addr);
+ return val & mask;
+}
+
+inline void openpic_writefield(volatile u_int __iomem *addr, u_int mask,
+ u_int field)
+{
+ u_int val = openpic_read(addr);
+ openpic_write(addr, (val & ~mask) | (field & mask));
+}
+
+static inline void openpic_clearfield(volatile u_int __iomem *addr, u_int mask)
+{
+ openpic_writefield(addr, mask, 0);
+}
+
+static inline void openpic_setfield(volatile u_int __iomem *addr, u_int mask)
+{
+ openpic_writefield(addr, mask, mask);
+}
+
+static void openpic_safe_writefield(volatile u_int __iomem *addr, u_int mask,
+ u_int field)
+{
+ openpic_setfield(addr, OPENPIC_MASK);
+ while (openpic_read(addr) & OPENPIC_ACTIVITY);
+ openpic_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
+}
+
+#ifdef CONFIG_SMP
+/* yes this is right ... bug, feature, you decide! -- tgall */
+u_int openpic_read_IPI(volatile u_int __iomem * addr)
+{
+ u_int val = 0;
+#if defined(OPENPIC_BIG_ENDIAN) || defined(CONFIG_POWER3)
+ val = in_be32(addr);
+#else
+ val = in_le32(addr);
+#endif
+ return val;
+}
+
+/* because of the power3 be / le above, this is needed */
+inline void openpic_writefield_IPI(volatile u_int __iomem * addr, u_int mask, u_int field)
+{
+ u_int val = openpic_read_IPI(addr);
+ openpic_write(addr, (val & ~mask) | (field & mask));
+}
+
+static inline void openpic_clearfield_IPI(volatile u_int __iomem *addr, u_int mask)
+{
+ openpic_writefield_IPI(addr, mask, 0);
+}
+
+static inline void openpic_setfield_IPI(volatile u_int __iomem *addr, u_int mask)
+{
+ openpic_writefield_IPI(addr, mask, mask);
+}
+
+static void openpic_safe_writefield_IPI(volatile u_int __iomem *addr, u_int mask, u_int field)
+{
+ openpic_setfield_IPI(addr, OPENPIC_MASK);
+
+ /* wait until it's not in use */
+ /* BenH: Is this code really enough ? I would rather check the result
+ * and eventually retry ...
+ */
+ while(openpic_read_IPI(addr) & OPENPIC_ACTIVITY);
+
+ openpic_writefield_IPI(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
+}
+#endif /* CONFIG_SMP */
+
+#ifdef CONFIG_EPIC_SERIAL_MODE
+/* On platforms that may use EPIC serial mode, the default is enabled. */
+int epic_serial_mode = 1;
+
+static void __init openpic_eicr_set_clk(u_int clkval)
+{
+ openpic_writefield(&OpenPIC->Global.Global_Configuration1,
+ OPENPIC_EICR_S_CLK_MASK, (clkval << 28));
+}
+
+static void __init openpic_enable_sie(void)
+{
+ openpic_setfield(&OpenPIC->Global.Global_Configuration1,
+ OPENPIC_EICR_SIE);
+}
+#endif
+
+#if defined(CONFIG_EPIC_SERIAL_MODE) || defined(CONFIG_PM)
+static void openpic_reset(void)
+{
+ openpic_setfield(&OpenPIC->Global.Global_Configuration0,
+ OPENPIC_CONFIG_RESET);
+ while (openpic_readfield(&OpenPIC->Global.Global_Configuration0,
+ OPENPIC_CONFIG_RESET))
+ mb();
+}
+#endif
+
+void __init openpic_set_sources(int first_irq, int num_irqs, void __iomem *first_ISR)
+{
+ volatile OpenPIC_Source __iomem *src = first_ISR;
+ int i, last_irq;
+
+ last_irq = first_irq + num_irqs;
+ if (last_irq > NumSources)
+ NumSources = last_irq;
+ if (src == 0)
+ src = &((struct OpenPIC __iomem *)OpenPIC_Addr)->Source[first_irq];
+ for (i = first_irq; i < last_irq; ++i, ++src)
+ ISR[i] = src;
+}
+
+/*
+ * The `offset' parameter defines where the interrupts handled by the
+ * OpenPIC start in the space of interrupt numbers that the kernel knows
+ * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the
+ * kernel's interrupt numbering scheme.
+ * We assume there is only one OpenPIC.
+ */
+void __init openpic_init(int offset)
+{
+ u_int t, i;
+ u_int timerfreq;
+ const char *version;
+
+ if (!OpenPIC_Addr) {
+ printk("No OpenPIC found !\n");
+ return;
+ }
+ OpenPIC = (volatile struct OpenPIC __iomem *)OpenPIC_Addr;
+
+#ifdef CONFIG_EPIC_SERIAL_MODE
+ /* Have to start from ground zero.
+ */
+ openpic_reset();
+#endif
+
+ if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122);
+
+ t = openpic_read(&OpenPIC->Global.Feature_Reporting0);
+ switch (t & OPENPIC_FEATURE_VERSION_MASK) {
+ case 1:
+ version = "1.0";
+ break;
+ case 2:
+ version = "1.2";
+ break;
+ case 3:
+ version = "1.3";
+ break;
+ default:
+ version = "?";
+ break;
+ }
+ NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
+ OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1;
+ if (NumSources == 0)
+ openpic_set_sources(0,
+ ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
+ OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1,
+ NULL);
+ printk("OpenPIC Version %s (%d CPUs and %d IRQ sources) at %p\n",
+ version, NumProcessors, NumSources, OpenPIC);
+ timerfreq = openpic_read(&OpenPIC->Global.Timer_Frequency);
+ if (timerfreq)
+ printk("OpenPIC timer frequency is %d.%06d MHz\n",
+ timerfreq / 1000000, timerfreq % 1000000);
+
+ open_pic_irq_offset = offset;
+
+ /* Initialize timer interrupts */
+ if ( ppc_md.progress ) ppc_md.progress("openpic: timer",0x3ba);
+ for (i = 0; i < OPENPIC_NUM_TIMERS; i++) {
+ /* Disabled, Priority 0 */
+ openpic_inittimer(i, 0, OPENPIC_VEC_TIMER+i+offset);
+ /* No processor */
+ openpic_maptimer(i, CPU_MASK_NONE);
+ }
+
+#ifdef CONFIG_SMP
+ /* Initialize IPI interrupts */
+ if ( ppc_md.progress ) ppc_md.progress("openpic: ipi",0x3bb);
+ for (i = 0; i < OPENPIC_NUM_IPI; i++) {
+ /* Disabled, Priority 10..13 */
+ openpic_initipi(i, 10+i, OPENPIC_VEC_IPI+i+offset);
+ /* IPIs are per-CPU */
+ irq_desc[OPENPIC_VEC_IPI+i+offset].status |= IRQ_PER_CPU;
+ irq_desc[OPENPIC_VEC_IPI+i+offset].handler = &open_pic_ipi;
+ }
+#endif
+
+ /* Initialize external interrupts */
+ if (ppc_md.progress) ppc_md.progress("openpic: external",0x3bc);
+
+ openpic_set_priority(0xf);
+
+ /* Init all external sources, including possibly the cascade. */
+ for (i = 0; i < NumSources; i++) {
+ int sense;
+
+ if (ISR[i] == 0)
+ continue;
+
+ /* the bootloader may have left it enabled (bad !) */
+ openpic_disable_irq(i+offset);
+
+ sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE);
+
+ if (sense & IRQ_SENSE_MASK)
+ irq_desc[i+offset].status = IRQ_LEVEL;
+
+ /* Enabled, Priority 8 */
+ openpic_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK),
+ (sense & IRQ_SENSE_MASK));
+ /* Processor 0 */
+ openpic_mapirq(i, CPU_MASK_CPU0, CPU_MASK_NONE);
+ }
+
+ /* Init descriptors */
+ for (i = offset; i < NumSources + offset; i++)
+ irq_desc[i].handler = &open_pic;
+
+ /* Initialize the spurious interrupt */
+ if (ppc_md.progress) ppc_md.progress("openpic: spurious",0x3bd);
+ openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
+ openpic_disable_8259_pass_through();
+#ifdef CONFIG_EPIC_SERIAL_MODE
+ if (epic_serial_mode) {
+ openpic_eicr_set_clk(7); /* Slowest value until we know better */
+ openpic_enable_sie();
+ }
+#endif
+ openpic_set_priority(0);
+
+ if (ppc_md.progress) ppc_md.progress("openpic: exit",0x222);
+}
+
+#ifdef notused
+static void openpic_enable_8259_pass_through(void)
+{
+ openpic_clearfield(&OpenPIC->Global.Global_Configuration0,
+ OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
+}
+#endif /* notused */
+
+static void openpic_disable_8259_pass_through(void)
+{
+ openpic_setfield(&OpenPIC->Global.Global_Configuration0,
+ OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
+}
+
+/*
+ * Find out the current interrupt
+ */
+u_int openpic_irq(void)
+{
+ u_int vec;
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ vec = openpic_readfield(&OpenPIC->THIS_CPU.Interrupt_Acknowledge,
+ OPENPIC_VECTOR_MASK);
+ return vec;
+}
+
+void openpic_eoi(void)
+{
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ openpic_write(&OpenPIC->THIS_CPU.EOI, 0);
+ /* Handle PCI write posting */
+ (void)openpic_read(&OpenPIC->THIS_CPU.EOI);
+}
+
+#ifdef notused
+static u_int openpic_get_priority(void)
+{
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ return openpic_readfield(&OpenPIC->THIS_CPU.Current_Task_Priority,
+ OPENPIC_CURRENT_TASK_PRIORITY_MASK);
+}
+#endif /* notused */
+
+void openpic_set_priority(u_int pri)
+{
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ check_arg_pri(pri);
+ openpic_writefield(&OpenPIC->THIS_CPU.Current_Task_Priority,
+ OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
+}
+
+/*
+ * Get/set the spurious vector
+ */
+#ifdef notused
+static u_int openpic_get_spurious(void)
+{
+ return openpic_readfield(&OpenPIC->Global.Spurious_Vector,
+ OPENPIC_VECTOR_MASK);
+}
+#endif /* notused */
+
+static void openpic_set_spurious(u_int vec)
+{
+ check_arg_vec(vec);
+ openpic_writefield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
+ vec);
+}
+
+#ifdef CONFIG_SMP
+/*
+ * Convert a cpu mask from logical to physical cpu numbers.
+ */
+static inline cpumask_t physmask(cpumask_t cpumask)
+{
+ int i;
+ cpumask_t mask = CPU_MASK_NONE;
+
+ cpus_and(cpumask, cpu_online_map, cpumask);
+
+ for (i = 0; i < NR_CPUS; i++)
+ if (cpu_isset(i, cpumask))
+ cpu_set(smp_hw_index[i], mask);
+
+ return mask;
+}
+#else
+#define physmask(cpumask) (cpumask)
+#endif
+
+void openpic_reset_processor_phys(u_int mask)
+{
+ openpic_write(&OpenPIC->Global.Processor_Initialization, mask);
+}
+
+#if defined(CONFIG_SMP) || defined(CONFIG_PM)
+static DEFINE_SPINLOCK(openpic_setup_lock);
+#endif
+
+#ifdef CONFIG_SMP
+/*
+ * Initialize an interprocessor interrupt (and disable it)
+ *
+ * ipi: OpenPIC interprocessor interrupt number
+ * pri: interrupt source priority
+ * vec: the vector it will produce
+ */
+static void __init openpic_initipi(u_int ipi, u_int pri, u_int vec)
+{
+ check_arg_ipi(ipi);
+ check_arg_pri(pri);
+ check_arg_vec(vec);
+ openpic_safe_writefield_IPI(&OpenPIC->Global.IPI_Vector_Priority(ipi),
+ OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
+ (pri << OPENPIC_PRIORITY_SHIFT) | vec);
+}
+
+/*
+ * Send an IPI to one or more CPUs
+ *
+ * Externally called, however, it takes an IPI number (0...OPENPIC_NUM_IPI)
+ * and not a system-wide interrupt number
+ */
+void openpic_cause_IPI(u_int ipi, cpumask_t cpumask)
+{
+ cpumask_t phys;
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ check_arg_ipi(ipi);
+ phys = physmask(cpumask);
+ openpic_write(&OpenPIC->THIS_CPU.IPI_Dispatch(ipi),
+ cpus_addr(physmask(cpumask))[0]);
+}
+
+void openpic_request_IPIs(void)
+{
+ int i;
+
+ /*
+ * Make sure this matches what is defined in smp.c for
+ * smp_message_{pass|recv}() or what shows up in
+ * /proc/interrupts will be wrong!!! --Troy */
+
+ if (OpenPIC == NULL)
+ return;
+
+ /* IPIs are marked SA_INTERRUPT as they must run with irqs disabled */
+ request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset,
+ openpic_ipi_action, SA_INTERRUPT,
+ "IPI0 (call function)", NULL);
+ request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+1,
+ openpic_ipi_action, SA_INTERRUPT,
+ "IPI1 (reschedule)", NULL);
+ request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+2,
+ openpic_ipi_action, SA_INTERRUPT,
+ "IPI2 (invalidate tlb)", NULL);
+ request_irq(OPENPIC_VEC_IPI+open_pic_irq_offset+3,
+ openpic_ipi_action, SA_INTERRUPT,
+ "IPI3 (xmon break)", NULL);
+
+ for ( i = 0; i < OPENPIC_NUM_IPI ; i++ )
+ openpic_enable_ipi(OPENPIC_VEC_IPI+open_pic_irq_offset+i);
+}
+
+/*
+ * Do per-cpu setup for SMP systems.
+ *
+ * Get IPI's working and start taking interrupts.
+ * -- Cort
+ */
+
+void __devinit do_openpic_setup_cpu(void)
+{
+#ifdef CONFIG_IRQ_ALL_CPUS
+ int i;
+ cpumask_t msk = CPU_MASK_NONE;
+#endif
+ spin_lock(&openpic_setup_lock);
+
+#ifdef CONFIG_IRQ_ALL_CPUS
+ cpu_set(smp_hw_index[smp_processor_id()], msk);
+
+ /* let the openpic know we want intrs. default affinity
+ * is 0xffffffff until changed via /proc
+ * That's how it's done on x86. If we want it differently, then
+ * we should make sure we also change the default values of irq_affinity
+ * in irq.c.
+ */
+ for (i = 0; i < NumSources; i++)
+ openpic_mapirq(i, msk, CPU_MASK_ALL);
+#endif /* CONFIG_IRQ_ALL_CPUS */
+ openpic_set_priority(0);
+
+ spin_unlock(&openpic_setup_lock);
+}
+#endif /* CONFIG_SMP */
+
+/*
+ * Initialize a timer interrupt (and disable it)
+ *
+ * timer: OpenPIC timer number
+ * pri: interrupt source priority
+ * vec: the vector it will produce
+ */
+static void __init openpic_inittimer(u_int timer, u_int pri, u_int vec)
+{
+ check_arg_timer(timer);
+ check_arg_pri(pri);
+ check_arg_vec(vec);
+ openpic_safe_writefield(&OpenPIC->Global.Timer[timer].Vector_Priority,
+ OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
+ (pri << OPENPIC_PRIORITY_SHIFT) | vec);
+}
+
+/*
+ * Map a timer interrupt to one or more CPUs
+ */
+static void __init openpic_maptimer(u_int timer, cpumask_t cpumask)
+{
+ cpumask_t phys = physmask(cpumask);
+ check_arg_timer(timer);
+ openpic_write(&OpenPIC->Global.Timer[timer].Destination,
+ cpus_addr(phys)[0]);
+}
+
+/*
+ * Initalize the interrupt source which will generate an NMI.
+ * This raises the interrupt's priority from 8 to 9.
+ *
+ * irq: The logical IRQ which generates an NMI.
+ */
+void __init
+openpic_init_nmi_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ openpic_safe_writefield(&ISR[irq - open_pic_irq_offset]->Vector_Priority,
+ OPENPIC_PRIORITY_MASK,
+ 9 << OPENPIC_PRIORITY_SHIFT);
+}
+
+/*
+ *
+ * All functions below take an offset'ed irq argument
+ *
+ */
+
+/*
+ * Hookup a cascade to the OpenPIC.
+ */
+
+static struct irqaction openpic_cascade_irqaction = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .mask = CPU_MASK_NONE,
+};
+
+void __init
+openpic_hookup_cascade(u_int irq, char *name,
+ int (*cascade_fn)(struct pt_regs *))
+{
+ openpic_cascade_irq = irq;
+ openpic_cascade_fn = cascade_fn;
+
+ if (setup_irq(irq, &openpic_cascade_irqaction))
+ printk("Unable to get OpenPIC IRQ %d for cascade\n",
+ irq - open_pic_irq_offset);
+}
+
+/*
+ * Enable/disable an external interrupt source
+ *
+ * Externally called, irq is an offseted system-wide interrupt number
+ */
+static void openpic_enable_irq(u_int irq)
+{
+ volatile u_int __iomem *vpp;
+
+ check_arg_irq(irq);
+ vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority;
+ openpic_clearfield(vpp, OPENPIC_MASK);
+ /* make sure mask gets to controller before we return to user */
+ do {
+ mb(); /* sync is probably useless here */
+ } while (openpic_readfield(vpp, OPENPIC_MASK));
+}
+
+static void openpic_disable_irq(u_int irq)
+{
+ volatile u_int __iomem *vpp;
+ u32 vp;
+
+ check_arg_irq(irq);
+ vpp = &ISR[irq - open_pic_irq_offset]->Vector_Priority;
+ openpic_setfield(vpp, OPENPIC_MASK);
+ /* make sure mask gets to controller before we return to user */
+ do {
+ mb(); /* sync is probably useless here */
+ vp = openpic_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY);
+ } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK));
+}
+
+#ifdef CONFIG_SMP
+/*
+ * Enable/disable an IPI interrupt source
+ *
+ * Externally called, irq is an offseted system-wide interrupt number
+ */
+void openpic_enable_ipi(u_int irq)
+{
+ irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset);
+ check_arg_ipi(irq);
+ openpic_clearfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK);
+
+}
+
+void openpic_disable_ipi(u_int irq)
+{
+ irq -= (OPENPIC_VEC_IPI+open_pic_irq_offset);
+ check_arg_ipi(irq);
+ openpic_setfield_IPI(&OpenPIC->Global.IPI_Vector_Priority(irq), OPENPIC_MASK);
+}
+#endif
+
+/*
+ * Initialize an interrupt source (and disable it!)
+ *
+ * irq: OpenPIC interrupt number
+ * pri: interrupt source priority
+ * vec: the vector it will produce
+ * pol: polarity (1 for positive, 0 for negative)
+ * sense: 1 for level, 0 for edge
+ */
+static void __init
+openpic_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
+{
+ openpic_safe_writefield(&ISR[irq]->Vector_Priority,
+ OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
+ OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK,
+ (pri << OPENPIC_PRIORITY_SHIFT) | vec |
+ (pol ? OPENPIC_POLARITY_POSITIVE :
+ OPENPIC_POLARITY_NEGATIVE) |
+ (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE));
+}
+
+/*
+ * Map an interrupt source to one or more CPUs
+ */
+static void openpic_mapirq(u_int irq, cpumask_t physmask, cpumask_t keepmask)
+{
+ if (ISR[irq] == 0)
+ return;
+ if (!cpus_empty(keepmask)) {
+ cpumask_t irqdest = { .bits[0] = openpic_read(&ISR[irq]->Destination) };
+ cpus_and(irqdest, irqdest, keepmask);
+ cpus_or(physmask, physmask, irqdest);
+ }
+ openpic_write(&ISR[irq]->Destination, cpus_addr(physmask)[0]);
+}
+
+#ifdef notused
+/*
+ * Set the sense for an interrupt source (and disable it!)
+ *
+ * sense: 1 for level, 0 for edge
+ */
+static void openpic_set_sense(u_int irq, int sense)
+{
+ if (ISR[irq] != 0)
+ openpic_safe_writefield(&ISR[irq]->Vector_Priority,
+ OPENPIC_SENSE_LEVEL,
+ (sense ? OPENPIC_SENSE_LEVEL : 0));
+}
+#endif /* notused */
+
+/* No spinlocks, should not be necessary with the OpenPIC
+ * (1 register = 1 interrupt and we have the desc lock).
+ */
+static void openpic_ack_irq(unsigned int irq_nr)
+{
+#ifdef __SLOW_VERSION__
+ openpic_disable_irq(irq_nr);
+ openpic_eoi();
+#else
+ if ((irq_desc[irq_nr].status & IRQ_LEVEL) == 0)
+ openpic_eoi();
+#endif
+}
+
+static void openpic_end_irq(unsigned int irq_nr)
+{
+#ifdef __SLOW_VERSION__
+ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
+ && irq_desc[irq_nr].action)
+ openpic_enable_irq(irq_nr);
+#else
+ if ((irq_desc[irq_nr].status & IRQ_LEVEL) != 0)
+ openpic_eoi();
+#endif
+}
+
+static void openpic_set_affinity(unsigned int irq_nr, cpumask_t cpumask)
+{
+ openpic_mapirq(irq_nr - open_pic_irq_offset, physmask(cpumask), CPU_MASK_NONE);
+}
+
+#ifdef CONFIG_SMP
+static void openpic_ack_ipi(unsigned int irq_nr)
+{
+ openpic_eoi();
+}
+
+static void openpic_end_ipi(unsigned int irq_nr)
+{
+}
+
+static irqreturn_t openpic_ipi_action(int cpl, void *dev_id, struct pt_regs *regs)
+{
+ smp_message_recv(cpl-OPENPIC_VEC_IPI-open_pic_irq_offset, regs);
+ return IRQ_HANDLED;
+}
+
+#endif /* CONFIG_SMP */
+
+int
+openpic_get_irq(struct pt_regs *regs)
+{
+ int irq = openpic_irq();
+
+ /*
+ * Check for the cascade interrupt and call the cascaded
+ * interrupt controller function (usually i8259_irq) if so.
+ * This should move to irq.c eventually. -- paulus
+ */
+ if (irq == openpic_cascade_irq && openpic_cascade_fn != NULL) {
+ int cirq = openpic_cascade_fn(regs);
+
+ /* Allow for the cascade being shared with other devices */
+ if (cirq != -1) {
+ irq = cirq;
+ openpic_eoi();
+ }
+ } else if (irq == OPENPIC_VEC_SPURIOUS)
+ irq = -1;
+ return irq;
+}
+
+#ifdef CONFIG_SMP
+void
+smp_openpic_message_pass(int target, int msg, unsigned long data, int wait)
+{
+ cpumask_t mask = CPU_MASK_ALL;
+ /* make sure we're sending something that translates to an IPI */
+ if (msg > 0x3) {
+ printk("SMP %d: smp_message_pass: unknown msg %d\n",
+ smp_processor_id(), msg);
+ return;
+ }
+ switch (target) {
+ case MSG_ALL:
+ openpic_cause_IPI(msg, mask);
+ break;
+ case MSG_ALL_BUT_SELF:
+ cpu_clear(smp_processor_id(), mask);
+ openpic_cause_IPI(msg, mask);
+ break;
+ default:
+ openpic_cause_IPI(msg, cpumask_of_cpu(target));
+ break;
+ }
+}
+#endif /* CONFIG_SMP */
+
+#ifdef CONFIG_PM
+
+/*
+ * We implement the IRQ controller as a sysdev and put it
+ * to sleep at powerdown stage (the callback is named suspend,
+ * but it's old semantics, for the Device Model, it's really
+ * powerdown). The possible problem is that another sysdev that
+ * happens to be suspend after this one will have interrupts off,
+ * that may be an issue... For now, this isn't an issue on pmac
+ * though...
+ */
+
+static u32 save_ipi_vp[OPENPIC_NUM_IPI];
+static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES];
+static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES];
+static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS];
+static int openpic_suspend_count;
+
+static void openpic_cached_enable_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ save_irq_src_vp[irq - open_pic_irq_offset] &= ~OPENPIC_MASK;
+}
+
+static void openpic_cached_disable_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ save_irq_src_vp[irq - open_pic_irq_offset] |= OPENPIC_MASK;
+}
+
+/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
+ * we need something better to deal with that... Maybe switch to S1 for
+ * cpufreq changes
+ */
+int openpic_suspend(struct sys_device *sysdev, u32 state)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&openpic_setup_lock, flags);
+
+ if (openpic_suspend_count++ > 0) {
+ spin_unlock_irqrestore(&openpic_setup_lock, flags);
+ return 0;
+ }
+
+ openpic_set_priority(0xf);
+
+ open_pic.enable = openpic_cached_enable_irq;
+ open_pic.disable = openpic_cached_disable_irq;
+
+ for (i=0; i<NumProcessors; i++) {
+ save_cpu_task_pri[i] = openpic_read(&OpenPIC->Processor[i].Current_Task_Priority);
+ openpic_writefield(&OpenPIC->Processor[i].Current_Task_Priority,
+ OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf);
+ }
+
+ for (i=0; i<OPENPIC_NUM_IPI; i++)
+ save_ipi_vp[i] = openpic_read(&OpenPIC->Global.IPI_Vector_Priority(i));
+ for (i=0; i<NumSources; i++) {
+ if (ISR[i] == 0)
+ continue;
+ save_irq_src_vp[i] = openpic_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY;
+ save_irq_src_dest[i] = openpic_read(&ISR[i]->Destination);
+ }
+
+ spin_unlock_irqrestore(&openpic_setup_lock, flags);
+
+ return 0;
+}
+
+/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
+ * we need something better to deal with that... Maybe switch to S1 for
+ * cpufreq changes
+ */
+int openpic_resume(struct sys_device *sysdev)
+{
+ int i;
+ unsigned long flags;
+ u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
+ OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK |
+ OPENPIC_MASK;
+
+ spin_lock_irqsave(&openpic_setup_lock, flags);
+
+ if ((--openpic_suspend_count) > 0) {
+ spin_unlock_irqrestore(&openpic_setup_lock, flags);
+ return 0;
+ }
+
+ openpic_reset();
+
+ /* OpenPIC sometimes seem to need some time to be fully back up... */
+ do {
+ openpic_set_spurious(OPENPIC_VEC_SPURIOUS);
+ } while(openpic_readfield(&OpenPIC->Global.Spurious_Vector, OPENPIC_VECTOR_MASK)
+ != OPENPIC_VEC_SPURIOUS);
+
+ openpic_disable_8259_pass_through();
+
+ for (i=0; i<OPENPIC_NUM_IPI; i++)
+ openpic_write(&OpenPIC->Global.IPI_Vector_Priority(i),
+ save_ipi_vp[i]);
+ for (i=0; i<NumSources; i++) {
+ if (ISR[i] == 0)
+ continue;
+ openpic_write(&ISR[i]->Destination, save_irq_src_dest[i]);
+ openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
+ /* make sure mask gets to controller before we return to user */
+ do {
+ openpic_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
+ } while (openpic_readfield(&ISR[i]->Vector_Priority, vppmask)
+ != (save_irq_src_vp[i] & vppmask));
+ }
+ for (i=0; i<NumProcessors; i++)
+ openpic_write(&OpenPIC->Processor[i].Current_Task_Priority,
+ save_cpu_task_pri[i]);
+
+ open_pic.enable = openpic_enable_irq;
+ open_pic.disable = openpic_disable_irq;
+
+ openpic_set_priority(0);
+
+ spin_unlock_irqrestore(&openpic_setup_lock, flags);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static struct sysdev_class openpic_sysclass = {
+ set_kset_name("openpic"),
+};
+
+static struct sys_device device_openpic = {
+ .id = 0,
+ .cls = &openpic_sysclass,
+};
+
+static struct sysdev_driver driver_openpic = {
+#ifdef CONFIG_PM
+ .suspend = &openpic_suspend,
+ .resume = &openpic_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init init_openpic_sysfs(void)
+{
+ int rc;
+
+ if (!OpenPIC_Addr)
+ return -ENODEV;
+ printk(KERN_DEBUG "Registering openpic with sysfs...\n");
+ rc = sysdev_class_register(&openpic_sysclass);
+ if (rc) {
+ printk(KERN_ERR "Failed registering openpic sys class\n");
+ return -ENODEV;
+ }
+ rc = sysdev_register(&device_openpic);
+ if (rc) {
+ printk(KERN_ERR "Failed registering openpic sys device\n");
+ return -ENODEV;
+ }
+ rc = sysdev_driver_register(&openpic_sysclass, &driver_openpic);
+ if (rc) {
+ printk(KERN_ERR "Failed registering openpic sys driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+subsys_initcall(init_openpic_sysfs);
+
diff --git a/arch/ppc/syslib/open_pic2.c b/arch/ppc/syslib/open_pic2.c
new file mode 100644
index 000000000000..ea26da0d8b6b
--- /dev/null
+++ b/arch/ppc/syslib/open_pic2.c
@@ -0,0 +1,716 @@
+/*
+ * arch/ppc/kernel/open_pic.c -- OpenPIC Interrupt Handling
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ *
+ * This is a duplicate of open_pic.c that deals with U3s MPIC on
+ * G5 PowerMacs. It's the same file except it's using big endian
+ * register accesses
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/sysdev.h>
+#include <linux/errno.h>
+#include <asm/ptrace.h>
+#include <asm/signal.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/open_pic.h>
+#include <asm/i8259.h>
+
+#include "open_pic_defs.h"
+
+void *OpenPIC2_Addr;
+static volatile struct OpenPIC *OpenPIC2 = NULL;
+/*
+ * We define OpenPIC_InitSenses table thusly:
+ * bit 0x1: sense, 0 for edge and 1 for level.
+ * bit 0x2: polarity, 0 for negative, 1 for positive.
+ */
+extern u_int OpenPIC_NumInitSenses;
+extern u_char *OpenPIC_InitSenses;
+extern int use_of_interrupt_tree;
+
+static u_int NumProcessors;
+static u_int NumSources;
+static int open_pic2_irq_offset;
+static volatile OpenPIC_Source *ISR[NR_IRQS];
+
+/* Global Operations */
+static void openpic2_disable_8259_pass_through(void);
+static void openpic2_set_priority(u_int pri);
+static void openpic2_set_spurious(u_int vector);
+
+/* Timer Interrupts */
+static void openpic2_inittimer(u_int timer, u_int pri, u_int vector);
+static void openpic2_maptimer(u_int timer, u_int cpumask);
+
+/* Interrupt Sources */
+static void openpic2_enable_irq(u_int irq);
+static void openpic2_disable_irq(u_int irq);
+static void openpic2_initirq(u_int irq, u_int pri, u_int vector, int polarity,
+ int is_level);
+static void openpic2_mapirq(u_int irq, u_int cpumask, u_int keepmask);
+
+/*
+ * These functions are not used but the code is kept here
+ * for completeness and future reference.
+ */
+static void openpic2_reset(void);
+#ifdef notused
+static void openpic2_enable_8259_pass_through(void);
+static u_int openpic2_get_priority(void);
+static u_int openpic2_get_spurious(void);
+static void openpic2_set_sense(u_int irq, int sense);
+#endif /* notused */
+
+/*
+ * Description of the openpic for the higher-level irq code
+ */
+static void openpic2_end_irq(unsigned int irq_nr);
+static void openpic2_ack_irq(unsigned int irq_nr);
+
+struct hw_interrupt_type open_pic2 = {
+ " OpenPIC2 ",
+ NULL,
+ NULL,
+ openpic2_enable_irq,
+ openpic2_disable_irq,
+ openpic2_ack_irq,
+ openpic2_end_irq,
+};
+
+/*
+ * Accesses to the current processor's openpic registers
+ * On cascaded controller, this is only CPU 0
+ */
+#define THIS_CPU Processor[0]
+#define DECL_THIS_CPU
+#define CHECK_THIS_CPU
+
+#if 1
+#define check_arg_ipi(ipi) \
+ if (ipi < 0 || ipi >= OPENPIC_NUM_IPI) \
+ printk("open_pic.c:%d: illegal ipi %d\n", __LINE__, ipi);
+#define check_arg_timer(timer) \
+ if (timer < 0 || timer >= OPENPIC_NUM_TIMERS) \
+ printk("open_pic.c:%d: illegal timer %d\n", __LINE__, timer);
+#define check_arg_vec(vec) \
+ if (vec < 0 || vec >= OPENPIC_NUM_VECTORS) \
+ printk("open_pic.c:%d: illegal vector %d\n", __LINE__, vec);
+#define check_arg_pri(pri) \
+ if (pri < 0 || pri >= OPENPIC_NUM_PRI) \
+ printk("open_pic.c:%d: illegal priority %d\n", __LINE__, pri);
+/*
+ * Print out a backtrace if it's out of range, since if it's larger than NR_IRQ's
+ * data has probably been corrupted and we're going to panic or deadlock later
+ * anyway --Troy
+ */
+extern unsigned long* _get_SP(void);
+#define check_arg_irq(irq) \
+ if (irq < open_pic2_irq_offset || irq >= NumSources+open_pic2_irq_offset \
+ || ISR[irq - open_pic2_irq_offset] == 0) { \
+ printk("open_pic.c:%d: illegal irq %d\n", __LINE__, irq); \
+ /*print_backtrace(_get_SP());*/ }
+#define check_arg_cpu(cpu) \
+ if (cpu < 0 || cpu >= NumProcessors){ \
+ printk("open_pic2.c:%d: illegal cpu %d\n", __LINE__, cpu); \
+ /*print_backtrace(_get_SP());*/ }
+#else
+#define check_arg_ipi(ipi) do {} while (0)
+#define check_arg_timer(timer) do {} while (0)
+#define check_arg_vec(vec) do {} while (0)
+#define check_arg_pri(pri) do {} while (0)
+#define check_arg_irq(irq) do {} while (0)
+#define check_arg_cpu(cpu) do {} while (0)
+#endif
+
+static u_int openpic2_read(volatile u_int *addr)
+{
+ u_int val;
+
+ val = in_be32(addr);
+ return val;
+}
+
+static inline void openpic2_write(volatile u_int *addr, u_int val)
+{
+ out_be32(addr, val);
+}
+
+static inline u_int openpic2_readfield(volatile u_int *addr, u_int mask)
+{
+ u_int val = openpic2_read(addr);
+ return val & mask;
+}
+
+inline void openpic2_writefield(volatile u_int *addr, u_int mask,
+ u_int field)
+{
+ u_int val = openpic2_read(addr);
+ openpic2_write(addr, (val & ~mask) | (field & mask));
+}
+
+static inline void openpic2_clearfield(volatile u_int *addr, u_int mask)
+{
+ openpic2_writefield(addr, mask, 0);
+}
+
+static inline void openpic2_setfield(volatile u_int *addr, u_int mask)
+{
+ openpic2_writefield(addr, mask, mask);
+}
+
+static void openpic2_safe_writefield(volatile u_int *addr, u_int mask,
+ u_int field)
+{
+ openpic2_setfield(addr, OPENPIC_MASK);
+ while (openpic2_read(addr) & OPENPIC_ACTIVITY);
+ openpic2_writefield(addr, mask | OPENPIC_MASK, field | OPENPIC_MASK);
+}
+
+static void openpic2_reset(void)
+{
+ openpic2_setfield(&OpenPIC2->Global.Global_Configuration0,
+ OPENPIC_CONFIG_RESET);
+ while (openpic2_readfield(&OpenPIC2->Global.Global_Configuration0,
+ OPENPIC_CONFIG_RESET))
+ mb();
+}
+
+void __init openpic2_set_sources(int first_irq, int num_irqs, void *first_ISR)
+{
+ volatile OpenPIC_Source *src = first_ISR;
+ int i, last_irq;
+
+ last_irq = first_irq + num_irqs;
+ if (last_irq > NumSources)
+ NumSources = last_irq;
+ if (src == 0)
+ src = &((struct OpenPIC *)OpenPIC2_Addr)->Source[first_irq];
+ for (i = first_irq; i < last_irq; ++i, ++src)
+ ISR[i] = src;
+}
+
+/*
+ * The `offset' parameter defines where the interrupts handled by the
+ * OpenPIC start in the space of interrupt numbers that the kernel knows
+ * about. In other words, the OpenPIC's IRQ0 is numbered `offset' in the
+ * kernel's interrupt numbering scheme.
+ * We assume there is only one OpenPIC.
+ */
+void __init openpic2_init(int offset)
+{
+ u_int t, i;
+ u_int timerfreq;
+ const char *version;
+
+ if (!OpenPIC2_Addr) {
+ printk("No OpenPIC2 found !\n");
+ return;
+ }
+ OpenPIC2 = (volatile struct OpenPIC *)OpenPIC2_Addr;
+
+ if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122);
+
+ t = openpic2_read(&OpenPIC2->Global.Feature_Reporting0);
+ switch (t & OPENPIC_FEATURE_VERSION_MASK) {
+ case 1:
+ version = "1.0";
+ break;
+ case 2:
+ version = "1.2";
+ break;
+ case 3:
+ version = "1.3";
+ break;
+ default:
+ version = "?";
+ break;
+ }
+ NumProcessors = ((t & OPENPIC_FEATURE_LAST_PROCESSOR_MASK) >>
+ OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT) + 1;
+ if (NumSources == 0)
+ openpic2_set_sources(0,
+ ((t & OPENPIC_FEATURE_LAST_SOURCE_MASK) >>
+ OPENPIC_FEATURE_LAST_SOURCE_SHIFT) + 1,
+ NULL);
+ printk("OpenPIC (2) Version %s (%d CPUs and %d IRQ sources) at %p\n",
+ version, NumProcessors, NumSources, OpenPIC2);
+ timerfreq = openpic2_read(&OpenPIC2->Global.Timer_Frequency);
+ if (timerfreq)
+ printk("OpenPIC timer frequency is %d.%06d MHz\n",
+ timerfreq / 1000000, timerfreq % 1000000);
+
+ open_pic2_irq_offset = offset;
+
+ /* Initialize timer interrupts */
+ if ( ppc_md.progress ) ppc_md.progress("openpic2: timer",0x3ba);
+ for (i = 0; i < OPENPIC_NUM_TIMERS; i++) {
+ /* Disabled, Priority 0 */
+ openpic2_inittimer(i, 0, OPENPIC2_VEC_TIMER+i+offset);
+ /* No processor */
+ openpic2_maptimer(i, 0);
+ }
+
+ /* Initialize external interrupts */
+ if (ppc_md.progress) ppc_md.progress("openpic2: external",0x3bc);
+
+ openpic2_set_priority(0xf);
+
+ /* Init all external sources, including possibly the cascade. */
+ for (i = 0; i < NumSources; i++) {
+ int sense;
+
+ if (ISR[i] == 0)
+ continue;
+
+ /* the bootloader may have left it enabled (bad !) */
+ openpic2_disable_irq(i+offset);
+
+ sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \
+ (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE);
+
+ if (sense & IRQ_SENSE_MASK)
+ irq_desc[i+offset].status = IRQ_LEVEL;
+
+ /* Enabled, Priority 8 */
+ openpic2_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK),
+ (sense & IRQ_SENSE_MASK));
+ /* Processor 0 */
+ openpic2_mapirq(i, 1<<0, 0);
+ }
+
+ /* Init descriptors */
+ for (i = offset; i < NumSources + offset; i++)
+ irq_desc[i].handler = &open_pic2;
+
+ /* Initialize the spurious interrupt */
+ if (ppc_md.progress) ppc_md.progress("openpic2: spurious",0x3bd);
+ openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+offset);
+
+ openpic2_disable_8259_pass_through();
+ openpic2_set_priority(0);
+
+ if (ppc_md.progress) ppc_md.progress("openpic2: exit",0x222);
+}
+
+#ifdef notused
+static void openpic2_enable_8259_pass_through(void)
+{
+ openpic2_clearfield(&OpenPIC2->Global.Global_Configuration0,
+ OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
+}
+#endif /* notused */
+
+/* This can't be __init, it is used in openpic_sleep_restore_intrs */
+static void openpic2_disable_8259_pass_through(void)
+{
+ openpic2_setfield(&OpenPIC2->Global.Global_Configuration0,
+ OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE);
+}
+
+/*
+ * Find out the current interrupt
+ */
+u_int openpic2_irq(void)
+{
+ u_int vec;
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ vec = openpic2_readfield(&OpenPIC2->THIS_CPU.Interrupt_Acknowledge,
+ OPENPIC_VECTOR_MASK);
+ return vec;
+}
+
+void openpic2_eoi(void)
+{
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ openpic2_write(&OpenPIC2->THIS_CPU.EOI, 0);
+ /* Handle PCI write posting */
+ (void)openpic2_read(&OpenPIC2->THIS_CPU.EOI);
+}
+
+#ifdef notused
+static u_int openpic2_get_priority(void)
+{
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ return openpic2_readfield(&OpenPIC2->THIS_CPU.Current_Task_Priority,
+ OPENPIC_CURRENT_TASK_PRIORITY_MASK);
+}
+#endif /* notused */
+
+static void __init openpic2_set_priority(u_int pri)
+{
+ DECL_THIS_CPU;
+
+ CHECK_THIS_CPU;
+ check_arg_pri(pri);
+ openpic2_writefield(&OpenPIC2->THIS_CPU.Current_Task_Priority,
+ OPENPIC_CURRENT_TASK_PRIORITY_MASK, pri);
+}
+
+/*
+ * Get/set the spurious vector
+ */
+#ifdef notused
+static u_int openpic2_get_spurious(void)
+{
+ return openpic2_readfield(&OpenPIC2->Global.Spurious_Vector,
+ OPENPIC_VECTOR_MASK);
+}
+#endif /* notused */
+
+/* This can't be __init, it is used in openpic_sleep_restore_intrs */
+static void openpic2_set_spurious(u_int vec)
+{
+ check_arg_vec(vec);
+ openpic2_writefield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK,
+ vec);
+}
+
+static DEFINE_SPINLOCK(openpic2_setup_lock);
+
+/*
+ * Initialize a timer interrupt (and disable it)
+ *
+ * timer: OpenPIC timer number
+ * pri: interrupt source priority
+ * vec: the vector it will produce
+ */
+static void __init openpic2_inittimer(u_int timer, u_int pri, u_int vec)
+{
+ check_arg_timer(timer);
+ check_arg_pri(pri);
+ check_arg_vec(vec);
+ openpic2_safe_writefield(&OpenPIC2->Global.Timer[timer].Vector_Priority,
+ OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK,
+ (pri << OPENPIC_PRIORITY_SHIFT) | vec);
+}
+
+/*
+ * Map a timer interrupt to one or more CPUs
+ */
+static void __init openpic2_maptimer(u_int timer, u_int cpumask)
+{
+ check_arg_timer(timer);
+ openpic2_write(&OpenPIC2->Global.Timer[timer].Destination,
+ cpumask);
+}
+
+/*
+ * Initalize the interrupt source which will generate an NMI.
+ * This raises the interrupt's priority from 8 to 9.
+ *
+ * irq: The logical IRQ which generates an NMI.
+ */
+void __init
+openpic2_init_nmi_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ openpic2_safe_writefield(&ISR[irq - open_pic2_irq_offset]->Vector_Priority,
+ OPENPIC_PRIORITY_MASK,
+ 9 << OPENPIC_PRIORITY_SHIFT);
+}
+
+/*
+ *
+ * All functions below take an offset'ed irq argument
+ *
+ */
+
+
+/*
+ * Enable/disable an external interrupt source
+ *
+ * Externally called, irq is an offseted system-wide interrupt number
+ */
+static void openpic2_enable_irq(u_int irq)
+{
+ volatile u_int *vpp;
+
+ check_arg_irq(irq);
+ vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority;
+ openpic2_clearfield(vpp, OPENPIC_MASK);
+ /* make sure mask gets to controller before we return to user */
+ do {
+ mb(); /* sync is probably useless here */
+ } while (openpic2_readfield(vpp, OPENPIC_MASK));
+}
+
+static void openpic2_disable_irq(u_int irq)
+{
+ volatile u_int *vpp;
+ u32 vp;
+
+ check_arg_irq(irq);
+ vpp = &ISR[irq - open_pic2_irq_offset]->Vector_Priority;
+ openpic2_setfield(vpp, OPENPIC_MASK);
+ /* make sure mask gets to controller before we return to user */
+ do {
+ mb(); /* sync is probably useless here */
+ vp = openpic2_readfield(vpp, OPENPIC_MASK | OPENPIC_ACTIVITY);
+ } while((vp & OPENPIC_ACTIVITY) && !(vp & OPENPIC_MASK));
+}
+
+
+/*
+ * Initialize an interrupt source (and disable it!)
+ *
+ * irq: OpenPIC interrupt number
+ * pri: interrupt source priority
+ * vec: the vector it will produce
+ * pol: polarity (1 for positive, 0 for negative)
+ * sense: 1 for level, 0 for edge
+ */
+static void __init
+openpic2_initirq(u_int irq, u_int pri, u_int vec, int pol, int sense)
+{
+ openpic2_safe_writefield(&ISR[irq]->Vector_Priority,
+ OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
+ OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK,
+ (pri << OPENPIC_PRIORITY_SHIFT) | vec |
+ (pol ? OPENPIC_POLARITY_POSITIVE :
+ OPENPIC_POLARITY_NEGATIVE) |
+ (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE));
+}
+
+/*
+ * Map an interrupt source to one or more CPUs
+ */
+static void openpic2_mapirq(u_int irq, u_int physmask, u_int keepmask)
+{
+ if (ISR[irq] == 0)
+ return;
+ if (keepmask != 0)
+ physmask |= openpic2_read(&ISR[irq]->Destination) & keepmask;
+ openpic2_write(&ISR[irq]->Destination, physmask);
+}
+
+#ifdef notused
+/*
+ * Set the sense for an interrupt source (and disable it!)
+ *
+ * sense: 1 for level, 0 for edge
+ */
+static void openpic2_set_sense(u_int irq, int sense)
+{
+ if (ISR[irq] != 0)
+ openpic2_safe_writefield(&ISR[irq]->Vector_Priority,
+ OPENPIC_SENSE_LEVEL,
+ (sense ? OPENPIC_SENSE_LEVEL : 0));
+}
+#endif /* notused */
+
+/* No spinlocks, should not be necessary with the OpenPIC
+ * (1 register = 1 interrupt and we have the desc lock).
+ */
+static void openpic2_ack_irq(unsigned int irq_nr)
+{
+ openpic2_disable_irq(irq_nr);
+ openpic2_eoi();
+}
+
+static void openpic2_end_irq(unsigned int irq_nr)
+{
+ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ openpic2_enable_irq(irq_nr);
+}
+
+int
+openpic2_get_irq(struct pt_regs *regs)
+{
+ int irq = openpic2_irq();
+
+ if (irq == (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset))
+ irq = -1;
+ return irq;
+}
+
+#ifdef CONFIG_PM
+
+/*
+ * We implement the IRQ controller as a sysdev and put it
+ * to sleep at powerdown stage (the callback is named suspend,
+ * but it's old semantics, for the Device Model, it's really
+ * powerdown). The possible problem is that another sysdev that
+ * happens to be suspend after this one will have interrupts off,
+ * that may be an issue... For now, this isn't an issue on pmac
+ * though...
+ */
+
+static u32 save_ipi_vp[OPENPIC_NUM_IPI];
+static u32 save_irq_src_vp[OPENPIC_MAX_SOURCES];
+static u32 save_irq_src_dest[OPENPIC_MAX_SOURCES];
+static u32 save_cpu_task_pri[OPENPIC_MAX_PROCESSORS];
+static int openpic_suspend_count;
+
+static void openpic2_cached_enable_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ save_irq_src_vp[irq - open_pic2_irq_offset] &= ~OPENPIC_MASK;
+}
+
+static void openpic2_cached_disable_irq(u_int irq)
+{
+ check_arg_irq(irq);
+ save_irq_src_vp[irq - open_pic2_irq_offset] |= OPENPIC_MASK;
+}
+
+/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
+ * we need something better to deal with that... Maybe switch to S1 for
+ * cpufreq changes
+ */
+int openpic2_suspend(struct sys_device *sysdev, u32 state)
+{
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&openpic2_setup_lock, flags);
+
+ if (openpic_suspend_count++ > 0) {
+ spin_unlock_irqrestore(&openpic2_setup_lock, flags);
+ return 0;
+ }
+
+ open_pic2.enable = openpic2_cached_enable_irq;
+ open_pic2.disable = openpic2_cached_disable_irq;
+
+ for (i=0; i<NumProcessors; i++) {
+ save_cpu_task_pri[i] = openpic2_read(&OpenPIC2->Processor[i].Current_Task_Priority);
+ openpic2_writefield(&OpenPIC2->Processor[i].Current_Task_Priority,
+ OPENPIC_CURRENT_TASK_PRIORITY_MASK, 0xf);
+ }
+
+ for (i=0; i<OPENPIC_NUM_IPI; i++)
+ save_ipi_vp[i] = openpic2_read(&OpenPIC2->Global.IPI_Vector_Priority(i));
+ for (i=0; i<NumSources; i++) {
+ if (ISR[i] == 0)
+ continue;
+ save_irq_src_vp[i] = openpic2_read(&ISR[i]->Vector_Priority) & ~OPENPIC_ACTIVITY;
+ save_irq_src_dest[i] = openpic2_read(&ISR[i]->Destination);
+ }
+
+ spin_unlock_irqrestore(&openpic2_setup_lock, flags);
+
+ return 0;
+}
+
+/* WARNING: Can be called directly by the cpufreq code with NULL parameter,
+ * we need something better to deal with that... Maybe switch to S1 for
+ * cpufreq changes
+ */
+int openpic2_resume(struct sys_device *sysdev)
+{
+ int i;
+ unsigned long flags;
+ u32 vppmask = OPENPIC_PRIORITY_MASK | OPENPIC_VECTOR_MASK |
+ OPENPIC_SENSE_MASK | OPENPIC_POLARITY_MASK |
+ OPENPIC_MASK;
+
+ spin_lock_irqsave(&openpic2_setup_lock, flags);
+
+ if ((--openpic_suspend_count) > 0) {
+ spin_unlock_irqrestore(&openpic2_setup_lock, flags);
+ return 0;
+ }
+
+ openpic2_reset();
+
+ /* OpenPIC sometimes seem to need some time to be fully back up... */
+ do {
+ openpic2_set_spurious(OPENPIC2_VEC_SPURIOUS+open_pic2_irq_offset);
+ } while(openpic2_readfield(&OpenPIC2->Global.Spurious_Vector, OPENPIC_VECTOR_MASK)
+ != (OPENPIC2_VEC_SPURIOUS + open_pic2_irq_offset));
+
+ openpic2_disable_8259_pass_through();
+
+ for (i=0; i<OPENPIC_NUM_IPI; i++)
+ openpic2_write(&OpenPIC2->Global.IPI_Vector_Priority(i),
+ save_ipi_vp[i]);
+ for (i=0; i<NumSources; i++) {
+ if (ISR[i] == 0)
+ continue;
+ openpic2_write(&ISR[i]->Destination, save_irq_src_dest[i]);
+ openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
+ /* make sure mask gets to controller before we return to user */
+ do {
+ openpic2_write(&ISR[i]->Vector_Priority, save_irq_src_vp[i]);
+ } while (openpic2_readfield(&ISR[i]->Vector_Priority, vppmask)
+ != (save_irq_src_vp[i] & vppmask));
+ }
+ for (i=0; i<NumProcessors; i++)
+ openpic2_write(&OpenPIC2->Processor[i].Current_Task_Priority,
+ save_cpu_task_pri[i]);
+
+ open_pic2.enable = openpic2_enable_irq;
+ open_pic2.disable = openpic2_disable_irq;
+
+ spin_unlock_irqrestore(&openpic2_setup_lock, flags);
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+/* HACK ALERT */
+static struct sysdev_class openpic2_sysclass = {
+ set_kset_name("openpic2"),
+};
+
+static struct sys_device device_openpic2 = {
+ .id = 0,
+ .cls = &openpic2_sysclass,
+};
+
+static struct sysdev_driver driver_openpic2 = {
+#ifdef CONFIG_PM
+ .suspend = &openpic2_suspend,
+ .resume = &openpic2_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init init_openpic2_sysfs(void)
+{
+ int rc;
+
+ if (!OpenPIC2_Addr)
+ return -ENODEV;
+ printk(KERN_DEBUG "Registering openpic2 with sysfs...\n");
+ rc = sysdev_class_register(&openpic2_sysclass);
+ if (rc) {
+ printk(KERN_ERR "Failed registering openpic sys class\n");
+ return -ENODEV;
+ }
+ rc = sysdev_register(&device_openpic2);
+ if (rc) {
+ printk(KERN_ERR "Failed registering openpic sys device\n");
+ return -ENODEV;
+ }
+ rc = sysdev_driver_register(&openpic2_sysclass, &driver_openpic2);
+ if (rc) {
+ printk(KERN_ERR "Failed registering openpic sys driver\n");
+ return -ENODEV;
+ }
+ return 0;
+}
+
+subsys_initcall(init_openpic2_sysfs);
+
diff --git a/arch/ppc/syslib/open_pic_defs.h b/arch/ppc/syslib/open_pic_defs.h
new file mode 100644
index 000000000000..4f05624b249e
--- /dev/null
+++ b/arch/ppc/syslib/open_pic_defs.h
@@ -0,0 +1,292 @@
+/*
+ * arch/ppc/kernel/open_pic_defs.h -- OpenPIC definitions
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * This file is based on the following documentation:
+ *
+ * The Open Programmable Interrupt Controller (PIC)
+ * Register Interface Specification Revision 1.2
+ *
+ * Issue Date: October 1995
+ *
+ * Issued jointly by Advanced Micro Devices and Cyrix Corporation
+ *
+ * AMD is a registered trademark of Advanced Micro Devices, Inc.
+ * Copyright (C) 1995, Advanced Micro Devices, Inc. and Cyrix, Inc.
+ * All Rights Reserved.
+ *
+ * To receive a copy of this documentation, send an email to openpic@amd.com.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#ifndef _LINUX_OPENPIC_H
+#define _LINUX_OPENPIC_H
+
+#ifdef __KERNEL__
+
+ /*
+ * OpenPIC supports up to 2048 interrupt sources and up to 32 processors
+ */
+
+#define OPENPIC_MAX_SOURCES 2048
+#define OPENPIC_MAX_PROCESSORS 32
+#define OPENPIC_MAX_ISU 16
+
+#define OPENPIC_NUM_TIMERS 4
+#define OPENPIC_NUM_IPI 4
+#define OPENPIC_NUM_PRI 16
+#define OPENPIC_NUM_VECTORS 256
+
+
+
+ /*
+ * OpenPIC Registers are 32 bits and aligned on 128 bit boundaries
+ */
+
+typedef struct _OpenPIC_Reg {
+ u_int Reg; /* Little endian! */
+ char Pad[0xc];
+} OpenPIC_Reg;
+
+
+ /*
+ * Per Processor Registers
+ */
+
+typedef struct _OpenPIC_Processor {
+ /*
+ * Private Shadow Registers (for SLiC backwards compatibility)
+ */
+ u_int IPI0_Dispatch_Shadow; /* Write Only */
+ char Pad1[0x4];
+ u_int IPI0_Vector_Priority_Shadow; /* Read/Write */
+ char Pad2[0x34];
+ /*
+ * Interprocessor Interrupt Command Ports
+ */
+ OpenPIC_Reg _IPI_Dispatch[OPENPIC_NUM_IPI]; /* Write Only */
+ /*
+ * Current Task Priority Register
+ */
+ OpenPIC_Reg _Current_Task_Priority; /* Read/Write */
+ char Pad3[0x10];
+ /*
+ * Interrupt Acknowledge Register
+ */
+ OpenPIC_Reg _Interrupt_Acknowledge; /* Read Only */
+ /*
+ * End of Interrupt (EOI) Register
+ */
+ OpenPIC_Reg _EOI; /* Read/Write */
+ char Pad5[0xf40];
+} OpenPIC_Processor;
+
+
+ /*
+ * Timer Registers
+ */
+
+typedef struct _OpenPIC_Timer {
+ OpenPIC_Reg _Current_Count; /* Read Only */
+ OpenPIC_Reg _Base_Count; /* Read/Write */
+ OpenPIC_Reg _Vector_Priority; /* Read/Write */
+ OpenPIC_Reg _Destination; /* Read/Write */
+} OpenPIC_Timer;
+
+
+ /*
+ * Global Registers
+ */
+
+typedef struct _OpenPIC_Global {
+ /*
+ * Feature Reporting Registers
+ */
+ OpenPIC_Reg _Feature_Reporting0; /* Read Only */
+ OpenPIC_Reg _Feature_Reporting1; /* Future Expansion */
+ /*
+ * Global Configuration Registers
+ */
+ OpenPIC_Reg _Global_Configuration0; /* Read/Write */
+ OpenPIC_Reg _Global_Configuration1; /* Future Expansion */
+ /*
+ * Vendor Specific Registers
+ */
+ OpenPIC_Reg _Vendor_Specific[4];
+ /*
+ * Vendor Identification Register
+ */
+ OpenPIC_Reg _Vendor_Identification; /* Read Only */
+ /*
+ * Processor Initialization Register
+ */
+ OpenPIC_Reg _Processor_Initialization; /* Read/Write */
+ /*
+ * IPI Vector/Priority Registers
+ */
+ OpenPIC_Reg _IPI_Vector_Priority[OPENPIC_NUM_IPI]; /* Read/Write */
+ /*
+ * Spurious Vector Register
+ */
+ OpenPIC_Reg _Spurious_Vector; /* Read/Write */
+ /*
+ * Global Timer Registers
+ */
+ OpenPIC_Reg _Timer_Frequency; /* Read/Write */
+ OpenPIC_Timer Timer[OPENPIC_NUM_TIMERS];
+ char Pad1[0xee00];
+} OpenPIC_Global;
+
+
+ /*
+ * Interrupt Source Registers
+ */
+
+typedef struct _OpenPIC_Source {
+ OpenPIC_Reg _Vector_Priority; /* Read/Write */
+ OpenPIC_Reg _Destination; /* Read/Write */
+} OpenPIC_Source, *OpenPIC_SourcePtr;
+
+
+ /*
+ * OpenPIC Register Map
+ */
+
+struct OpenPIC {
+ char Pad1[0x1000];
+ /*
+ * Global Registers
+ */
+ OpenPIC_Global Global;
+ /*
+ * Interrupt Source Configuration Registers
+ */
+ OpenPIC_Source Source[OPENPIC_MAX_SOURCES];
+ /*
+ * Per Processor Registers
+ */
+ OpenPIC_Processor Processor[OPENPIC_MAX_PROCESSORS];
+};
+
+extern volatile struct OpenPIC __iomem *OpenPIC;
+
+
+ /*
+ * Current Task Priority Register
+ */
+
+#define OPENPIC_CURRENT_TASK_PRIORITY_MASK 0x0000000f
+
+ /*
+ * Who Am I Register
+ */
+
+#define OPENPIC_WHO_AM_I_ID_MASK 0x0000001f
+
+ /*
+ * Feature Reporting Register 0
+ */
+
+#define OPENPIC_FEATURE_LAST_SOURCE_MASK 0x07ff0000
+#define OPENPIC_FEATURE_LAST_SOURCE_SHIFT 16
+#define OPENPIC_FEATURE_LAST_PROCESSOR_MASK 0x00001f00
+#define OPENPIC_FEATURE_LAST_PROCESSOR_SHIFT 8
+#define OPENPIC_FEATURE_VERSION_MASK 0x000000ff
+
+ /*
+ * Global Configuration Register 0
+ */
+
+#define OPENPIC_CONFIG_RESET 0x80000000
+#define OPENPIC_CONFIG_8259_PASSTHROUGH_DISABLE 0x20000000
+#define OPENPIC_CONFIG_BASE_MASK 0x000fffff
+
+ /*
+ * Global Configuration Register 1
+ * This is the EICR on EPICs.
+ */
+
+#define OPENPIC_EICR_S_CLK_MASK 0x70000000
+#define OPENPIC_EICR_SIE 0x08000000
+
+ /*
+ * Vendor Identification Register
+ */
+
+#define OPENPIC_VENDOR_ID_STEPPING_MASK 0x00ff0000
+#define OPENPIC_VENDOR_ID_STEPPING_SHIFT 16
+#define OPENPIC_VENDOR_ID_DEVICE_ID_MASK 0x0000ff00
+#define OPENPIC_VENDOR_ID_DEVICE_ID_SHIFT 8
+#define OPENPIC_VENDOR_ID_VENDOR_ID_MASK 0x000000ff
+
+ /*
+ * Vector/Priority Registers
+ */
+
+#define OPENPIC_MASK 0x80000000
+#define OPENPIC_ACTIVITY 0x40000000 /* Read Only */
+#define OPENPIC_PRIORITY_MASK 0x000f0000
+#define OPENPIC_PRIORITY_SHIFT 16
+#define OPENPIC_VECTOR_MASK 0x000000ff
+
+
+ /*
+ * Interrupt Source Registers
+ */
+
+#define OPENPIC_POLARITY_POSITIVE 0x00800000
+#define OPENPIC_POLARITY_NEGATIVE 0x00000000
+#define OPENPIC_POLARITY_MASK 0x00800000
+#define OPENPIC_SENSE_LEVEL 0x00400000
+#define OPENPIC_SENSE_EDGE 0x00000000
+#define OPENPIC_SENSE_MASK 0x00400000
+
+
+ /*
+ * Timer Registers
+ */
+
+#define OPENPIC_COUNT_MASK 0x7fffffff
+#define OPENPIC_TIMER_TOGGLE 0x80000000
+#define OPENPIC_TIMER_COUNT_INHIBIT 0x80000000
+
+
+ /*
+ * Aliases to make life simpler
+ */
+
+/* Per Processor Registers */
+#define IPI_Dispatch(i) _IPI_Dispatch[i].Reg
+#define Current_Task_Priority _Current_Task_Priority.Reg
+#define Interrupt_Acknowledge _Interrupt_Acknowledge.Reg
+#define EOI _EOI.Reg
+
+/* Global Registers */
+#define Feature_Reporting0 _Feature_Reporting0.Reg
+#define Feature_Reporting1 _Feature_Reporting1.Reg
+#define Global_Configuration0 _Global_Configuration0.Reg
+#define Global_Configuration1 _Global_Configuration1.Reg
+#define Vendor_Specific(i) _Vendor_Specific[i].Reg
+#define Vendor_Identification _Vendor_Identification.Reg
+#define Processor_Initialization _Processor_Initialization.Reg
+#define IPI_Vector_Priority(i) _IPI_Vector_Priority[i].Reg
+#define Spurious_Vector _Spurious_Vector.Reg
+#define Timer_Frequency _Timer_Frequency.Reg
+
+/* Timer Registers */
+#define Current_Count _Current_Count.Reg
+#define Base_Count _Base_Count.Reg
+#define Vector_Priority _Vector_Priority.Reg
+#define Destination _Destination.Reg
+
+/* Interrupt Source Registers */
+#define Vector_Priority _Vector_Priority.Reg
+#define Destination _Destination.Reg
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_OPENPIC_H */
diff --git a/arch/ppc/syslib/pci_auto.c b/arch/ppc/syslib/pci_auto.c
new file mode 100644
index 000000000000..d64207c2a972
--- /dev/null
+++ b/arch/ppc/syslib/pci_auto.c
@@ -0,0 +1,517 @@
+/*
+ * arch/ppc/syslib/pci_auto.c
+ *
+ * PCI autoconfiguration library
+ *
+ * Author: Matt Porter <mporter@mvista.com>
+ *
+ * 2001 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+/*
+ * The CardBus support is very preliminary. Preallocating space is
+ * the way to go but will require some change in card services to
+ * make it useful. Eventually this will ensure that we can put
+ * multiple CB bridges behind multiple P2P bridges. For now, at
+ * least it ensures that we place the CB bridge BAR and assigned
+ * initial bus numbers. I definitely need to do something about
+ * the lack of 16-bit I/O support. -MDP
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <asm/pci-bridge.h>
+
+#define PCIAUTO_IDE_MODE_MASK 0x05
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif /* DEBUG */
+
+static int pciauto_upper_iospc;
+static int pciauto_upper_memspc;
+
+void __init pciauto_setup_bars(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int bar_limit)
+{
+ int bar_response, bar_size, bar_value;
+ int bar, addr_mask;
+ int * upper_limit;
+ int found_mem64 = 0;
+
+ DBG("PCI Autoconfig: Found Bus %d, Device %d, Function %d\n",
+ current_bus, PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn) );
+
+ for (bar = PCI_BASE_ADDRESS_0; bar <= bar_limit; bar+=4) {
+ /* Tickle the BAR and get the response */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ 0xffffffff);
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ &bar_response);
+
+ /* If BAR is not implemented go to the next BAR */
+ if (!bar_response)
+ continue;
+
+ /* Check the BAR type and set our address mask */
+ if (bar_response & PCI_BASE_ADDRESS_SPACE) {
+ addr_mask = PCI_BASE_ADDRESS_IO_MASK;
+ upper_limit = &pciauto_upper_iospc;
+ DBG("PCI Autoconfig: BAR 0x%x, I/O, ", bar);
+ } else {
+ if ( (bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
+ PCI_BASE_ADDRESS_MEM_TYPE_64)
+ found_mem64 = 1;
+
+ addr_mask = PCI_BASE_ADDRESS_MEM_MASK;
+ upper_limit = &pciauto_upper_memspc;
+ DBG("PCI Autoconfig: BAR 0x%x, Mem ", bar);
+ }
+
+ /* Calculate requested size */
+ bar_size = ~(bar_response & addr_mask) + 1;
+
+ /* Allocate a base address */
+ bar_value = (*upper_limit - bar_size) & ~(bar_size - 1);
+
+ /* Write it out and update our limit */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ bar_value);
+
+ *upper_limit = bar_value;
+
+ /*
+ * If we are a 64-bit decoder then increment to the
+ * upper 32 bits of the bar and force it to locate
+ * in the lower 4GB of memory.
+ */
+ if (found_mem64) {
+ bar += 4;
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ bar,
+ 0x00000000);
+ found_mem64 = 0;
+ }
+
+ DBG("size=0x%x, address=0x%x\n",
+ bar_size, bar_value);
+ }
+
+}
+
+void __init pciauto_prescan_setup_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ /* Configure bus number registers */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PRIMARY_BUS,
+ current_bus);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SECONDARY_BUS,
+ sub_bus + 1);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ 0xff);
+
+ /* Round memory allocator to 1MB boundary */
+ pciauto_upper_memspc &= ~(0x100000 - 1);
+ *memsave = pciauto_upper_memspc;
+
+ /* Round I/O allocator to 4KB boundary */
+ pciauto_upper_iospc &= ~(0x1000 - 1);
+ *iosave = pciauto_upper_iospc;
+
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_MEMORY_LIMIT,
+ ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_LIMIT,
+ ((pciauto_upper_iospc - 1) & 0x0000f000) >> 8);
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_LIMIT_UPPER16,
+ ((pciauto_upper_iospc - 1) & 0xffff0000) >> 16);
+
+ /* Zero upper 32 bits of prefetchable base/limit */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_BASE_UPPER32,
+ 0);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_LIMIT_UPPER32,
+ 0);
+}
+
+void __init pciauto_postscan_setup_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ int cmdstat;
+
+ /* Configure bus number registers */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ sub_bus);
+
+ /*
+ * Round memory allocator to 1MB boundary.
+ * If no space used, allocate minimum.
+ */
+ pciauto_upper_memspc &= ~(0x100000 - 1);
+ if (*memsave == pciauto_upper_memspc)
+ pciauto_upper_memspc -= 0x00100000;
+
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_MEMORY_BASE,
+ pciauto_upper_memspc >> 16);
+
+ /* Allocate 1MB for pre-fretch */
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_MEMORY_LIMIT,
+ ((pciauto_upper_memspc - 1) & 0xfff00000) >> 16);
+
+ pciauto_upper_memspc -= 0x100000;
+
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PREF_MEMORY_BASE,
+ pciauto_upper_memspc >> 16);
+
+ /* Round I/O allocator to 4KB boundary */
+ pciauto_upper_iospc &= ~(0x1000 - 1);
+ if (*iosave == pciauto_upper_iospc)
+ pciauto_upper_iospc -= 0x1000;
+
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_BASE,
+ (pciauto_upper_iospc & 0x0000f000) >> 8);
+ early_write_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_IO_BASE_UPPER16,
+ pciauto_upper_iospc >> 16);
+
+ /* Enable memory and I/O accesses, enable bus master */
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ &cmdstat);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+}
+
+void __init pciauto_prescan_setup_cardbus_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ /* Configure bus number registers */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_PRIMARY_BUS,
+ current_bus);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SECONDARY_BUS,
+ sub_bus + 1);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ 0xff);
+
+ /* Round memory allocator to 4KB boundary */
+ pciauto_upper_memspc &= ~(0x1000 - 1);
+ *memsave = pciauto_upper_memspc;
+
+ /* Round I/O allocator to 4 byte boundary */
+ pciauto_upper_iospc &= ~(0x4 - 1);
+ *iosave = pciauto_upper_iospc;
+
+ /* Set up memory and I/O filter limits, assume 32-bit I/O space */
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x20,
+ pciauto_upper_memspc - 1);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x30,
+ pciauto_upper_iospc - 1);
+}
+
+void __init pciauto_postscan_setup_cardbus_bridge(struct pci_controller *hose,
+ int current_bus,
+ int pci_devfn,
+ int sub_bus,
+ int *iosave,
+ int *memsave)
+{
+ int cmdstat;
+
+ /*
+ * Configure subordinate bus number. The PCI subsystem
+ * bus scan will renumber buses (reserving three additional
+ * for this PCI<->CardBus bridge for the case where a CardBus
+ * adapter contains a P2P or CB2CB bridge.
+ */
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_SUBORDINATE_BUS,
+ sub_bus);
+
+ /*
+ * Reserve an additional 4MB for mem space and 16KB for
+ * I/O space. This should cover any additional space
+ * requirement of unusual CardBus devices with
+ * additional bridges that can consume more address space.
+ *
+ * Although pcmcia-cs currently will reprogram bridge
+ * windows, the goal is to add an option to leave them
+ * alone and use the bridge window ranges as the regions
+ * that are searched for free resources upon hot-insertion
+ * of a device. This will allow a PCI<->CardBus bridge
+ * configured by this routine to happily live behind a
+ * P2P bridge in a system.
+ */
+ pciauto_upper_memspc -= 0x00400000;
+ pciauto_upper_iospc -= 0x00004000;
+
+ /* Round memory allocator to 4KB boundary */
+ pciauto_upper_memspc &= ~(0x1000 - 1);
+
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x1c,
+ pciauto_upper_memspc);
+
+ /* Round I/O allocator to 4 byte boundary */
+ pciauto_upper_iospc &= ~(0x4 - 1);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ 0x2c,
+ pciauto_upper_iospc);
+
+ /* Enable memory and I/O accesses, enable bus master */
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ &cmdstat);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+}
+
+int __init pciauto_bus_scan(struct pci_controller *hose, int current_bus)
+{
+ int sub_bus, pci_devfn, pci_class, cmdstat, found_multi = 0;
+ unsigned short vid;
+ unsigned char header_type;
+
+ /*
+ * Fetch our I/O and memory space upper boundaries used
+ * to allocated base addresses on this hose.
+ */
+ if (current_bus == hose->first_busno) {
+ pciauto_upper_iospc = hose->io_space.end + 1;
+ pciauto_upper_memspc = hose->mem_space.end + 1;
+ }
+
+ sub_bus = current_bus;
+
+ for (pci_devfn = 0; pci_devfn < 0xff; pci_devfn++) {
+ /* Skip our host bridge */
+ if ( (current_bus == hose->first_busno) && (pci_devfn == 0) )
+ continue;
+
+ if (PCI_FUNC(pci_devfn) && !found_multi)
+ continue;
+
+ /* If config space read fails from this device, move on */
+ if (early_read_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_HEADER_TYPE,
+ &header_type))
+ continue;
+
+ if (!PCI_FUNC(pci_devfn))
+ found_multi = header_type & 0x80;
+
+ early_read_config_word(hose,
+ current_bus,
+ pci_devfn,
+ PCI_VENDOR_ID,
+ &vid);
+
+ if (vid != 0xffff) {
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_CLASS_REVISION, &pci_class);
+ if ( (pci_class >> 16) == PCI_CLASS_BRIDGE_PCI ) {
+ int iosave, memsave;
+
+ DBG("PCI Autoconfig: Found P2P bridge, device %d\n", PCI_SLOT(pci_devfn));
+ /* Allocate PCI I/O and/or memory space */
+ pciauto_setup_bars(hose,
+ current_bus,
+ pci_devfn,
+ PCI_BASE_ADDRESS_1);
+
+ pciauto_prescan_setup_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ sub_bus = pciauto_bus_scan(hose, sub_bus+1);
+ pciauto_postscan_setup_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ } else if ((pci_class >> 16) == PCI_CLASS_BRIDGE_CARDBUS) {
+ int iosave, memsave;
+
+ DBG("PCI Autoconfig: Found CardBus bridge, device %d function %d\n", PCI_SLOT(pci_devfn), PCI_FUNC(pci_devfn));
+ /* Place CardBus Socket/ExCA registers */
+ pciauto_setup_bars(hose,
+ current_bus,
+ pci_devfn,
+ PCI_BASE_ADDRESS_0);
+
+ pciauto_prescan_setup_cardbus_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ sub_bus = pciauto_bus_scan(hose, sub_bus+1);
+ pciauto_postscan_setup_cardbus_bridge(hose,
+ current_bus,
+ pci_devfn,
+ sub_bus,
+ &iosave,
+ &memsave);
+ } else {
+ if ((pci_class >> 16) == PCI_CLASS_STORAGE_IDE) {
+ unsigned char prg_iface;
+
+ early_read_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_CLASS_PROG,
+ &prg_iface);
+ if (!(prg_iface & PCIAUTO_IDE_MODE_MASK)) {
+ DBG("PCI Autoconfig: Skipping legacy mode IDE controller\n");
+ continue;
+ }
+ }
+ /* Allocate PCI I/O and/or memory space */
+ pciauto_setup_bars(hose,
+ current_bus,
+ pci_devfn,
+ PCI_BASE_ADDRESS_5);
+
+ /*
+ * Enable some standard settings
+ */
+ early_read_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ &cmdstat);
+ early_write_config_dword(hose,
+ current_bus,
+ pci_devfn,
+ PCI_COMMAND,
+ cmdstat |
+ PCI_COMMAND_IO |
+ PCI_COMMAND_MEMORY |
+ PCI_COMMAND_MASTER);
+ early_write_config_byte(hose,
+ current_bus,
+ pci_devfn,
+ PCI_LATENCY_TIMER,
+ 0x80);
+ }
+ }
+ }
+ return sub_bus;
+}
diff --git a/arch/ppc/syslib/ppc403_pic.c b/arch/ppc/syslib/ppc403_pic.c
new file mode 100644
index 000000000000..06cb0af2a58d
--- /dev/null
+++ b/arch/ppc/syslib/ppc403_pic.c
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
+ *
+ * Module name: ppc403_pic.c
+ *
+ * Description:
+ * Interrupt controller driver for PowerPC 403-based processors.
+ */
+
+/*
+ * The PowerPC 403 cores' Asynchronous Interrupt Controller (AIC) has
+ * 32 possible interrupts, a majority of which are not implemented on
+ * all cores. There are six configurable, external interrupt pins and
+ * there are eight internal interrupts for the on-chip serial port
+ * (SPU), DMA controller, and JTAG controller.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/ppc4xx_pic.h>
+
+/* Function Prototypes */
+
+static void ppc403_aic_enable(unsigned int irq);
+static void ppc403_aic_disable(unsigned int irq);
+static void ppc403_aic_disable_and_ack(unsigned int irq);
+
+static struct hw_interrupt_type ppc403_aic = {
+ "403GC AIC",
+ NULL,
+ NULL,
+ ppc403_aic_enable,
+ ppc403_aic_disable,
+ ppc403_aic_disable_and_ack,
+ 0
+};
+
+int
+ppc403_pic_get_irq(struct pt_regs *regs)
+{
+ int irq;
+ unsigned long bits;
+
+ /*
+ * Only report the status of those interrupts that are actually
+ * enabled.
+ */
+
+ bits = mfdcr(DCRN_EXISR) & mfdcr(DCRN_EXIER);
+
+ /*
+ * Walk through the interrupts from highest priority to lowest, and
+ * report the first pending interrupt found.
+ * We want PPC, not C bit numbering, so just subtract the ffs()
+ * result from 32.
+ */
+ irq = 32 - ffs(bits);
+
+ if (irq == NR_AIC_IRQS)
+ irq = -1;
+
+ return (irq);
+}
+
+static void
+ppc403_aic_enable(unsigned int irq)
+{
+ int bit, word;
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+
+ ppc_cached_irq_mask[word] |= (1 << (31 - bit));
+ mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+}
+
+static void
+ppc403_aic_disable(unsigned int irq)
+{
+ int bit, word;
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+
+ ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
+ mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+}
+
+static void
+ppc403_aic_disable_and_ack(unsigned int irq)
+{
+ int bit, word;
+
+ bit = irq & 0x1f;
+ word = irq >> 5;
+
+ ppc_cached_irq_mask[word] &= ~(1 << (31 - bit));
+ mtdcr(DCRN_EXIER, ppc_cached_irq_mask[word]);
+ mtdcr(DCRN_EXISR, (1 << (31 - bit)));
+}
+
+void __init
+ppc4xx_pic_init(void)
+{
+ int i;
+
+ /*
+ * Disable all external interrupts until they are
+ * explicity requested.
+ */
+ ppc_cached_irq_mask[0] = 0;
+
+ mtdcr(DCRN_EXIER, ppc_cached_irq_mask[0]);
+
+ ppc_md.get_irq = ppc403_pic_get_irq;
+
+ for (i = 0; i < NR_IRQS; i++)
+ irq_desc[i].handler = &ppc403_aic;
+}
diff --git a/arch/ppc/syslib/ppc405_pci.c b/arch/ppc/syslib/ppc405_pci.c
new file mode 100644
index 000000000000..81c83bf98df4
--- /dev/null
+++ b/arch/ppc/syslib/ppc405_pci.c
@@ -0,0 +1,177 @@
+/*
+ * Authors: Frank Rowand <frank_rowand@mvista.com>,
+ * Debbie Chu <debbie_chu@mvista.com>, or source@mvista.com
+ * Further modifications by Armin Kuster <akuster@mvista.com>
+ *
+ * 2000 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * Based on arch/ppc/kernel/indirect.c, Copyright (C) 1998 Gabriel Paubert.
+ */
+
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/machdep.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <asm/ocp.h>
+#include <asm/ibm4xx.h>
+#include <asm/pci-bridge.h>
+#include <asm/ibm_ocp_pci.h>
+
+
+extern void bios_fixup(struct pci_controller *, struct pcil0_regs *);
+extern int ppc405_map_irq(struct pci_dev *dev, unsigned char idsel,
+ unsigned char pin);
+
+void
+ppc405_pcibios_fixup_resources(struct pci_dev *dev)
+{
+ int i;
+ unsigned long max_host_addr;
+ unsigned long min_host_addr;
+ struct resource *res;
+
+ /*
+ * openbios puts some graphics cards in the same range as the host
+ * controller uses to map to SDRAM. Fix it.
+ */
+
+ min_host_addr = 0;
+ max_host_addr = PPC405_PCI_MEM_BASE - 1;
+
+ for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+ res = dev->resource + i;
+ if (!res->start)
+ continue;
+ if ((res->flags & IORESOURCE_MEM) &&
+ (((res->start >= min_host_addr)
+ && (res->start <= max_host_addr))
+ || ((res->end >= min_host_addr)
+ && (res->end <= max_host_addr))
+ || ((res->start < min_host_addr)
+ && (res->end > max_host_addr))
+ )
+ ) {
+
+ /* force pcibios_assign_resources() to assign a new address */
+ res->end -= res->start;
+ res->start = 0;
+ }
+ }
+}
+
+static int
+ppc4xx_exclude_device(unsigned char bus, unsigned char devfn)
+{
+ /* We prevent us from seeing ourselves to avoid having
+ * the kernel try to remap our BAR #1 and fuck up bus
+ * master from external PCI devices
+ */
+ return (bus == 0 && devfn == 0);
+}
+
+void
+ppc4xx_find_bridges(void)
+{
+ struct pci_controller *hose_a;
+ struct pcil0_regs *pcip;
+ unsigned int tmp_addr;
+ unsigned int tmp_size;
+ unsigned int reg_index;
+ unsigned int new_pmm_max = 0;
+ unsigned int new_pmm_min = 0;
+
+ isa_io_base = 0;
+ isa_mem_base = 0;
+ pci_dram_offset = 0;
+
+#if (PSR_PCI_ARBIT_EN > 1)
+ /* Check if running in slave mode */
+ if ((mfdcr(DCRN_CHPSR) & PSR_PCI_ARBIT_EN) == 0) {
+ printk("Running as PCI slave, kernel PCI disabled !\n");
+ return;
+ }
+#endif
+ /* Setup PCI32 hose */
+ hose_a = pcibios_alloc_controller();
+ if (!hose_a)
+ return;
+ setup_indirect_pci(hose_a, PPC405_PCI_CONFIG_ADDR,
+ PPC405_PCI_CONFIG_DATA);
+
+ pcip = ioremap(PPC4xx_PCI_LCFG_PADDR, PAGE_SIZE);
+ if (pcip != NULL) {
+
+#if defined(CONFIG_BIOS_FIXUP)
+ bios_fixup(hose_a, pcip);
+#endif
+ new_pmm_min = 0xffffffff;
+ for (reg_index = 0; reg_index < 3; reg_index++) {
+ tmp_size = in_le32(&pcip->pmm[reg_index].ma); // mask & attrs
+ /* test the enable bit */
+ if ((tmp_size & 0x1) == 0)
+ continue;
+ tmp_addr = in_le32(&pcip->pmm[reg_index].pcila); // PCI addr
+ if (tmp_addr < PPC405_PCI_PHY_MEM_BASE) {
+ printk(KERN_DEBUG
+ "Disabling mapping to PCI mem addr 0x%8.8x\n",
+ tmp_addr);
+ out_le32(&pcip->pmm[reg_index].ma, tmp_size & ~1); // *_PMMOMA
+ continue;
+ }
+ tmp_addr = in_le32(&pcip->pmm[reg_index].la); // *_PMMOLA
+ if (tmp_addr < new_pmm_min)
+ new_pmm_min = tmp_addr;
+ tmp_addr = tmp_addr +
+ (0xffffffff - (tmp_size & 0xffffc000));
+ if (tmp_addr > PPC405_PCI_UPPER_MEM) {
+ new_pmm_max = tmp_addr; // PPC405_PCI_UPPER_MEM
+ } else {
+ new_pmm_max = PPC405_PCI_UPPER_MEM;
+ }
+
+ } // for
+
+ iounmap(pcip);
+ }
+
+ hose_a->first_busno = 0;
+ hose_a->last_busno = 0xff;
+ hose_a->pci_mem_offset = 0;
+
+ /* Setup bridge memory/IO ranges & resources
+ * TODO: Handle firmwares setting up a legacy ISA mem base
+ */
+ hose_a->io_space.start = PPC405_PCI_LOWER_IO;
+ hose_a->io_space.end = PPC405_PCI_UPPER_IO;
+ hose_a->mem_space.start = new_pmm_min;
+ hose_a->mem_space.end = new_pmm_max;
+ hose_a->io_base_phys = PPC405_PCI_PHY_IO_BASE;
+ hose_a->io_base_virt = ioremap(hose_a->io_base_phys, 0x10000);
+ hose_a->io_resource.start = 0;
+ hose_a->io_resource.end = PPC405_PCI_UPPER_IO - PPC405_PCI_LOWER_IO;
+ hose_a->io_resource.flags = IORESOURCE_IO;
+ hose_a->io_resource.name = "PCI I/O";
+ hose_a->mem_resources[0].start = new_pmm_min;
+ hose_a->mem_resources[0].end = new_pmm_max;
+ hose_a->mem_resources[0].flags = IORESOURCE_MEM;
+ hose_a->mem_resources[0].name = "PCI Memory";
+ isa_io_base = (int) hose_a->io_base_virt;
+ isa_mem_base = 0; /* ISA not implemented */
+ ISA_DMA_THRESHOLD = 0x00ffffff; /* ??? ISA not implemented */
+
+ /* Scan busses & initial setup by pci_auto */
+ hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno);
+ hose_a->last_busno = 0;
+
+ /* Setup ppc_md */
+ ppc_md.pcibios_fixup = NULL;
+ ppc_md.pci_exclude_device = ppc4xx_exclude_device;
+ ppc_md.pcibios_fixup_resources = ppc405_pcibios_fixup_resources;
+ ppc_md.pci_swizzle = common_swizzle;
+ ppc_md.pci_map_irq = ppc405_map_irq;
+}
diff --git a/arch/ppc/syslib/ppc4xx_dma.c b/arch/ppc/syslib/ppc4xx_dma.c
new file mode 100644
index 000000000000..5015ab99afd2
--- /dev/null
+++ b/arch/ppc/syslib/ppc4xx_dma.c
@@ -0,0 +1,708 @@
+/*
+ * arch/ppc/kernel/ppc4xx_dma.c
+ *
+ * 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/config.h>
+#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/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 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;
+
+ mtdcr(DCRN_DMASR, 0xffffffff); /* clear status register */
+ 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;
+}
+
+/*
+ * 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_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);
+EXPORT_SYMBOL(ppc4xx_enable_burst);
+EXPORT_SYMBOL(ppc4xx_disable_burst);
+EXPORT_SYMBOL(ppc4xx_set_burst_size);
diff --git a/arch/ppc/syslib/ppc4xx_kgdb.c b/arch/ppc/syslib/ppc4xx_kgdb.c
new file mode 100644
index 000000000000..fe8668bf8137
--- /dev/null
+++ b/arch/ppc/syslib/ppc4xx_kgdb.c
@@ -0,0 +1,124 @@
+#include <linux/config.h>
+#include <linux/types.h>
+#include <asm/ibm4xx.h>
+#include <linux/kernel.h>
+
+
+
+#define LSR_DR 0x01 /* Data ready */
+#define LSR_OE 0x02 /* Overrun */
+#define LSR_PE 0x04 /* Parity error */
+#define LSR_FE 0x08 /* Framing error */
+#define LSR_BI 0x10 /* Break */
+#define LSR_THRE 0x20 /* Xmit holding register empty */
+#define LSR_TEMT 0x40 /* Xmitter empty */
+#define LSR_ERR 0x80 /* Error */
+
+#include <platforms/4xx/ibm_ocp.h>
+
+extern struct NS16550* COM_PORTS[];
+#ifndef NULL
+#define NULL 0x00
+#endif
+
+static volatile struct NS16550 *kgdb_debugport = NULL;
+
+volatile struct NS16550 *
+NS16550_init(int chan)
+{
+ volatile struct NS16550 *com_port;
+ int quot;
+#ifdef BASE_BAUD
+ quot = BASE_BAUD / 9600;
+#else
+ quot = 0x000c; /* 0xc = 9600 baud (on a pc) */
+#endif
+
+ com_port = (struct NS16550 *) COM_PORTS[chan];
+
+ com_port->lcr = 0x00;
+ com_port->ier = 0xFF;
+ com_port->ier = 0x00;
+ com_port->lcr = com_port->lcr | 0x80; /* Access baud rate */
+ com_port->dll = ( quot & 0x00ff ); /* 0xc = 9600 baud */
+ com_port->dlm = ( quot & 0xff00 ) >> 8;
+ com_port->lcr = 0x03; /* 8 data, 1 stop, no parity */
+ com_port->mcr = 0x00; /* RTS/DTR */
+ com_port->fcr = 0x07; /* Clear & enable FIFOs */
+
+ return( com_port );
+}
+
+
+void
+NS16550_putc(volatile struct NS16550 *com_port, unsigned char c)
+{
+ while ((com_port->lsr & LSR_THRE) == 0)
+ ;
+ com_port->thr = c;
+ return;
+}
+
+unsigned char
+NS16550_getc(volatile struct NS16550 *com_port)
+{
+ while ((com_port->lsr & LSR_DR) == 0)
+ ;
+ return (com_port->rbr);
+}
+
+unsigned char
+NS16550_tstc(volatile struct NS16550 *com_port)
+{
+ return ((com_port->lsr & LSR_DR) != 0);
+}
+
+
+#if defined(CONFIG_KGDB_TTYS0)
+#define KGDB_PORT 0
+#elif defined(CONFIG_KGDB_TTYS1)
+#define KGDB_PORT 1
+#elif defined(CONFIG_KGDB_TTYS2)
+#define KGDB_PORT 2
+#elif defined(CONFIG_KGDB_TTYS3)
+#define KGDB_PORT 3
+#else
+#error "invalid kgdb_tty port"
+#endif
+
+void putDebugChar( unsigned char c )
+{
+ if ( kgdb_debugport == NULL )
+ kgdb_debugport = NS16550_init(KGDB_PORT);
+ NS16550_putc( kgdb_debugport, c );
+}
+
+int getDebugChar( void )
+{
+ if (kgdb_debugport == NULL)
+ kgdb_debugport = NS16550_init(KGDB_PORT);
+
+ return(NS16550_getc(kgdb_debugport));
+}
+
+void kgdb_interruptible(int enable)
+{
+ return;
+}
+
+void putDebugString(char* str)
+{
+ while (*str != '\0') {
+ putDebugChar(*str);
+ str++;
+ }
+ putDebugChar('\r');
+ return;
+}
+
+void
+kgdb_map_scc(void)
+{
+ printk("kgdb init \n");
+ kgdb_debugport = NS16550_init(KGDB_PORT);
+}
diff --git a/arch/ppc/syslib/ppc4xx_pic.c b/arch/ppc/syslib/ppc4xx_pic.c
new file mode 100644
index 000000000000..08f06dd17e7b
--- /dev/null
+++ b/arch/ppc/syslib/ppc4xx_pic.c
@@ -0,0 +1,244 @@
+/*
+ * arch/ppc/syslib/ppc4xx_pic.c
+ *
+ * Interrupt controller driver for PowerPC 4xx-based processors.
+ *
+ * Eugene Surovegin <eugene.surovegin@zultys.com> or <ebs@ebshome.net>
+ * Copyright (c) 2004, 2005 Zultys Technologies
+ *
+ * Based on original code by
+ * Copyright (c) 1999 Grant Erickson <grant@lcse.umn.edu>
+ * Armin Custer <akuster@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.
+*/
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/stddef.h>
+
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/ppc4xx_pic.h>
+
+/* See comment in include/arch-ppc/ppc4xx_pic.h
+ * for more info about these two variables
+ */
+extern struct ppc4xx_uic_settings ppc4xx_core_uic_cfg[NR_UICS]
+ __attribute__ ((weak));
+extern unsigned char ppc4xx_uic_ext_irq_cfg[] __attribute__ ((weak));
+
+#define IRQ_MASK_UIC0(irq) (1 << (31 - (irq)))
+#define IRQ_MASK_UICx(irq) (1 << (31 - ((irq) & 0x1f)))
+#define IRQ_MASK_UIC1(irq) IRQ_MASK_UICx(irq)
+#define IRQ_MASK_UIC2(irq) IRQ_MASK_UICx(irq)
+
+#define UIC_HANDLERS(n) \
+static void ppc4xx_uic##n##_enable(unsigned int irq) \
+{ \
+ ppc_cached_irq_mask[n] |= IRQ_MASK_UIC##n(irq); \
+ mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
+} \
+ \
+static void ppc4xx_uic##n##_disable(unsigned int irq) \
+{ \
+ ppc_cached_irq_mask[n] &= ~IRQ_MASK_UIC##n(irq); \
+ mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
+ ACK_UIC##n##_PARENT \
+} \
+ \
+static void ppc4xx_uic##n##_ack(unsigned int irq) \
+{ \
+ u32 mask = IRQ_MASK_UIC##n(irq); \
+ ppc_cached_irq_mask[n] &= ~mask; \
+ mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
+ mtdcr(DCRN_UIC_SR(UIC##n), mask); \
+ ACK_UIC##n##_PARENT \
+} \
+ \
+static void ppc4xx_uic##n##_end(unsigned int irq) \
+{ \
+ unsigned int status = irq_desc[irq].status; \
+ u32 mask = IRQ_MASK_UIC##n(irq); \
+ if (status & IRQ_LEVEL) { \
+ mtdcr(DCRN_UIC_SR(UIC##n), mask); \
+ ACK_UIC##n##_PARENT \
+ } \
+ if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { \
+ ppc_cached_irq_mask[n] |= mask; \
+ mtdcr(DCRN_UIC_ER(UIC##n), ppc_cached_irq_mask[n]); \
+ } \
+}
+
+#define DECLARE_UIC(n) \
+{ \
+ .typename = "UIC"#n, \
+ .enable = ppc4xx_uic##n##_enable, \
+ .disable = ppc4xx_uic##n##_disable, \
+ .ack = ppc4xx_uic##n##_ack, \
+ .end = ppc4xx_uic##n##_end, \
+} \
+
+#if NR_UICS == 3
+#define ACK_UIC0_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC0NC);
+#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC1NC);
+#define ACK_UIC2_PARENT mtdcr(DCRN_UIC_SR(UICB), UICB_UIC2NC);
+UIC_HANDLERS(0);
+UIC_HANDLERS(1);
+UIC_HANDLERS(2);
+
+static int ppc4xx_pic_get_irq(struct pt_regs *regs)
+{
+ u32 uicb = mfdcr(DCRN_UIC_MSR(UICB));
+ if (uicb & UICB_UIC0NC)
+ return 32 - ffs(mfdcr(DCRN_UIC_MSR(UIC0)));
+ else if (uicb & UICB_UIC1NC)
+ return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
+ else if (uicb & UICB_UIC2NC)
+ return 96 - ffs(mfdcr(DCRN_UIC_MSR(UIC2)));
+ else
+ return -1;
+}
+
+static void __init ppc4xx_pic_impl_init(void)
+{
+ /* Configure Base UIC */
+ mtdcr(DCRN_UIC_CR(UICB), 0);
+ mtdcr(DCRN_UIC_TR(UICB), 0);
+ mtdcr(DCRN_UIC_PR(UICB), 0xffffffff);
+ mtdcr(DCRN_UIC_SR(UICB), 0xffffffff);
+ mtdcr(DCRN_UIC_ER(UICB), UICB_UIC0NC | UICB_UIC1NC | UICB_UIC2NC);
+}
+
+#elif NR_UICS == 2
+#define ACK_UIC0_PARENT
+#define ACK_UIC1_PARENT mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
+UIC_HANDLERS(0);
+UIC_HANDLERS(1);
+
+static int ppc4xx_pic_get_irq(struct pt_regs *regs)
+{
+ u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
+ if (uic0 & UIC0_UIC1NC)
+ return 64 - ffs(mfdcr(DCRN_UIC_MSR(UIC1)));
+ else
+ return uic0 ? 32 - ffs(uic0) : -1;
+}
+
+static void __init ppc4xx_pic_impl_init(void)
+{
+ /* Enable cascade interrupt in UIC0 */
+ ppc_cached_irq_mask[0] |= UIC0_UIC1NC;
+ mtdcr(DCRN_UIC_SR(UIC0), UIC0_UIC1NC);
+ mtdcr(DCRN_UIC_ER(UIC0), ppc_cached_irq_mask[0]);
+}
+
+#elif NR_UICS == 1
+#define ACK_UIC0_PARENT
+UIC_HANDLERS(0);
+
+static int ppc4xx_pic_get_irq(struct pt_regs *regs)
+{
+ u32 uic0 = mfdcr(DCRN_UIC_MSR(UIC0));
+ return uic0 ? 32 - ffs(uic0) : -1;
+}
+
+static inline void ppc4xx_pic_impl_init(void)
+{
+}
+#endif
+
+static struct ppc4xx_uic_impl {
+ struct hw_interrupt_type decl;
+ int base; /* Base DCR number */
+} __uic[] = {
+ { .decl = DECLARE_UIC(0), .base = UIC0 },
+#if NR_UICS > 1
+ { .decl = DECLARE_UIC(1), .base = UIC1 },
+#if NR_UICS > 2
+ { .decl = DECLARE_UIC(2), .base = UIC2 },
+#endif
+#endif
+};
+
+static inline int is_level_sensitive(int irq)
+{
+ u32 tr = mfdcr(DCRN_UIC_TR(__uic[irq >> 5].base));
+ return (tr & IRQ_MASK_UICx(irq)) == 0;
+}
+
+void __init ppc4xx_pic_init(void)
+{
+ int i;
+ unsigned char *eirqs = ppc4xx_uic_ext_irq_cfg;
+
+ for (i = 0; i < NR_UICS; ++i) {
+ int base = __uic[i].base;
+
+ /* Disable everything by default */
+ ppc_cached_irq_mask[i] = 0;
+ mtdcr(DCRN_UIC_ER(base), 0);
+
+ /* We don't use critical interrupts */
+ mtdcr(DCRN_UIC_CR(base), 0);
+
+ /* Configure polarity and triggering */
+ if (ppc4xx_core_uic_cfg) {
+ struct ppc4xx_uic_settings *p = ppc4xx_core_uic_cfg + i;
+ u32 mask = p->ext_irq_mask;
+ u32 pr = mfdcr(DCRN_UIC_PR(base)) & mask;
+ u32 tr = mfdcr(DCRN_UIC_TR(base)) & mask;
+
+ /* "Fixed" interrupts (on-chip devices) */
+ pr |= p->polarity & ~mask;
+ tr |= p->triggering & ~mask;
+
+ /* Merge external IRQs settings if board port
+ * provided them
+ */
+ if (eirqs && mask) {
+ pr &= ~mask;
+ tr &= ~mask;
+ while (mask) {
+ /* Extract current external IRQ mask */
+ u32 eirq_mask = 1 << __ilog2(mask);
+
+ if (!(*eirqs & IRQ_SENSE_LEVEL))
+ tr |= eirq_mask;
+
+ if (*eirqs & IRQ_POLARITY_POSITIVE)
+ pr |= eirq_mask;
+
+ mask &= ~eirq_mask;
+ ++eirqs;
+ }
+ }
+ mtdcr(DCRN_UIC_PR(base), pr);
+ mtdcr(DCRN_UIC_TR(base), tr);
+ }
+
+ /* ACK any pending interrupts to prevent false
+ * triggering after first enable
+ */
+ mtdcr(DCRN_UIC_SR(base), 0xffffffff);
+ }
+
+ /* Perform optional implementation specific setup
+ * (e.g. enable cascade interrupts for multi-UIC configurations)
+ */
+ ppc4xx_pic_impl_init();
+
+ /* Attach low-level handlers */
+ for (i = 0; i < (NR_UICS << 5); ++i) {
+ irq_desc[i].handler = &__uic[i >> 5].decl;
+ if (is_level_sensitive(i))
+ irq_desc[i].status |= IRQ_LEVEL;
+ }
+
+ ppc_md.get_irq = ppc4xx_pic_get_irq;
+}
diff --git a/arch/ppc/syslib/ppc4xx_pm.c b/arch/ppc/syslib/ppc4xx_pm.c
new file mode 100644
index 000000000000..60a479204885
--- /dev/null
+++ b/arch/ppc/syslib/ppc4xx_pm.c
@@ -0,0 +1,47 @@
+/*
+ * Author: Armin Kuster <akuster@mvista.com>
+ *
+ * 2002 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ * This an attempt to get Power Management going for the IBM 4xx processor.
+ * This was derived from the ppc4xx._setup.c file
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+
+#include <asm/ibm4xx.h>
+
+void __init
+ppc4xx_pm_init(void)
+{
+
+ unsigned int value = 0;
+
+ /* turn off unused hardware to save power */
+#ifdef CONFIG_405GP
+ value |= CPM_DCP; /* CodePack */
+#endif
+
+#if !defined(CONFIG_IBM_OCP_GPIO)
+ value |= CPM_GPIO0;
+#endif
+
+#if !defined(CONFIG_PPC405_I2C_ADAP)
+ value |= CPM_IIC0;
+#ifdef CONFIG_STB03xxx
+ value |= CPM_IIC1;
+#endif
+#endif
+
+
+#if !defined(CONFIG_405_DMA)
+ value |= CPM_DMA;
+#endif
+
+ mtdcr(DCRN_CPMFR, value);
+
+}
diff --git a/arch/ppc/syslib/ppc4xx_setup.c b/arch/ppc/syslib/ppc4xx_setup.c
new file mode 100644
index 000000000000..e170aebeb69b
--- /dev/null
+++ b/arch/ppc/syslib/ppc4xx_setup.c
@@ -0,0 +1,321 @@
+/*
+ *
+ * Copyright (c) 1999-2000 Grant Erickson <grant@lcse.umn.edu>
+ *
+ * Copyright 2000-2001 MontaVista Software Inc.
+ * Completed implementation.
+ * Author: MontaVista Software, Inc. <source@mvista.com>
+ * Frank Rowand <frank_rowand@mvista.com>
+ * Debbie Chu <debbie_chu@mvista.com>
+ * Further modifications by Armin Kuster
+ *
+ * Module name: ppc4xx_setup.c
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/smp.h>
+#include <linux/threads.h>
+#include <linux/spinlock.h>
+#include <linux/irq.h>
+#include <linux/reboot.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/initrd.h>
+#include <linux/pci.h>
+#include <linux/rtc.h>
+#include <linux/console.h>
+#include <linux/ide.h>
+#include <linux/serial_reg.h>
+#include <linux/seq_file.h>
+
+#include <asm/system.h>
+#include <asm/processor.h>
+#include <asm/machdep.h>
+#include <asm/page.h>
+#include <asm/kgdb.h>
+#include <asm/ibm4xx.h>
+#include <asm/time.h>
+#include <asm/todc.h>
+#include <asm/ppc4xx_pic.h>
+#include <asm/pci-bridge.h>
+#include <asm/bootinfo.h>
+
+#include <syslib/gen550.h>
+
+/* Function Prototypes */
+extern void abort(void);
+extern void ppc4xx_find_bridges(void);
+
+extern void ppc4xx_wdt_heartbeat(void);
+extern int wdt_enable;
+extern unsigned long wdt_period;
+
+/* Global Variables */
+bd_t __res;
+
+void __init
+ppc4xx_setup_arch(void)
+{
+#if !defined(CONFIG_BDI_SWITCH)
+ /*
+ * The Abatron BDI JTAG debugger does not tolerate others
+ * mucking with the debug registers.
+ */
+ mtspr(SPRN_DBCR0, (DBCR0_IDM));
+ mtspr(SPRN_DBSR, 0xffffffff);
+#endif
+
+ /* Setup PCI host bridges */
+#ifdef CONFIG_PCI
+ ppc4xx_find_bridges();
+#endif
+}
+
+/*
+ * This routine pretty-prints the platform's internal CPU clock
+ * frequencies into the buffer for usage in /proc/cpuinfo.
+ */
+
+static int
+ppc4xx_show_percpuinfo(struct seq_file *m, int i)
+{
+ seq_printf(m, "clock\t\t: %ldMHz\n", (long)__res.bi_intfreq / 1000000);
+
+ return 0;
+}
+
+/*
+ * This routine pretty-prints the platform's internal bus clock
+ * frequencies into the buffer for usage in /proc/cpuinfo.
+ */
+static int
+ppc4xx_show_cpuinfo(struct seq_file *m)
+{
+ bd_t *bip = &__res;
+
+ seq_printf(m, "machine\t\t: %s\n", PPC4xx_MACHINE_NAME);
+ seq_printf(m, "plb bus clock\t: %ldMHz\n",
+ (long) bip->bi_busfreq / 1000000);
+#ifdef CONFIG_PCI
+ seq_printf(m, "pci bus clock\t: %dMHz\n",
+ bip->bi_pci_busfreq / 1000000);
+#endif
+
+ return 0;
+}
+
+/*
+ * Return the virtual address representing the top of physical RAM.
+ */
+static unsigned long __init
+ppc4xx_find_end_of_memory(void)
+{
+ return ((unsigned long) __res.bi_memsize);
+}
+
+void __init
+ppc4xx_map_io(void)
+{
+ io_block_mapping(PPC4xx_ONB_IO_VADDR,
+ PPC4xx_ONB_IO_PADDR, PPC4xx_ONB_IO_SIZE, _PAGE_IO);
+#ifdef CONFIG_PCI
+ io_block_mapping(PPC4xx_PCI_IO_VADDR,
+ PPC4xx_PCI_IO_PADDR, PPC4xx_PCI_IO_SIZE, _PAGE_IO);
+ io_block_mapping(PPC4xx_PCI_CFG_VADDR,
+ PPC4xx_PCI_CFG_PADDR, PPC4xx_PCI_CFG_SIZE, _PAGE_IO);
+ io_block_mapping(PPC4xx_PCI_LCFG_VADDR,
+ PPC4xx_PCI_LCFG_PADDR, PPC4xx_PCI_LCFG_SIZE, _PAGE_IO);
+#endif
+}
+
+void __init
+ppc4xx_init_IRQ(void)
+{
+ ppc4xx_pic_init();
+}
+
+static void
+ppc4xx_restart(char *cmd)
+{
+ printk("%s\n", cmd);
+ abort();
+}
+
+static void
+ppc4xx_power_off(void)
+{
+ printk("System Halted\n");
+ local_irq_disable();
+ while (1) ;
+}
+
+static void
+ppc4xx_halt(void)
+{
+ printk("System Halted\n");
+ local_irq_disable();
+ while (1) ;
+}
+
+/*
+ * This routine retrieves the internal processor frequency from the board
+ * information structure, sets up the kernel timer decrementer based on
+ * that value, enables the 4xx programmable interval timer (PIT) and sets
+ * it up for auto-reload.
+ */
+static void __init
+ppc4xx_calibrate_decr(void)
+{
+ unsigned int freq;
+ bd_t *bip = &__res;
+
+#if defined(CONFIG_WALNUT) || defined(CONFIG_ASH) || defined(CONFIG_SYCAMORE)
+ /* Walnut boot rom sets DCR CHCR1 (aka CPC0_CR1) bit CETE to 1 */
+ mtdcr(DCRN_CHCR1, mfdcr(DCRN_CHCR1) & ~CHR1_CETE);
+#endif
+ freq = bip->bi_tbfreq;
+ tb_ticks_per_jiffy = freq / HZ;
+ tb_to_us = mulhwu_scale_factor(freq, 1000000);
+
+ /* Set the time base to zero.
+ ** At 200 Mhz, time base will rollover in ~2925 years.
+ */
+
+ mtspr(SPRN_TBWL, 0);
+ mtspr(SPRN_TBWU, 0);
+
+ /* Clear any pending timer interrupts */
+
+ mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_PIS | TSR_FIS);
+ mtspr(SPRN_TCR, TCR_PIE | TCR_ARE);
+
+ /* Set the PIT reload value and just let it run. */
+ mtspr(SPRN_PIT, tb_ticks_per_jiffy);
+}
+
+/*
+ * IDE stuff.
+ * should be generic for every IDE PCI chipset
+ */
+#if defined(CONFIG_PCI) && defined(CONFIG_IDE)
+static void
+ppc4xx_ide_init_hwif_ports(hw_regs_t * hw, unsigned long data_port,
+ unsigned long ctrl_port, int *irq)
+{
+ int i;
+
+ for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i)
+ hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET;
+
+ hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+}
+#endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */
+
+TODC_ALLOC();
+
+/*
+ * Input(s):
+ * r3 - Optional pointer to a board information structure.
+ * r4 - Optional pointer to the physical starting address of the init RAM
+ * disk.
+ * r5 - Optional pointer to the physical ending address of the init RAM
+ * disk.
+ * r6 - Optional pointer to the physical starting address of any kernel
+ * command-line parameters.
+ * r7 - Optional pointer to the physical ending address of any kernel
+ * command-line parameters.
+ */
+void __init
+ppc4xx_init(unsigned long r3, unsigned long r4, unsigned long r5,
+ unsigned long r6, unsigned long r7)
+{
+ parse_bootinfo(find_bootinfo());
+
+ /*
+ * If we were passed in a board information, copy it into the
+ * residual data area.
+ */
+ if (r3)
+ __res = *(bd_t *)(r3 + KERNELBASE);
+
+#if defined(CONFIG_BLK_DEV_INITRD)
+ /*
+ * If the init RAM disk has been configured in, and there's a valid
+ * starting address for it, set it up.
+ */
+ if (r4) {
+ initrd_start = r4 + KERNELBASE;
+ initrd_end = r5 + KERNELBASE;
+ }
+#endif /* CONFIG_BLK_DEV_INITRD */
+
+ /* Copy the kernel command line arguments to a safe place. */
+
+ if (r6) {
+ *(char *) (r7 + KERNELBASE) = 0;
+ strcpy(cmd_line, (char *) (r6 + KERNELBASE));
+ }
+#if defined(CONFIG_PPC405_WDT)
+/* Look for wdt= option on command line */
+ if (strstr(cmd_line, "wdt=")) {
+ int valid_wdt = 0;
+ char *p, *q;
+ for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) {
+ q = p + 4;
+ if (p > cmd_line && p[-1] != ' ')
+ continue;
+ wdt_period = simple_strtoul(q, &q, 0);
+ valid_wdt = 1;
+ ++q;
+ }
+ wdt_enable = valid_wdt;
+ }
+#endif
+
+ /* Initialize machine-dependent vectors */
+
+ ppc_md.setup_arch = ppc4xx_setup_arch;
+ ppc_md.show_percpuinfo = ppc4xx_show_percpuinfo;
+ ppc_md.show_cpuinfo = ppc4xx_show_cpuinfo;
+ ppc_md.init_IRQ = ppc4xx_init_IRQ;
+
+ ppc_md.restart = ppc4xx_restart;
+ ppc_md.power_off = ppc4xx_power_off;
+ ppc_md.halt = ppc4xx_halt;
+
+ ppc_md.calibrate_decr = ppc4xx_calibrate_decr;
+
+#ifdef CONFIG_PPC405_WDT
+ ppc_md.heartbeat = ppc4xx_wdt_heartbeat;
+#endif
+ ppc_md.heartbeat_count = 0;
+
+ ppc_md.find_end_of_memory = ppc4xx_find_end_of_memory;
+ ppc_md.setup_io_mappings = ppc4xx_map_io;
+
+#ifdef CONFIG_SERIAL_TEXT_DEBUG
+ ppc_md.progress = gen550_progress;
+#endif
+
+#if defined(CONFIG_PCI) && defined(CONFIG_IDE)
+ ppc_ide_md.ide_init_hwif = ppc4xx_ide_init_hwif_ports;
+#endif /* defined(CONFIG_PCI) && defined(CONFIG_IDE) */
+}
+
+/* Called from MachineCheckException */
+void platform_machine_check(struct pt_regs *regs)
+{
+#if defined(DCRN_PLB0_BEAR)
+ printk("PLB0: BEAR= 0x%08x ACR= 0x%08x BESR= 0x%08x\n",
+ mfdcr(DCRN_PLB0_BEAR), mfdcr(DCRN_PLB0_ACR),
+ mfdcr(DCRN_PLB0_BESR));
+#endif
+#if defined(DCRN_POB0_BEAR)
+ printk("PLB0 to OPB: BEAR= 0x%08x BESR0= 0x%08x BESR1= 0x%08x\n",
+ mfdcr(DCRN_POB0_BEAR), mfdcr(DCRN_POB0_BESR0),
+ mfdcr(DCRN_POB0_BESR1));
+#endif
+
+}
diff --git a/arch/ppc/syslib/ppc4xx_sgdma.c b/arch/ppc/syslib/ppc4xx_sgdma.c
new file mode 100644
index 000000000000..9f76e8ee39ed
--- /dev/null
+++ b/arch/ppc/syslib/ppc4xx_sgdma.c
@@ -0,0 +1,467 @@
+/*
+ * arch/ppc/kernel/ppc4xx_sgdma.c
+ *
+ * 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/config.h>
+#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>
+
+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
+
+ 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);
+ }
+ 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;
+
+ 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 */
+ sg_command = mfdcr(DCRN_ASGC);
+
+ ppc4xx_set_sg_addr(psgl->dmanr, psgl->phead_dma);
+
+ sg_command |= SSG_ENABLE(psgl->dmanr);
+
+ mtdcr(DCRN_ASGC, sg_command); /* start transfer */
+}
+
+/*
+ * Halt an active scatter/gather DMA operation.
+ */
+void
+ppc4xx_disable_dma_sgl(sgl_handle_t handle)
+{
+ sgl_list_info_t *psgl = (sgl_list_info_t *) handle;
+ 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;
+ }
+
+ sg_command = mfdcr(DCRN_ASGC);
+ sg_command &= ~SSG_ENABLE(psgl->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.
+ */
+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;
+
+ 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;
+}
+
+/*
+ * 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");
+ *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 */
+ psgl->phead = NULL;
+ psgl->ptail = NULL;
+ } else {
+ psgl->phead++;
+ psgl->phead_dma += sizeof(ppc_sgl_t);
+ }
+
+ 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.
+ */
+int
+ppc4xx_alloc_dma_handle(sgl_handle_t * phandle, unsigned int mode, unsigned int dmanr)
+{
+ sgl_list_info_t *psgl=NULL;
+ dma_addr_t dma_addr;
+ ppc_dma_ch_t *p_dma_ch = &dma_channels[dmanr];
+ uint32_t sg_command;
+ uint32_t ctc_settings;
+ void *ret;
+
+ 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 pointer\n");
+ return DMA_STATUS_NULL_POINTER;
+ }
+
+ /* 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;
+
+ /*
+ * 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);
+
+ /* 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;
+ }
+
+ sg_command = mfdcr(DCRN_ASGC);
+ sg_command |= SSG_MASK_ENABLE(dmanr);
+
+ /* Enable SGL control access */
+ mtdcr(DCRN_ASGC, sg_command);
+ psgl->sgl_control = SG_ERI_ENABLE | SG_LINK;
+
+ /* 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;
+
+ 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;
+
+ 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);
+}
+
+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_get_dma_sgl_residue);
diff --git a/arch/ppc/syslib/ppc83xx_setup.c b/arch/ppc/syslib/ppc83xx_setup.c
new file mode 100644
index 000000000000..c28f9d679484
--- /dev/null
+++ b/arch/ppc/syslib/ppc83xx_setup.c
@@ -0,0 +1,138 @@
+/*
+ * arch/ppc/syslib/ppc83xx_setup.c
+ *
+ * MPC83XX common board code
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor Inc.
+ *
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/serial.h>
+#include <linux/tty.h> /* for linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+
+#include <asm/prom.h>
+#include <asm/time.h>
+#include <asm/mpc83xx.h>
+#include <asm/mmu.h>
+#include <asm/ppc_sys.h>
+#include <asm/kgdb.h>
+
+#include <syslib/ppc83xx_setup.h>
+
+phys_addr_t immrbar;
+
+/* Return the amount of memory */
+unsigned long __init
+mpc83xx_find_end_of_memory(void)
+{
+ bd_t *binfo;
+
+ binfo = (bd_t *) __res;
+
+ return binfo->bi_memsize;
+}
+
+long __init
+mpc83xx_time_init(void)
+{
+#define SPCR_OFFS 0x00000110
+#define SPCR_TBEN 0x00400000
+
+ bd_t *binfo = (bd_t *)__res;
+ u32 *spcr = ioremap(binfo->bi_immr_base + SPCR_OFFS, 4);
+
+ *spcr |= SPCR_TBEN;
+
+ iounmap(spcr);
+
+ return 0;
+}
+
+/* The decrementer counts at the system (internal) clock freq divided by 4 */
+void __init
+mpc83xx_calibrate_decr(void)
+{
+ bd_t *binfo = (bd_t *) __res;
+ unsigned int freq, divisor;
+
+ freq = binfo->bi_busfreq;
+ divisor = 4;
+ tb_ticks_per_jiffy = freq / HZ / divisor;
+ tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
+}
+
+#ifdef CONFIG_SERIAL_8250
+void __init
+mpc83xx_early_serial_map(void)
+{
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+ struct uart_port serial_req;
+#endif
+ struct plat_serial8250_port *pdata;
+ bd_t *binfo = (bd_t *) __res;
+ pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC83xx_DUART);
+
+ /* Setup serial port access */
+ pdata[0].uartclk = binfo->bi_busfreq;
+ pdata[0].mapbase += binfo->bi_immr_base;
+ pdata[0].membase = ioremap(pdata[0].mapbase, 0x100);
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+ memset(&serial_req, 0, sizeof (serial_req));
+ serial_req.iotype = SERIAL_IO_MEM;
+ serial_req.mapbase = pdata[0].mapbase;
+ serial_req.membase = pdata[0].membase;
+ serial_req.regshift = 0;
+
+ gen550_init(0, &serial_req);
+#endif
+
+ pdata[1].uartclk = binfo->bi_busfreq;
+ pdata[1].mapbase += binfo->bi_immr_base;
+ pdata[1].membase = ioremap(pdata[1].mapbase, 0x100);
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+ /* Assume gen550_init() doesn't modify serial_req */
+ serial_req.mapbase = pdata[1].mapbase;
+ serial_req.membase = pdata[1].membase;
+
+ gen550_init(1, &serial_req);
+#endif
+}
+#endif
+
+void
+mpc83xx_restart(char *cmd)
+{
+ local_irq_disable();
+ for(;;);
+}
+
+void
+mpc83xx_power_off(void)
+{
+ local_irq_disable();
+ for(;;);
+}
+
+void
+mpc83xx_halt(void)
+{
+ local_irq_disable();
+ for(;;);
+}
+
+/* PCI SUPPORT DOES NOT EXIT, MODEL after ppc85xx_setup.c */
diff --git a/arch/ppc/syslib/ppc83xx_setup.h b/arch/ppc/syslib/ppc83xx_setup.h
new file mode 100644
index 000000000000..683f179b746c
--- /dev/null
+++ b/arch/ppc/syslib/ppc83xx_setup.h
@@ -0,0 +1,53 @@
+/*
+ * arch/ppc/syslib/ppc83xx_setup.h
+ *
+ * MPC83XX common board definitions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __PPC_SYSLIB_PPC83XX_SETUP_H
+#define __PPC_SYSLIB_PPC83XX_SETUP_H
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/ppcboot.h>
+
+extern unsigned long mpc83xx_find_end_of_memory(void) __init;
+extern long mpc83xx_time_init(void) __init;
+extern void mpc83xx_calibrate_decr(void) __init;
+extern void mpc83xx_early_serial_map(void) __init;
+extern void mpc83xx_restart(char *cmd);
+extern void mpc83xx_power_off(void);
+extern void mpc83xx_halt(void);
+extern void mpc83xx_setup_hose(void) __init;
+
+/* PCI config */
+#if 0
+#define PCI1_CFG_ADDR_OFFSET (FIXME)
+#define PCI1_CFG_DATA_OFFSET (FIXME)
+
+#define PCI2_CFG_ADDR_OFFSET (FIXME)
+#define PCI2_CFG_DATA_OFFSET (FIXME)
+#endif
+
+/* Serial Config */
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define RS_TABLE_SIZE 64
+#else
+#define RS_TABLE_SIZE 2
+#endif
+
+#ifndef BASE_BAUD
+#define BASE_BAUD 115200
+#endif
+
+#endif /* __PPC_SYSLIB_PPC83XX_SETUP_H */
diff --git a/arch/ppc/syslib/ppc85xx_common.c b/arch/ppc/syslib/ppc85xx_common.c
new file mode 100644
index 000000000000..e83f2f8686d3
--- /dev/null
+++ b/arch/ppc/syslib/ppc85xx_common.c
@@ -0,0 +1,33 @@
+/*
+ * arch/ppc/syslib/ppc85xx_common.c
+ *
+ * MPC85xx support routines
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/mpc85xx.h>
+#include <asm/mmu.h>
+
+/* ************************************************************************ */
+/* Return the value of CCSRBAR for the current board */
+
+phys_addr_t
+get_ccsrbar(void)
+{
+ return BOARD_CCSRBAR;
+}
+
+EXPORT_SYMBOL(get_ccsrbar);
diff --git a/arch/ppc/syslib/ppc85xx_common.h b/arch/ppc/syslib/ppc85xx_common.h
new file mode 100644
index 000000000000..2c8f304441bf
--- /dev/null
+++ b/arch/ppc/syslib/ppc85xx_common.h
@@ -0,0 +1,25 @@
+/*
+ * arch/ppc/syslib/ppc85xx_common.h
+ *
+ * MPC85xx support routines
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __PPC_SYSLIB_PPC85XX_COMMON_H
+#define __PPC_SYSLIB_PPC85XX_COMMON_H
+
+#include <linux/config.h>
+#include <linux/init.h>
+
+/* Provide access to ccsrbar for any modules, etc */
+phys_addr_t get_ccsrbar(void);
+
+#endif /* __PPC_SYSLIB_PPC85XX_COMMON_H */
diff --git a/arch/ppc/syslib/ppc85xx_setup.c b/arch/ppc/syslib/ppc85xx_setup.c
new file mode 100644
index 000000000000..81f1968c3269
--- /dev/null
+++ b/arch/ppc/syslib/ppc85xx_setup.c
@@ -0,0 +1,354 @@
+/*
+ * arch/ppc/syslib/ppc85xx_setup.c
+ *
+ * MPC85XX common board code
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * 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/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/serial.h>
+#include <linux/tty.h> /* for linux/serial_core.h */
+#include <linux/serial_core.h>
+#include <linux/serial_8250.h>
+
+#include <asm/prom.h>
+#include <asm/time.h>
+#include <asm/mpc85xx.h>
+#include <asm/immap_85xx.h>
+#include <asm/mmu.h>
+#include <asm/ppc_sys.h>
+#include <asm/kgdb.h>
+
+#include <syslib/ppc85xx_setup.h>
+
+/* Return the amount of memory */
+unsigned long __init
+mpc85xx_find_end_of_memory(void)
+{
+ bd_t *binfo;
+
+ binfo = (bd_t *) __res;
+
+ return binfo->bi_memsize;
+}
+
+/* The decrementer counts at the system (internal) clock freq divided by 8 */
+void __init
+mpc85xx_calibrate_decr(void)
+{
+ bd_t *binfo = (bd_t *) __res;
+ unsigned int freq, divisor;
+
+ /* get the core frequency */
+ freq = binfo->bi_busfreq;
+
+ /* The timebase is updated every 8 bus clocks, HID0[SEL_TBCLK] = 0 */
+ divisor = 8;
+ tb_ticks_per_jiffy = freq / divisor / HZ;
+ tb_to_us = mulhwu_scale_factor(freq / divisor, 1000000);
+
+ /* Set the time base to zero */
+ mtspr(SPRN_TBWL, 0);
+ mtspr(SPRN_TBWU, 0);
+
+ /* Clear any pending timer interrupts */
+ mtspr(SPRN_TSR, TSR_ENW | TSR_WIS | TSR_DIS | TSR_FIS);
+
+ /* Enable decrementer interrupt */
+ mtspr(SPRN_TCR, TCR_DIE);
+}
+
+#ifdef CONFIG_SERIAL_8250
+void __init
+mpc85xx_early_serial_map(void)
+{
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+ struct uart_port serial_req;
+#endif
+ struct plat_serial8250_port *pdata;
+ bd_t *binfo = (bd_t *) __res;
+ pdata = (struct plat_serial8250_port *) ppc_sys_get_pdata(MPC85xx_DUART);
+
+ /* Setup serial port access */
+ pdata[0].uartclk = binfo->bi_busfreq;
+ pdata[0].mapbase += binfo->bi_immr_base;
+ pdata[0].membase = ioremap(pdata[0].mapbase, MPC85xx_UART0_SIZE);
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+ memset(&serial_req, 0, sizeof (serial_req));
+ serial_req.iotype = SERIAL_IO_MEM;
+ serial_req.mapbase = pdata[0].mapbase;
+ serial_req.membase = pdata[0].membase;
+ serial_req.regshift = 0;
+
+ gen550_init(0, &serial_req);
+#endif
+
+ pdata[1].uartclk = binfo->bi_busfreq;
+ pdata[1].mapbase += binfo->bi_immr_base;
+ pdata[1].membase = ioremap(pdata[1].mapbase, MPC85xx_UART0_SIZE);
+
+#if defined(CONFIG_SERIAL_TEXT_DEBUG) || defined(CONFIG_KGDB)
+ /* Assume gen550_init() doesn't modify serial_req */
+ serial_req.mapbase = pdata[1].mapbase;
+ serial_req.membase = pdata[1].membase;
+
+ gen550_init(1, &serial_req);
+#endif
+}
+#endif
+
+void
+mpc85xx_restart(char *cmd)
+{
+ local_irq_disable();
+ abort();
+}
+
+void
+mpc85xx_power_off(void)
+{
+ local_irq_disable();
+ for(;;);
+}
+
+void
+mpc85xx_halt(void)
+{
+ local_irq_disable();
+ for(;;);
+}
+
+#ifdef CONFIG_PCI
+static void __init
+mpc85xx_setup_pci1(struct pci_controller *hose)
+{
+ volatile struct ccsr_pci *pci;
+ volatile struct ccsr_guts *guts;
+ unsigned short temps;
+ bd_t *binfo = (bd_t *) __res;
+
+ pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI1_OFFSET,
+ MPC85xx_PCI1_SIZE);
+
+ guts = ioremap(binfo->bi_immr_base + MPC85xx_GUTS_OFFSET,
+ MPC85xx_GUTS_SIZE);
+
+ early_read_config_word(hose, 0, 0, PCI_COMMAND, &temps);
+ temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ early_write_config_word(hose, 0, 0, PCI_COMMAND, temps);
+
+#define PORDEVSR_PCI (0x00800000) /* PCI Mode */
+ if (guts->pordevsr & PORDEVSR_PCI) {
+ early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80);
+ } else {
+ /* PCI-X init */
+ temps = PCI_X_CMD_MAX_SPLIT | PCI_X_CMD_MAX_READ
+ | PCI_X_CMD_ERO | PCI_X_CMD_DPERR_E;
+ early_write_config_word(hose, 0, 0, PCIX_COMMAND, temps);
+ }
+
+ /* Disable all windows (except powar0 since its ignored) */
+ pci->powar1 = 0;
+ pci->powar2 = 0;
+ pci->powar3 = 0;
+ pci->powar4 = 0;
+ pci->piwar1 = 0;
+ pci->piwar2 = 0;
+ pci->piwar3 = 0;
+
+ /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI1_LOWER_MEM */
+ pci->potar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff;
+ pci->potear1 = 0x00000000;
+ pci->powbar1 = (MPC85XX_PCI1_LOWER_MEM >> 12) & 0x000fffff;
+ /* Enable, Mem R/W */
+ pci->powar1 = 0x80044000 |
+ (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1);
+
+ /* Setup outboud IO windows @ MPC85XX_PCI1_IO_BASE */
+ pci->potar2 = 0x00000000;
+ pci->potear2 = 0x00000000;
+ pci->powbar2 = (MPC85XX_PCI1_IO_BASE >> 12) & 0x000fffff;
+ /* Enable, IO R/W */
+ pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1);
+
+ /* Setup 2G inbound Memory Window @ 0 */
+ pci->pitar1 = 0x00000000;
+ pci->piwbar1 = 0x00000000;
+ pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local
+ Mem, Snoop R/W, 2G */
+}
+
+
+extern int mpc85xx_map_irq(struct pci_dev *dev, unsigned char idsel, unsigned char pin);
+extern int mpc85xx_exclude_device(u_char bus, u_char devfn);
+
+#ifdef CONFIG_85xx_PCI2
+static void __init
+mpc85xx_setup_pci2(struct pci_controller *hose)
+{
+ volatile struct ccsr_pci *pci;
+ unsigned short temps;
+ bd_t *binfo = (bd_t *) __res;
+
+ pci = ioremap(binfo->bi_immr_base + MPC85xx_PCI2_OFFSET,
+ MPC85xx_PCI2_SIZE);
+
+ early_read_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, &temps);
+ temps |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY;
+ early_write_config_word(hose, hose->bus_offset, 0, PCI_COMMAND, temps);
+ early_write_config_byte(hose, hose->bus_offset, 0, PCI_LATENCY_TIMER, 0x80);
+
+ /* Disable all windows (except powar0 since its ignored) */
+ pci->powar1 = 0;
+ pci->powar2 = 0;
+ pci->powar3 = 0;
+ pci->powar4 = 0;
+ pci->piwar1 = 0;
+ pci->piwar2 = 0;
+ pci->piwar3 = 0;
+
+ /* Setup Phys:PCI 1:1 outbound mem window @ MPC85XX_PCI2_LOWER_MEM */
+ pci->potar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff;
+ pci->potear1 = 0x00000000;
+ pci->powbar1 = (MPC85XX_PCI2_LOWER_MEM >> 12) & 0x000fffff;
+ /* Enable, Mem R/W */
+ pci->powar1 = 0x80044000 |
+ (__ilog2(MPC85XX_PCI1_UPPER_MEM - MPC85XX_PCI1_LOWER_MEM + 1) - 1);
+
+ /* Setup outboud IO windows @ MPC85XX_PCI2_IO_BASE */
+ pci->potar2 = 0x00000000;
+ pci->potear2 = 0x00000000;
+ pci->powbar2 = (MPC85XX_PCI2_IO_BASE >> 12) & 0x000fffff;
+ /* Enable, IO R/W */
+ pci->powar2 = 0x80088000 | (__ilog2(MPC85XX_PCI1_IO_SIZE) - 1);
+
+ /* Setup 2G inbound Memory Window @ 0 */
+ pci->pitar1 = 0x00000000;
+ pci->piwbar1 = 0x00000000;
+ pci->piwar1 = 0xa0f5501e; /* Enable, Prefetch, Local
+ Mem, Snoop R/W, 2G */
+}
+#endif /* CONFIG_85xx_PCI2 */
+
+int mpc85xx_pci1_last_busno = 0;
+
+void __init
+mpc85xx_setup_hose(void)
+{
+ struct pci_controller *hose_a;
+#ifdef CONFIG_85xx_PCI2
+ struct pci_controller *hose_b;
+#endif
+ bd_t *binfo = (bd_t *) __res;
+
+ hose_a = pcibios_alloc_controller();
+
+ if (!hose_a)
+ return;
+
+ ppc_md.pci_swizzle = common_swizzle;
+ ppc_md.pci_map_irq = mpc85xx_map_irq;
+
+ hose_a->first_busno = 0;
+ hose_a->bus_offset = 0;
+ hose_a->last_busno = 0xff;
+
+ setup_indirect_pci(hose_a, binfo->bi_immr_base + PCI1_CFG_ADDR_OFFSET,
+ binfo->bi_immr_base + PCI1_CFG_DATA_OFFSET);
+ hose_a->set_cfg_type = 1;
+
+ mpc85xx_setup_pci1(hose_a);
+
+ hose_a->pci_mem_offset = MPC85XX_PCI1_MEM_OFFSET;
+ hose_a->mem_space.start = MPC85XX_PCI1_LOWER_MEM;
+ hose_a->mem_space.end = MPC85XX_PCI1_UPPER_MEM;
+
+ hose_a->io_space.start = MPC85XX_PCI1_LOWER_IO;
+ hose_a->io_space.end = MPC85XX_PCI1_UPPER_IO;
+ hose_a->io_base_phys = MPC85XX_PCI1_IO_BASE;
+#ifdef CONFIG_85xx_PCI2
+ isa_io_base =
+ (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE,
+ MPC85XX_PCI1_IO_SIZE +
+ MPC85XX_PCI2_IO_SIZE);
+#else
+ isa_io_base =
+ (unsigned long) ioremap(MPC85XX_PCI1_IO_BASE,
+ MPC85XX_PCI1_IO_SIZE);
+#endif
+ hose_a->io_base_virt = (void *) isa_io_base;
+
+ /* setup resources */
+ pci_init_resource(&hose_a->mem_resources[0],
+ MPC85XX_PCI1_LOWER_MEM,
+ MPC85XX_PCI1_UPPER_MEM,
+ IORESOURCE_MEM, "PCI1 host bridge");
+
+ pci_init_resource(&hose_a->io_resource,
+ MPC85XX_PCI1_LOWER_IO,
+ MPC85XX_PCI1_UPPER_IO,
+ IORESOURCE_IO, "PCI1 host bridge");
+
+ ppc_md.pci_exclude_device = mpc85xx_exclude_device;
+
+ hose_a->last_busno = pciauto_bus_scan(hose_a, hose_a->first_busno);
+
+#ifdef CONFIG_85xx_PCI2
+ hose_b = pcibios_alloc_controller();
+
+ if (!hose_b)
+ return;
+
+ hose_b->bus_offset = hose_a->last_busno + 1;
+ hose_b->first_busno = hose_a->last_busno + 1;
+ hose_b->last_busno = 0xff;
+
+ setup_indirect_pci(hose_b, binfo->bi_immr_base + PCI2_CFG_ADDR_OFFSET,
+ binfo->bi_immr_base + PCI2_CFG_DATA_OFFSET);
+ hose_b->set_cfg_type = 1;
+
+ mpc85xx_setup_pci2(hose_b);
+
+ hose_b->pci_mem_offset = MPC85XX_PCI2_MEM_OFFSET;
+ hose_b->mem_space.start = MPC85XX_PCI2_LOWER_MEM;
+ hose_b->mem_space.end = MPC85XX_PCI2_UPPER_MEM;
+
+ hose_b->io_space.start = MPC85XX_PCI2_LOWER_IO;
+ hose_b->io_space.end = MPC85XX_PCI2_UPPER_IO;
+ hose_b->io_base_phys = MPC85XX_PCI2_IO_BASE;
+ hose_b->io_base_virt = (void *) isa_io_base + MPC85XX_PCI1_IO_SIZE;
+
+ /* setup resources */
+ pci_init_resource(&hose_b->mem_resources[0],
+ MPC85XX_PCI2_LOWER_MEM,
+ MPC85XX_PCI2_UPPER_MEM,
+ IORESOURCE_MEM, "PCI2 host bridge");
+
+ pci_init_resource(&hose_b->io_resource,
+ MPC85XX_PCI2_LOWER_IO,
+ MPC85XX_PCI2_UPPER_IO,
+ IORESOURCE_IO, "PCI2 host bridge");
+
+ hose_b->last_busno = pciauto_bus_scan(hose_b, hose_b->first_busno);
+
+ /* let board code know what the last bus number was on PCI1 */
+ mpc85xx_pci1_last_busno = hose_a->last_busno;
+#endif
+ return;
+}
+#endif /* CONFIG_PCI */
+
+
diff --git a/arch/ppc/syslib/ppc85xx_setup.h b/arch/ppc/syslib/ppc85xx_setup.h
new file mode 100644
index 000000000000..6e6cfe162faf
--- /dev/null
+++ b/arch/ppc/syslib/ppc85xx_setup.h
@@ -0,0 +1,59 @@
+/*
+ * arch/ppc/syslib/ppc85xx_setup.h
+ *
+ * MPC85XX common board definitions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2004 Freescale Semiconductor Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __PPC_SYSLIB_PPC85XX_SETUP_H
+#define __PPC_SYSLIB_PPC85XX_SETUP_H
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <asm/ppcboot.h>
+
+extern unsigned long mpc85xx_find_end_of_memory(void) __init;
+extern void mpc85xx_calibrate_decr(void) __init;
+extern void mpc85xx_early_serial_map(void) __init;
+extern void mpc85xx_restart(char *cmd);
+extern void mpc85xx_power_off(void);
+extern void mpc85xx_halt(void);
+extern void mpc85xx_setup_hose(void) __init;
+
+/* PCI config */
+#define PCI1_CFG_ADDR_OFFSET (0x8000)
+#define PCI1_CFG_DATA_OFFSET (0x8004)
+
+#define PCI2_CFG_ADDR_OFFSET (0x9000)
+#define PCI2_CFG_DATA_OFFSET (0x9004)
+
+/* Additional register for PCI-X configuration */
+#define PCIX_NEXT_CAP 0x60
+#define PCIX_CAP_ID 0x61
+#define PCIX_COMMAND 0x62
+#define PCIX_STATUS 0x64
+
+/* Serial Config */
+#ifdef CONFIG_SERIAL_MANY_PORTS
+#define RS_TABLE_SIZE 64
+#else
+#define RS_TABLE_SIZE 2
+#endif
+
+#ifndef BASE_BAUD
+#define BASE_BAUD 115200
+#endif
+
+/* Offset of CPM register space */
+#define CPM_MAP_ADDR (CCSRBAR + MPC85xx_CPM_OFFSET)
+
+#endif /* __PPC_SYSLIB_PPC85XX_SETUP_H */
diff --git a/arch/ppc/syslib/ppc8xx_pic.c b/arch/ppc/syslib/ppc8xx_pic.c
new file mode 100644
index 000000000000..d3b01c6c97de
--- /dev/null
+++ b/arch/ppc/syslib/ppc8xx_pic.c
@@ -0,0 +1,130 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/interrupt.h>
+#include <asm/irq.h>
+#include <asm/8xx_immap.h>
+#include <asm/mpc8xx.h>
+#include "ppc8xx_pic.h"
+
+extern int cpm_get_irq(struct pt_regs *regs);
+
+/* The 8xx internal interrupt controller. It is usually
+ * the only interrupt controller. Some boards, like the MBX and
+ * Sandpoint have the 8259 as a secondary controller. Depending
+ * upon the processor type, the internal controller can have as
+ * few as 16 interrups or as many as 64. We could use the
+ * "clear_bit()" and "set_bit()" functions like other platforms,
+ * but they are overkill for us.
+ */
+
+static void m8xx_mask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+
+ bit = irq_nr & 0x1f;
+ word = irq_nr >> 5;
+
+ ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
+ ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
+ ppc_cached_irq_mask[word];
+}
+
+static void m8xx_unmask_irq(unsigned int irq_nr)
+{
+ int bit, word;
+
+ bit = irq_nr & 0x1f;
+ word = irq_nr >> 5;
+
+ ppc_cached_irq_mask[word] |= (1 << (31-bit));
+ ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
+ ppc_cached_irq_mask[word];
+}
+
+static void m8xx_end_irq(unsigned int irq_nr)
+{
+ if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))
+ && irq_desc[irq_nr].action) {
+ int bit, word;
+
+ bit = irq_nr & 0x1f;
+ word = irq_nr >> 5;
+
+ ppc_cached_irq_mask[word] |= (1 << (31-bit));
+ ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
+ ppc_cached_irq_mask[word];
+ }
+}
+
+
+static void m8xx_mask_and_ack(unsigned int irq_nr)
+{
+ int bit, word;
+
+ bit = irq_nr & 0x1f;
+ word = irq_nr >> 5;
+
+ ppc_cached_irq_mask[word] &= ~(1 << (31-bit));
+ ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_simask =
+ ppc_cached_irq_mask[word];
+ ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sipend = 1 << (31-bit);
+}
+
+struct hw_interrupt_type ppc8xx_pic = {
+ .typename = " 8xx SIU ",
+ .enable = m8xx_unmask_irq,
+ .disable = m8xx_mask_irq,
+ .ack = m8xx_mask_and_ack,
+ .end = m8xx_end_irq,
+};
+
+/*
+ * We either return a valid interrupt or -1 if there is nothing pending
+ */
+int
+m8xx_get_irq(struct pt_regs *regs)
+{
+ int irq;
+
+ /* For MPC8xx, read the SIVEC register and shift the bits down
+ * to get the irq number.
+ */
+ irq = ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_sivec >> 26;
+
+ /*
+ * When we read the sivec without an interrupt to process, we will
+ * get back SIU_LEVEL7. In this case, return -1
+ */
+ if (irq == CPM_INTERRUPT)
+ irq = CPM_IRQ_OFFSET + cpm_get_irq(regs);
+#if defined(CONFIG_PCI)
+ else if (irq == ISA_BRIDGE_INT) {
+ int isa_irq;
+
+ if ((isa_irq = i8259_poll(regs)) >= 0)
+ irq = I8259_IRQ_OFFSET + isa_irq;
+ }
+#endif /* CONFIG_PCI */
+ else if (irq == SIU_LEVEL7)
+ irq = -1;
+
+ return irq;
+}
+
+#if defined(CONFIG_MBX) && defined(CONFIG_PCI)
+/* Only the MBX uses the external 8259. This allows us to catch standard
+ * drivers that may mess up the internal interrupt controllers, and also
+ * allow them to run without modification on the MBX.
+ */
+void mbx_i8259_action(int irq, void *dev_id, struct pt_regs *regs)
+{
+ /* This interrupt handler never actually gets called. It is
+ * installed only to unmask the 8259 cascade interrupt in the SIU
+ * and to make the 8259 cascade interrupt visible in /proc/interrupts.
+ */
+}
+#endif /* CONFIG_PCI */
diff --git a/arch/ppc/syslib/ppc8xx_pic.h b/arch/ppc/syslib/ppc8xx_pic.h
new file mode 100644
index 000000000000..784935eac365
--- /dev/null
+++ b/arch/ppc/syslib/ppc8xx_pic.h
@@ -0,0 +1,21 @@
+#ifndef _PPC_KERNEL_PPC8xx_H
+#define _PPC_KERNEL_PPC8xx_H
+
+#include <linux/config.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+
+extern struct hw_interrupt_type ppc8xx_pic;
+
+void m8xx_pic_init(void);
+void m8xx_do_IRQ(struct pt_regs *regs,
+ int cpu);
+int m8xx_get_irq(struct pt_regs *regs);
+
+#ifdef CONFIG_MBX
+#include <asm/i8259.h>
+#include <asm/io.h>
+void mbx_i8259_action(int cpl, void *dev_id, struct pt_regs *regs);
+#endif
+
+#endif /* _PPC_KERNEL_PPC8xx_H */
diff --git a/arch/ppc/syslib/ppc_sys.c b/arch/ppc/syslib/ppc_sys.c
new file mode 100644
index 000000000000..879202352560
--- /dev/null
+++ b/arch/ppc/syslib/ppc_sys.c
@@ -0,0 +1,103 @@
+/*
+ * arch/ppc/syslib/ppc_sys.c
+ *
+ * PPC System library functions
+ *
+ * Maintainer: Kumar Gala <kumar.gala@freescale.com>
+ *
+ * Copyright 2005 Freescale Semiconductor Inc.
+ *
+ * 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 <asm/ppc_sys.h>
+
+int (*ppc_sys_device_fixup) (struct platform_device * pdev);
+
+static int ppc_sys_inited;
+
+void __init identify_ppc_sys_by_id(u32 id)
+{
+ unsigned int i = 0;
+ while (1) {
+ if ((ppc_sys_specs[i].mask & id) == ppc_sys_specs[i].value)
+ break;
+ i++;
+ }
+
+ cur_ppc_sys_spec = &ppc_sys_specs[i];
+
+ return;
+}
+
+void __init identify_ppc_sys_by_name(char *name)
+{
+ /* TODO */
+ return;
+}
+
+/* Update all memory resources by paddr, call before platform_device_register */
+void __init
+ppc_sys_fixup_mem_resource(struct platform_device *pdev, phys_addr_t paddr)
+{
+ int i;
+ for (i = 0; i < pdev->num_resources; i++) {
+ struct resource *r = &pdev->resource[i];
+ if ((r->flags & IORESOURCE_MEM) == IORESOURCE_MEM) {
+ r->start += paddr;
+ r->end += paddr;
+ }
+ }
+}
+
+/* Get platform_data pointer out of platform device, call before platform_device_register */
+void *__init ppc_sys_get_pdata(enum ppc_sys_devices dev)
+{
+ return ppc_sys_platform_devices[dev].dev.platform_data;
+}
+
+void ppc_sys_device_remove(enum ppc_sys_devices dev)
+{
+ unsigned int i;
+
+ if (ppc_sys_inited) {
+ platform_device_unregister(&ppc_sys_platform_devices[dev]);
+ } else {
+ if (cur_ppc_sys_spec == NULL)
+ return;
+ for (i = 0; i < cur_ppc_sys_spec->num_devices; i++)
+ if (cur_ppc_sys_spec->device_list[i] == dev)
+ cur_ppc_sys_spec->device_list[i] = -1;
+ }
+}
+
+static int __init ppc_sys_init(void)
+{
+ unsigned int i, dev_id, ret = 0;
+
+ BUG_ON(cur_ppc_sys_spec == NULL);
+
+ for (i = 0; i < cur_ppc_sys_spec->num_devices; i++) {
+ dev_id = cur_ppc_sys_spec->device_list[i];
+ if (dev_id != -1) {
+ if (ppc_sys_device_fixup != NULL)
+ ppc_sys_device_fixup(&ppc_sys_platform_devices
+ [dev_id]);
+ if (platform_device_register
+ (&ppc_sys_platform_devices[dev_id])) {
+ ret = 1;
+ printk(KERN_ERR
+ "unable to register device %d\n",
+ dev_id);
+ }
+ }
+ }
+
+ ppc_sys_inited = 1;
+ return ret;
+}
+
+subsys_initcall(ppc_sys_init);
diff --git a/arch/ppc/syslib/prep_nvram.c b/arch/ppc/syslib/prep_nvram.c
new file mode 100644
index 000000000000..2bcf8a16d1c9
--- /dev/null
+++ b/arch/ppc/syslib/prep_nvram.c
@@ -0,0 +1,141 @@
+/*
+ * arch/ppc/kernel/prep_nvram.c
+ *
+ * Copyright (C) 1998 Corey Minyard
+ *
+ * This reads the NvRAM on PReP compliant machines (generally from IBM or
+ * Motorola). Motorola kept the format of NvRAM in their ROM, PPCBUG, the
+ * same, long after they had stopped producing PReP compliant machines. So
+ * this code is useful in those cases as well.
+ *
+ */
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+
+#include <asm/sections.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/machdep.h>
+#include <asm/prep_nvram.h>
+
+static char nvramData[MAX_PREP_NVRAM];
+static NVRAM_MAP *nvram=(NVRAM_MAP *)&nvramData[0];
+
+unsigned char __prep prep_nvram_read_val(int addr)
+{
+ outb(addr, PREP_NVRAM_AS0);
+ outb(addr>>8, PREP_NVRAM_AS1);
+ return inb(PREP_NVRAM_DATA);
+}
+
+void __prep prep_nvram_write_val(int addr,
+ unsigned char val)
+{
+ outb(addr, PREP_NVRAM_AS0);
+ outb(addr>>8, PREP_NVRAM_AS1);
+ outb(val, PREP_NVRAM_DATA);
+}
+
+void __init init_prep_nvram(void)
+{
+ unsigned char *nvp;
+ int i;
+ int nvramSize;
+
+ /*
+ * The following could fail if the NvRAM were corrupt but
+ * we expect the boot firmware to have checked its checksum
+ * before boot
+ */
+ nvp = (char *) &nvram->Header;
+ for (i=0; i<sizeof(HEADER); i++)
+ {
+ *nvp = ppc_md.nvram_read_val(i);
+ nvp++;
+ }
+
+ /*
+ * The PReP NvRAM may be any size so read in the header to
+ * determine how much we must read in order to get the complete
+ * GE area
+ */
+ nvramSize=(int)nvram->Header.GEAddress+nvram->Header.GELength;
+ if(nvramSize>MAX_PREP_NVRAM)
+ {
+ /*
+ * NvRAM is too large
+ */
+ nvram->Header.GELength=0;
+ return;
+ }
+
+ /*
+ * Read the remainder of the PReP NvRAM
+ */
+ nvp = (char *) &nvram->GEArea[0];
+ for (i=sizeof(HEADER); i<nvramSize; i++)
+ {
+ *nvp = ppc_md.nvram_read_val(i);
+ nvp++;
+ }
+}
+
+__prep
+char __prep *prep_nvram_get_var(const char *name)
+{
+ char *cp;
+ int namelen;
+
+ namelen = strlen(name);
+ cp = prep_nvram_first_var();
+ while (cp != NULL) {
+ if ((strncmp(name, cp, namelen) == 0)
+ && (cp[namelen] == '='))
+ {
+ return cp+namelen+1;
+ }
+ cp = prep_nvram_next_var(cp);
+ }
+
+ return NULL;
+}
+
+__prep
+char __prep *prep_nvram_first_var(void)
+{
+ if (nvram->Header.GELength == 0) {
+ return NULL;
+ } else {
+ return (((char *)nvram)
+ + ((unsigned int) nvram->Header.GEAddress));
+ }
+}
+
+__prep
+char __prep *prep_nvram_next_var(char *name)
+{
+ char *cp;
+
+
+ cp = name;
+ while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength)
+ && (*cp != '\0'))
+ {
+ cp++;
+ }
+
+ /* Skip over any null characters. */
+ while (((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength)
+ && (*cp == '\0'))
+ {
+ cp++;
+ }
+
+ if ((cp - ((char *) nvram->GEArea)) < nvram->Header.GELength) {
+ return cp;
+ } else {
+ return NULL;
+ }
+}
diff --git a/arch/ppc/syslib/prom.c b/arch/ppc/syslib/prom.c
new file mode 100644
index 000000000000..2c64ed627475
--- /dev/null
+++ b/arch/ppc/syslib/prom.c
@@ -0,0 +1,1447 @@
+/*
+ * Procedures for interfacing to the Open Firmware PROM on
+ * Power Macintosh computers.
+ *
+ * In particular, we are interested in the device tree
+ * and in using some of its services (exit, write to stdout).
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/threads.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/bootx.h>
+#include <asm/system.h>
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/bootinfo.h>
+#include <asm/btext.h>
+#include <asm/pci-bridge.h>
+#include <asm/open_pic.h>
+
+
+struct pci_address {
+ unsigned a_hi;
+ unsigned a_mid;
+ unsigned a_lo;
+};
+
+struct pci_reg_property {
+ struct pci_address addr;
+ unsigned size_hi;
+ unsigned size_lo;
+};
+
+struct isa_reg_property {
+ unsigned space;
+ unsigned address;
+ unsigned size;
+};
+
+typedef unsigned long interpret_func(struct device_node *, unsigned long,
+ int, int);
+static interpret_func interpret_pci_props;
+static interpret_func interpret_dbdma_props;
+static interpret_func interpret_isa_props;
+static interpret_func interpret_macio_props;
+static interpret_func interpret_root_props;
+
+extern char *klimit;
+
+/* Set for a newworld or CHRP machine */
+int use_of_interrupt_tree;
+struct device_node *dflt_interrupt_controller;
+int num_interrupt_controllers;
+
+int pmac_newworld;
+
+extern unsigned int rtas_entry; /* physical pointer */
+
+extern struct device_node *allnodes;
+
+static unsigned long finish_node(struct device_node *, unsigned long,
+ interpret_func *, int, int);
+static unsigned long finish_node_interrupts(struct device_node *, unsigned long);
+static struct device_node *find_phandle(phandle);
+
+extern void enter_rtas(void *);
+void phys_call_rtas(int, int, int, ...);
+
+extern char cmd_line[512]; /* XXX */
+extern boot_infos_t *boot_infos;
+unsigned long dev_tree_size;
+
+void __openfirmware
+phys_call_rtas(int service, int nargs, int nret, ...)
+{
+ va_list list;
+ union {
+ unsigned long words[16];
+ double align;
+ } u;
+ void (*rtas)(void *, unsigned long);
+ int i;
+
+ u.words[0] = service;
+ u.words[1] = nargs;
+ u.words[2] = nret;
+ va_start(list, nret);
+ for (i = 0; i < nargs; ++i)
+ u.words[i+3] = va_arg(list, unsigned long);
+ va_end(list);
+
+ rtas = (void (*)(void *, unsigned long)) rtas_entry;
+ rtas(&u, rtas_data);
+}
+
+/*
+ * finish_device_tree is called once things are running normally
+ * (i.e. with text and data mapped to the address they were linked at).
+ * It traverses the device tree and fills in the name, type,
+ * {n_}addrs and {n_}intrs fields of each node.
+ */
+void __init
+finish_device_tree(void)
+{
+ unsigned long mem = (unsigned long) klimit;
+ struct device_node *np;
+
+ /* All newworld pmac machines and CHRPs now use the interrupt tree */
+ for (np = allnodes; np != NULL; np = np->allnext) {
+ if (get_property(np, "interrupt-parent", NULL)) {
+ use_of_interrupt_tree = 1;
+ break;
+ }
+ }
+ if (_machine == _MACH_Pmac && use_of_interrupt_tree)
+ pmac_newworld = 1;
+
+#ifdef CONFIG_BOOTX_TEXT
+ if (boot_infos && pmac_newworld) {
+ prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n");
+ prom_print(" You should use an Open Firmware bootloader\n");
+ }
+#endif /* CONFIG_BOOTX_TEXT */
+
+ if (use_of_interrupt_tree) {
+ /*
+ * We want to find out here how many interrupt-controller
+ * nodes there are, and if we are booted from BootX,
+ * we need a pointer to the first (and hopefully only)
+ * such node. But we can't use find_devices here since
+ * np->name has not been set yet. -- paulus
+ */
+ int n = 0;
+ char *name, *ic;
+ int iclen;
+
+ for (np = allnodes; np != NULL; np = np->allnext) {
+ ic = get_property(np, "interrupt-controller", &iclen);
+ name = get_property(np, "name", NULL);
+ /* checking iclen makes sure we don't get a false
+ match on /chosen.interrupt_controller */
+ if ((name != NULL
+ && strcmp(name, "interrupt-controller") == 0)
+ || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) {
+ if (n == 0)
+ dflt_interrupt_controller = np;
+ ++n;
+ }
+ }
+ num_interrupt_controllers = n;
+ }
+
+ mem = finish_node(allnodes, mem, NULL, 1, 1);
+ dev_tree_size = mem - (unsigned long) allnodes;
+ klimit = (char *) mem;
+}
+
+static unsigned long __init
+finish_node(struct device_node *np, unsigned long mem_start,
+ interpret_func *ifunc, int naddrc, int nsizec)
+{
+ struct device_node *child;
+ int *ip;
+
+ np->name = get_property(np, "name", NULL);
+ np->type = get_property(np, "device_type", NULL);
+
+ if (!np->name)
+ np->name = "<NULL>";
+ if (!np->type)
+ np->type = "<NULL>";
+
+ /* get the device addresses and interrupts */
+ if (ifunc != NULL)
+ mem_start = ifunc(np, mem_start, naddrc, nsizec);
+
+ if (use_of_interrupt_tree)
+ mem_start = finish_node_interrupts(np, mem_start);
+
+ /* Look for #address-cells and #size-cells properties. */
+ ip = (int *) get_property(np, "#address-cells", NULL);
+ if (ip != NULL)
+ naddrc = *ip;
+ ip = (int *) get_property(np, "#size-cells", NULL);
+ if (ip != NULL)
+ nsizec = *ip;
+
+ if (np->parent == NULL)
+ ifunc = interpret_root_props;
+ else if (np->type == 0)
+ ifunc = NULL;
+ else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))
+ ifunc = interpret_pci_props;
+ else if (!strcmp(np->type, "dbdma"))
+ ifunc = interpret_dbdma_props;
+ else if (!strcmp(np->type, "mac-io")
+ || ifunc == interpret_macio_props)
+ ifunc = interpret_macio_props;
+ else if (!strcmp(np->type, "isa"))
+ ifunc = interpret_isa_props;
+ else if (!strcmp(np->name, "uni-n") || !strcmp(np->name, "u3"))
+ ifunc = interpret_root_props;
+ else if (!((ifunc == interpret_dbdma_props
+ || ifunc == interpret_macio_props)
+ && (!strcmp(np->type, "escc")
+ || !strcmp(np->type, "media-bay"))))
+ ifunc = NULL;
+
+ /* if we were booted from BootX, convert the full name */
+ if (boot_infos
+ && strncmp(np->full_name, "Devices:device-tree", 19) == 0) {
+ if (np->full_name[19] == 0) {
+ strcpy(np->full_name, "/");
+ } else if (np->full_name[19] == ':') {
+ char *p = np->full_name + 19;
+ np->full_name = p;
+ for (; *p; ++p)
+ if (*p == ':')
+ *p = '/';
+ }
+ }
+
+ for (child = np->child; child != NULL; child = child->sibling)
+ mem_start = finish_node(child, mem_start, ifunc,
+ naddrc, nsizec);
+
+ return mem_start;
+}
+
+/*
+ * Find the interrupt parent of a node.
+ */
+static struct device_node * __init
+intr_parent(struct device_node *p)
+{
+ phandle *parp;
+
+ parp = (phandle *) get_property(p, "interrupt-parent", NULL);
+ if (parp == NULL)
+ return p->parent;
+ p = find_phandle(*parp);
+ if (p != NULL)
+ return p;
+ /*
+ * On a powermac booted with BootX, we don't get to know the
+ * phandles for any nodes, so find_phandle will return NULL.
+ * Fortunately these machines only have one interrupt controller
+ * so there isn't in fact any ambiguity. -- paulus
+ */
+ if (num_interrupt_controllers == 1)
+ p = dflt_interrupt_controller;
+ return p;
+}
+
+/*
+ * Find out the size of each entry of the interrupts property
+ * for a node.
+ */
+static int __init
+prom_n_intr_cells(struct device_node *np)
+{
+ struct device_node *p;
+ unsigned int *icp;
+
+ for (p = np; (p = intr_parent(p)) != NULL; ) {
+ icp = (unsigned int *)
+ get_property(p, "#interrupt-cells", NULL);
+ if (icp != NULL)
+ return *icp;
+ if (get_property(p, "interrupt-controller", NULL) != NULL
+ || get_property(p, "interrupt-map", NULL) != NULL) {
+ printk("oops, node %s doesn't have #interrupt-cells\n",
+ p->full_name);
+ return 1;
+ }
+ }
+ printk("prom_n_intr_cells failed for %s\n", np->full_name);
+ return 1;
+}
+
+/*
+ * Map an interrupt from a device up to the platform interrupt
+ * descriptor.
+ */
+static int __init
+map_interrupt(unsigned int **irq, struct device_node **ictrler,
+ struct device_node *np, unsigned int *ints, int nintrc)
+{
+ struct device_node *p, *ipar;
+ unsigned int *imap, *imask, *ip;
+ int i, imaplen, match;
+ int newintrc = 1, newaddrc = 1;
+ unsigned int *reg;
+ int naddrc;
+
+ reg = (unsigned int *) get_property(np, "reg", NULL);
+ naddrc = prom_n_addr_cells(np);
+ p = intr_parent(np);
+ while (p != NULL) {
+ if (get_property(p, "interrupt-controller", NULL) != NULL)
+ /* this node is an interrupt controller, stop here */
+ break;
+ imap = (unsigned int *)
+ get_property(p, "interrupt-map", &imaplen);
+ if (imap == NULL) {
+ p = intr_parent(p);
+ continue;
+ }
+ imask = (unsigned int *)
+ get_property(p, "interrupt-map-mask", NULL);
+ if (imask == NULL) {
+ printk("oops, %s has interrupt-map but no mask\n",
+ p->full_name);
+ return 0;
+ }
+ imaplen /= sizeof(unsigned int);
+ match = 0;
+ ipar = NULL;
+ while (imaplen > 0 && !match) {
+ /* check the child-interrupt field */
+ match = 1;
+ for (i = 0; i < naddrc && match; ++i)
+ match = ((reg[i] ^ imap[i]) & imask[i]) == 0;
+ for (; i < naddrc + nintrc && match; ++i)
+ match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0;
+ imap += naddrc + nintrc;
+ imaplen -= naddrc + nintrc;
+ /* grab the interrupt parent */
+ ipar = find_phandle((phandle) *imap++);
+ --imaplen;
+ if (ipar == NULL && num_interrupt_controllers == 1)
+ /* cope with BootX not giving us phandles */
+ ipar = dflt_interrupt_controller;
+ if (ipar == NULL) {
+ printk("oops, no int parent %x in map of %s\n",
+ imap[-1], p->full_name);
+ return 0;
+ }
+ /* find the parent's # addr and intr cells */
+ ip = (unsigned int *)
+ get_property(ipar, "#interrupt-cells", NULL);
+ if (ip == NULL) {
+ printk("oops, no #interrupt-cells on %s\n",
+ ipar->full_name);
+ return 0;
+ }
+ newintrc = *ip;
+ ip = (unsigned int *)
+ get_property(ipar, "#address-cells", NULL);
+ newaddrc = (ip == NULL)? 0: *ip;
+ imap += newaddrc + newintrc;
+ imaplen -= newaddrc + newintrc;
+ }
+ if (imaplen < 0) {
+ printk("oops, error decoding int-map on %s, len=%d\n",
+ p->full_name, imaplen);
+ return 0;
+ }
+ if (!match) {
+ printk("oops, no match in %s int-map for %s\n",
+ p->full_name, np->full_name);
+ return 0;
+ }
+ p = ipar;
+ naddrc = newaddrc;
+ nintrc = newintrc;
+ ints = imap - nintrc;
+ reg = ints - naddrc;
+ }
+ if (p == NULL)
+ printk("hmmm, int tree for %s doesn't have ctrler\n",
+ np->full_name);
+ *irq = ints;
+ *ictrler = p;
+ return nintrc;
+}
+
+/*
+ * New version of finish_node_interrupts.
+ */
+static unsigned long __init
+finish_node_interrupts(struct device_node *np, unsigned long mem_start)
+{
+ unsigned int *ints;
+ int intlen, intrcells;
+ int i, j, n, offset;
+ unsigned int *irq;
+ struct device_node *ic;
+
+ ints = (unsigned int *) get_property(np, "interrupts", &intlen);
+ if (ints == NULL)
+ return mem_start;
+ intrcells = prom_n_intr_cells(np);
+ intlen /= intrcells * sizeof(unsigned int);
+ np->n_intrs = intlen;
+ np->intrs = (struct interrupt_info *) mem_start;
+ mem_start += intlen * sizeof(struct interrupt_info);
+
+ for (i = 0; i < intlen; ++i) {
+ np->intrs[i].line = 0;
+ np->intrs[i].sense = 1;
+ n = map_interrupt(&irq, &ic, np, ints, intrcells);
+ if (n <= 0)
+ continue;
+ offset = 0;
+ /*
+ * On a CHRP we have an 8259 which is subordinate to
+ * the openpic in the interrupt tree, but we want the
+ * openpic's interrupt numbers offsetted, not the 8259's.
+ * So we apply the offset if the controller is at the
+ * root of the interrupt tree, i.e. has no interrupt-parent.
+ * This doesn't cope with the general case of multiple
+ * cascaded interrupt controllers, but then neither will
+ * irq.c at the moment either. -- paulus
+ * The G5 triggers that code, I add a machine test. On
+ * those machines, we want to offset interrupts from the
+ * second openpic by 128 -- BenH
+ */
+ if (_machine != _MACH_Pmac && num_interrupt_controllers > 1
+ && ic != NULL
+ && get_property(ic, "interrupt-parent", NULL) == NULL)
+ offset = 16;
+ else if (_machine == _MACH_Pmac && num_interrupt_controllers > 1
+ && ic != NULL && ic->parent != NULL) {
+ char *name = get_property(ic->parent, "name", NULL);
+ if (name && !strcmp(name, "u3"))
+ offset = 128;
+ }
+
+ np->intrs[i].line = irq[0] + offset;
+ if (n > 1)
+ np->intrs[i].sense = irq[1];
+ if (n > 2) {
+ printk("hmmm, got %d intr cells for %s:", n,
+ np->full_name);
+ for (j = 0; j < n; ++j)
+ printk(" %d", irq[j]);
+ printk("\n");
+ }
+ ints += intrcells;
+ }
+
+ return mem_start;
+}
+
+/*
+ * When BootX makes a copy of the device tree from the MacOS
+ * Name Registry, it is in the format we use but all of the pointers
+ * are offsets from the start of the tree.
+ * This procedure updates the pointers.
+ */
+void __init
+relocate_nodes(void)
+{
+ unsigned long base;
+ struct device_node *np;
+ struct property *pp;
+
+#define ADDBASE(x) (x = (typeof (x))((x)? ((unsigned long)(x) + base): 0))
+
+ base = (unsigned long) boot_infos + boot_infos->deviceTreeOffset;
+ allnodes = (struct device_node *)(base + 4);
+ for (np = allnodes; np != 0; np = np->allnext) {
+ ADDBASE(np->full_name);
+ ADDBASE(np->properties);
+ ADDBASE(np->parent);
+ ADDBASE(np->child);
+ ADDBASE(np->sibling);
+ ADDBASE(np->allnext);
+ for (pp = np->properties; pp != 0; pp = pp->next) {
+ ADDBASE(pp->name);
+ ADDBASE(pp->value);
+ ADDBASE(pp->next);
+ }
+ }
+}
+
+int
+prom_n_addr_cells(struct device_node* np)
+{
+ int* ip;
+ do {
+ if (np->parent)
+ np = np->parent;
+ ip = (int *) get_property(np, "#address-cells", NULL);
+ if (ip != NULL)
+ return *ip;
+ } while (np->parent);
+ /* No #address-cells property for the root node, default to 1 */
+ return 1;
+}
+
+int
+prom_n_size_cells(struct device_node* np)
+{
+ int* ip;
+ do {
+ if (np->parent)
+ np = np->parent;
+ ip = (int *) get_property(np, "#size-cells", NULL);
+ if (ip != NULL)
+ return *ip;
+ } while (np->parent);
+ /* No #size-cells property for the root node, default to 1 */
+ return 1;
+}
+
+static unsigned long __init
+map_addr(struct device_node *np, unsigned long space, unsigned long addr)
+{
+ int na;
+ unsigned int *ranges;
+ int rlen = 0;
+ unsigned int type;
+
+ type = (space >> 24) & 3;
+ if (type == 0)
+ return addr;
+
+ while ((np = np->parent) != NULL) {
+ if (strcmp(np->type, "pci") != 0)
+ continue;
+ /* PCI bridge: map the address through the ranges property */
+ na = prom_n_addr_cells(np);
+ ranges = (unsigned int *) get_property(np, "ranges", &rlen);
+ while ((rlen -= (na + 5) * sizeof(unsigned int)) >= 0) {
+ if (((ranges[0] >> 24) & 3) == type
+ && ranges[2] <= addr
+ && addr - ranges[2] < ranges[na+4]) {
+ /* ok, this matches, translate it */
+ addr += ranges[na+2] - ranges[2];
+ break;
+ }
+ ranges += na + 5;
+ }
+ }
+ return addr;
+}
+
+static unsigned long __init
+interpret_pci_props(struct device_node *np, unsigned long mem_start,
+ int naddrc, int nsizec)
+{
+ struct address_range *adr;
+ struct pci_reg_property *pci_addrs;
+ int i, l, *ip;
+
+ pci_addrs = (struct pci_reg_property *)
+ get_property(np, "assigned-addresses", &l);
+ if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct pci_reg_property)) >= 0) {
+ adr[i].space = pci_addrs[i].addr.a_hi;
+ adr[i].address = map_addr(np, pci_addrs[i].addr.a_hi,
+ pci_addrs[i].addr.a_lo);
+ adr[i].size = pci_addrs[i].size_lo;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ if (use_of_interrupt_tree)
+ return mem_start;
+
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0 && np->parent)
+ ip = (int *) get_property(np->parent, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 1;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long __init
+interpret_dbdma_props(struct device_node *np, unsigned long mem_start,
+ int naddrc, int nsizec)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ unsigned long base_address;
+ int i, l, *ip;
+ struct device_node *db;
+
+ base_address = 0;
+ for (db = np->parent; db != NULL; db = db->parent) {
+ if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {
+ base_address = db->addrs[0].address;
+ break;
+ }
+ }
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 2;
+ adr[i].address = rp[i].address + base_address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ if (use_of_interrupt_tree)
+ return mem_start;
+
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 1;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long __init
+interpret_macio_props(struct device_node *np, unsigned long mem_start,
+ int naddrc, int nsizec)
+{
+ struct reg_property *rp;
+ struct address_range *adr;
+ unsigned long base_address;
+ int i, l, *ip;
+ struct device_node *db;
+
+ base_address = 0;
+ for (db = np->parent; db != NULL; db = db->parent) {
+ if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
+ base_address = db->addrs[0].address;
+ break;
+ }
+ }
+
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = 2;
+ adr[i].address = rp[i].address + base_address;
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ if (use_of_interrupt_tree)
+ return mem_start;
+
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 1;
+ }
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ }
+
+ return mem_start;
+}
+
+static unsigned long __init
+interpret_isa_props(struct device_node *np, unsigned long mem_start,
+ int naddrc, int nsizec)
+{
+ struct isa_reg_property *rp;
+ struct address_range *adr;
+ int i, l, *ip;
+
+ rp = (struct isa_reg_property *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ adr[i].space = rp[i].space;
+ adr[i].address = rp[i].address
+ + (adr[i].space? 0: _ISA_MEM_BASE);
+ adr[i].size = rp[i].size;
+ ++i;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ if (use_of_interrupt_tree)
+ return mem_start;
+
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / (2 * sizeof(int));
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = *ip++;
+ }
+ }
+
+ return mem_start;
+}
+
+static unsigned long __init
+interpret_root_props(struct device_node *np, unsigned long mem_start,
+ int naddrc, int nsizec)
+{
+ struct address_range *adr;
+ int i, l, *ip;
+ unsigned int *rp;
+ int rpsize = (naddrc + nsizec) * sizeof(unsigned int);
+
+ rp = (unsigned int *) get_property(np, "reg", &l);
+ if (rp != 0 && l >= rpsize) {
+ i = 0;
+ adr = (struct address_range *) mem_start;
+ while ((l -= rpsize) >= 0) {
+ adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2);
+ adr[i].address = rp[naddrc - 1];
+ adr[i].size = rp[naddrc + nsizec - 1];
+ ++i;
+ rp += naddrc + nsizec;
+ }
+ np->addrs = adr;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct address_range);
+ }
+
+ if (use_of_interrupt_tree)
+ return mem_start;
+
+ ip = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (ip == 0)
+ ip = (int *) get_property(np, "interrupts", &l);
+ if (ip != 0) {
+ np->intrs = (struct interrupt_info *) mem_start;
+ np->n_intrs = l / sizeof(int);
+ mem_start += np->n_intrs * sizeof(struct interrupt_info);
+ for (i = 0; i < np->n_intrs; ++i) {
+ np->intrs[i].line = *ip++;
+ np->intrs[i].sense = 1;
+ }
+ }
+
+ return mem_start;
+}
+
+/*
+ * Work out the sense (active-low level / active-high edge)
+ * of each interrupt from the device tree.
+ */
+void __init
+prom_get_irq_senses(unsigned char *senses, int off, int max)
+{
+ struct device_node *np;
+ int i, j;
+
+ /* default to level-triggered */
+ memset(senses, 1, max - off);
+ if (!use_of_interrupt_tree)
+ return;
+
+ for (np = allnodes; np != 0; np = np->allnext) {
+ for (j = 0; j < np->n_intrs; j++) {
+ i = np->intrs[j].line;
+ if (i >= off && i < max) {
+ if (np->intrs[j].sense == 1)
+ senses[i-off] = (IRQ_SENSE_LEVEL
+ | IRQ_POLARITY_NEGATIVE);
+ else
+ senses[i-off] = (IRQ_SENSE_EDGE
+ | IRQ_POLARITY_POSITIVE);
+ }
+ }
+ }
+}
+
+/*
+ * Construct and return a list of the device_nodes with a given name.
+ */
+struct device_node *
+find_devices(const char *name)
+{
+ struct device_node *head, **prevp, *np;
+
+ prevp = &head;
+ for (np = allnodes; np != 0; np = np->allnext) {
+ if (np->name != 0 && strcasecmp(np->name, name) == 0) {
+ *prevp = np;
+ prevp = &np->next;
+ }
+ }
+ *prevp = NULL;
+ return head;
+}
+
+/*
+ * Construct and return a list of the device_nodes with a given type.
+ */
+struct device_node *
+find_type_devices(const char *type)
+{
+ struct device_node *head, **prevp, *np;
+
+ prevp = &head;
+ for (np = allnodes; np != 0; np = np->allnext) {
+ if (np->type != 0 && strcasecmp(np->type, type) == 0) {
+ *prevp = np;
+ prevp = &np->next;
+ }
+ }
+ *prevp = NULL;
+ return head;
+}
+
+/*
+ * Returns all nodes linked together
+ */
+struct device_node * __openfirmware
+find_all_nodes(void)
+{
+ struct device_node *head, **prevp, *np;
+
+ prevp = &head;
+ for (np = allnodes; np != 0; np = np->allnext) {
+ *prevp = np;
+ prevp = &np->next;
+ }
+ *prevp = NULL;
+ return head;
+}
+
+/* Checks if the given "compat" string matches one of the strings in
+ * the device's "compatible" property
+ */
+int
+device_is_compatible(struct device_node *device, const char *compat)
+{
+ const char* cp;
+ int cplen, l;
+
+ cp = (char *) get_property(device, "compatible", &cplen);
+ if (cp == NULL)
+ return 0;
+ while (cplen > 0) {
+ if (strncasecmp(cp, compat, strlen(compat)) == 0)
+ return 1;
+ l = strlen(cp) + 1;
+ cp += l;
+ cplen -= l;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Indicates whether the root node has a given value in its
+ * compatible property.
+ */
+int
+machine_is_compatible(const char *compat)
+{
+ struct device_node *root;
+
+ root = find_path_device("/");
+ if (root == 0)
+ return 0;
+ return device_is_compatible(root, compat);
+}
+
+/*
+ * Construct and return a list of the device_nodes with a given type
+ * and compatible property.
+ */
+struct device_node *
+find_compatible_devices(const char *type, const char *compat)
+{
+ struct device_node *head, **prevp, *np;
+
+ prevp = &head;
+ for (np = allnodes; np != 0; np = np->allnext) {
+ if (type != NULL
+ && !(np->type != 0 && strcasecmp(np->type, type) == 0))
+ continue;
+ if (device_is_compatible(np, compat)) {
+ *prevp = np;
+ prevp = &np->next;
+ }
+ }
+ *prevp = NULL;
+ return head;
+}
+
+/*
+ * Find the device_node with a given full_name.
+ */
+struct device_node *
+find_path_device(const char *path)
+{
+ struct device_node *np;
+
+ for (np = allnodes; np != 0; np = np->allnext)
+ if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0)
+ return np;
+ return NULL;
+}
+
+/*******
+ *
+ * New implementation of the OF "find" APIs, return a refcounted
+ * object, call of_node_put() when done. Currently, still lacks
+ * locking as old implementation, this is beeing done for ppc64.
+ *
+ * Note that property management will need some locking as well,
+ * this isn't dealt with yet
+ *
+ *******/
+
+/**
+ * of_find_node_by_name - Find a node by it's "name" property
+ * @from: The node to start searching from or NULL, the node
+ * you pass will not be searched, only the next one
+ * will; typically, you pass what the previous call
+ * returned. of_node_put() will be called on it
+ * @name: The name string to match against
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_node_by_name(struct device_node *from,
+ const char *name)
+{
+ struct device_node *np = from ? from->allnext : allnodes;
+
+ for (; np != 0; np = np->allnext)
+ if (np->name != 0 && strcasecmp(np->name, name) == 0)
+ break;
+ if (from)
+ of_node_put(from);
+ return of_node_get(np);
+}
+
+/**
+ * of_find_node_by_type - Find a node by it's "device_type" property
+ * @from: The node to start searching from or NULL, the node
+ * you pass will not be searched, only the next one
+ * will; typically, you pass what the previous call
+ * returned. of_node_put() will be called on it
+ * @name: The type string to match against
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_node_by_type(struct device_node *from,
+ const char *type)
+{
+ struct device_node *np = from ? from->allnext : allnodes;
+
+ for (; np != 0; np = np->allnext)
+ if (np->type != 0 && strcasecmp(np->type, type) == 0)
+ break;
+ if (from)
+ of_node_put(from);
+ return of_node_get(np);
+}
+
+/**
+ * of_find_compatible_node - Find a node based on type and one of the
+ * tokens in it's "compatible" property
+ * @from: The node to start searching from or NULL, the node
+ * you pass will not be searched, only the next one
+ * will; typically, you pass what the previous call
+ * returned. of_node_put() will be called on it
+ * @type: The type string to match "device_type" or NULL to ignore
+ * @compatible: The string to match to one of the tokens in the device
+ * "compatible" list.
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_compatible_node(struct device_node *from,
+ const char *type, const char *compatible)
+{
+ struct device_node *np = from ? from->allnext : allnodes;
+
+ for (; np != 0; np = np->allnext) {
+ if (type != NULL
+ && !(np->type != 0 && strcasecmp(np->type, type) == 0))
+ continue;
+ if (device_is_compatible(np, compatible))
+ break;
+ }
+ if (from)
+ of_node_put(from);
+ return of_node_get(np);
+}
+
+/**
+ * of_find_node_by_path - Find a node matching a full OF path
+ * @path: The full path to match
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_node_by_path(const char *path)
+{
+ struct device_node *np = allnodes;
+
+ for (; np != 0; np = np->allnext)
+ if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0)
+ break;
+ return of_node_get(np);
+}
+
+/**
+ * of_find_all_nodes - Get next node in global list
+ * @prev: Previous node or NULL to start iteration
+ * of_node_put() will be called on it
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_find_all_nodes(struct device_node *prev)
+{
+ return of_node_get(prev ? prev->allnext : allnodes);
+}
+
+/**
+ * of_get_parent - Get a node's parent if any
+ * @node: Node to get parent
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_get_parent(const struct device_node *node)
+{
+ return node ? of_node_get(node->parent) : NULL;
+}
+
+/**
+ * of_get_next_child - Iterate a node childs
+ * @node: parent node
+ * @prev: previous child of the parent node, or NULL to get first
+ *
+ * Returns a node pointer with refcount incremented, use
+ * of_node_put() on it when done.
+ */
+struct device_node *of_get_next_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ struct device_node *next = prev ? prev->sibling : node->child;
+
+ for (; next != 0; next = next->sibling)
+ if (of_node_get(next))
+ break;
+ if (prev)
+ of_node_put(prev);
+ return next;
+}
+
+/**
+ * of_node_get - Increment refcount of a node
+ * @node: Node to inc refcount, NULL is supported to
+ * simplify writing of callers
+ *
+ * Returns the node itself or NULL if gone. Current implementation
+ * does nothing as we don't yet do dynamic node allocation on ppc32
+ */
+struct device_node *of_node_get(struct device_node *node)
+{
+ return node;
+}
+
+/**
+ * of_node_put - Decrement refcount of a node
+ * @node: Node to dec refcount, NULL is supported to
+ * simplify writing of callers
+ *
+ * Current implementation does nothing as we don't yet do dynamic node
+ * allocation on ppc32
+ */
+void of_node_put(struct device_node *node)
+{
+}
+
+/*
+ * Find the device_node with a given phandle.
+ */
+static struct device_node * __init
+find_phandle(phandle ph)
+{
+ struct device_node *np;
+
+ for (np = allnodes; np != 0; np = np->allnext)
+ if (np->node == ph)
+ return np;
+ return NULL;
+}
+
+/*
+ * Find a property with a given name for a given node
+ * and return the value.
+ */
+unsigned char *
+get_property(struct device_node *np, const char *name, int *lenp)
+{
+ struct property *pp;
+
+ for (pp = np->properties; pp != 0; pp = pp->next)
+ if (pp->name != NULL && strcmp(pp->name, name) == 0) {
+ if (lenp != 0)
+ *lenp = pp->length;
+ return pp->value;
+ }
+ return NULL;
+}
+
+/*
+ * Add a property to a node
+ */
+void __openfirmware
+prom_add_property(struct device_node* np, struct property* prop)
+{
+ struct property **next = &np->properties;
+
+ prop->next = NULL;
+ while (*next)
+ next = &(*next)->next;
+ *next = prop;
+}
+
+/* I quickly hacked that one, check against spec ! */
+static inline unsigned long __openfirmware
+bus_space_to_resource_flags(unsigned int bus_space)
+{
+ u8 space = (bus_space >> 24) & 0xf;
+ if (space == 0)
+ space = 0x02;
+ if (space == 0x02)
+ return IORESOURCE_MEM;
+ else if (space == 0x01)
+ return IORESOURCE_IO;
+ else {
+ printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n",
+ bus_space);
+ return 0;
+ }
+}
+
+static struct resource* __openfirmware
+find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range)
+{
+ unsigned long mask;
+ int i;
+
+ /* Check this one */
+ mask = bus_space_to_resource_flags(range->space);
+ for (i=0; i<DEVICE_COUNT_RESOURCE; i++) {
+ if ((pdev->resource[i].flags & mask) == mask &&
+ pdev->resource[i].start <= range->address &&
+ pdev->resource[i].end > range->address) {
+ if ((range->address + range->size - 1) > pdev->resource[i].end) {
+ /* Add better message */
+ printk(KERN_WARNING "PCI/OF resource overlap !\n");
+ return NULL;
+ }
+ break;
+ }
+ }
+ if (i == DEVICE_COUNT_RESOURCE)
+ return NULL;
+ return &pdev->resource[i];
+}
+
+/*
+ * Request an OF device resource. Currently handles child of PCI devices,
+ * or other nodes attached to the root node. Ultimately, put some
+ * link to resources in the OF node.
+ */
+struct resource* __openfirmware
+request_OF_resource(struct device_node* node, int index, const char* name_postfix)
+{
+ struct pci_dev* pcidev;
+ u8 pci_bus, pci_devfn;
+ unsigned long iomask;
+ struct device_node* nd;
+ struct resource* parent;
+ struct resource *res = NULL;
+ int nlen, plen;
+
+ if (index >= node->n_addrs)
+ goto fail;
+
+ /* Sanity check on bus space */
+ iomask = bus_space_to_resource_flags(node->addrs[index].space);
+ if (iomask & IORESOURCE_MEM)
+ parent = &iomem_resource;
+ else if (iomask & IORESOURCE_IO)
+ parent = &ioport_resource;
+ else
+ goto fail;
+
+ /* Find a PCI parent if any */
+ nd = node;
+ pcidev = NULL;
+ while(nd) {
+ if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
+ pcidev = pci_find_slot(pci_bus, pci_devfn);
+ if (pcidev) break;
+ nd = nd->parent;
+ }
+ if (pcidev)
+ parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
+ if (!parent) {
+ printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
+ node->name);
+ goto fail;
+ }
+
+ res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL);
+ if (!res)
+ goto fail;
+ nlen = strlen(node->name);
+ plen = name_postfix ? strlen(name_postfix) : 0;
+ res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL);
+ if (res->name) {
+ strcpy((char *)res->name, node->name);
+ if (plen)
+ strcpy((char *)res->name+nlen, name_postfix);
+ }
+ return res;
+fail:
+ return NULL;
+}
+
+int __openfirmware
+release_OF_resource(struct device_node* node, int index)
+{
+ struct pci_dev* pcidev;
+ u8 pci_bus, pci_devfn;
+ unsigned long iomask, start, end;
+ struct device_node* nd;
+ struct resource* parent;
+ struct resource *res = NULL;
+
+ if (index >= node->n_addrs)
+ return -EINVAL;
+
+ /* Sanity check on bus space */
+ iomask = bus_space_to_resource_flags(node->addrs[index].space);
+ if (iomask & IORESOURCE_MEM)
+ parent = &iomem_resource;
+ else if (iomask & IORESOURCE_IO)
+ parent = &ioport_resource;
+ else
+ return -EINVAL;
+
+ /* Find a PCI parent if any */
+ nd = node;
+ pcidev = NULL;
+ while(nd) {
+ if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
+ pcidev = pci_find_slot(pci_bus, pci_devfn);
+ if (pcidev) break;
+ nd = nd->parent;
+ }
+ if (pcidev)
+ parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
+ if (!parent) {
+ printk(KERN_WARNING "release_OF_resource(%s), parent not found\n",
+ node->name);
+ return -ENODEV;
+ }
+
+ /* Find us in the parent and its childs */
+ res = parent->child;
+ start = node->addrs[index].address;
+ end = start + node->addrs[index].size - 1;
+ while (res) {
+ if (res->start == start && res->end == end &&
+ (res->flags & IORESOURCE_BUSY))
+ break;
+ if (res->start <= start && res->end >= end)
+ res = res->child;
+ else
+ res = res->sibling;
+ }
+ if (!res)
+ return -ENODEV;
+
+ if (res->name) {
+ kfree(res->name);
+ res->name = NULL;
+ }
+ release_resource(res);
+ kfree(res);
+
+ return 0;
+}
+
+#if 0
+void __openfirmware
+print_properties(struct device_node *np)
+{
+ struct property *pp;
+ char *cp;
+ int i, n;
+
+ for (pp = np->properties; pp != 0; pp = pp->next) {
+ printk(KERN_INFO "%s", pp->name);
+ for (i = strlen(pp->name); i < 16; ++i)
+ printk(" ");
+ cp = (char *) pp->value;
+ for (i = pp->length; i > 0; --i, ++cp)
+ if ((i > 1 && (*cp < 0x20 || *cp > 0x7e))
+ || (i == 1 && *cp != 0))
+ break;
+ if (i == 0 && pp->length > 1) {
+ /* looks like a string */
+ printk(" %s\n", (char *) pp->value);
+ } else {
+ /* dump it in hex */
+ n = pp->length;
+ if (n > 64)
+ n = 64;
+ if (pp->length % 4 == 0) {
+ unsigned int *p = (unsigned int *) pp->value;
+
+ n /= 4;
+ for (i = 0; i < n; ++i) {
+ if (i != 0 && (i % 4) == 0)
+ printk("\n ");
+ printk(" %08x", *p++);
+ }
+ } else {
+ unsigned char *bp = pp->value;
+
+ for (i = 0; i < n; ++i) {
+ if (i != 0 && (i % 16) == 0)
+ printk("\n ");
+ printk(" %02x", *bp++);
+ }
+ }
+ printk("\n");
+ if (pp->length > 64)
+ printk(" ... (length = %d)\n",
+ pp->length);
+ }
+ }
+}
+#endif
+
+static DEFINE_SPINLOCK(rtas_lock);
+
+/* this can be called after setup -- Cort */
+int __openfirmware
+call_rtas(const char *service, int nargs, int nret,
+ unsigned long *outputs, ...)
+{
+ va_list list;
+ int i;
+ unsigned long s;
+ struct device_node *rtas;
+ int *tokp;
+ union {
+ unsigned long words[16];
+ double align;
+ } u;
+
+ rtas = find_devices("rtas");
+ if (rtas == NULL)
+ return -1;
+ tokp = (int *) get_property(rtas, service, NULL);
+ if (tokp == NULL) {
+ printk(KERN_ERR "No RTAS service called %s\n", service);
+ return -1;
+ }
+ u.words[0] = *tokp;
+ u.words[1] = nargs;
+ u.words[2] = nret;
+ va_start(list, outputs);
+ for (i = 0; i < nargs; ++i)
+ u.words[i+3] = va_arg(list, unsigned long);
+ va_end(list);
+
+ /*
+ * RTAS doesn't use floating point.
+ * Or at least, according to the CHRP spec we enter RTAS
+ * with FP disabled, and it doesn't change the FP registers.
+ * -- paulus.
+ */
+ spin_lock_irqsave(&rtas_lock, s);
+ enter_rtas((void *)__pa(&u));
+ spin_unlock_irqrestore(&rtas_lock, s);
+
+ if (nret > 1 && outputs != NULL)
+ for (i = 0; i < nret-1; ++i)
+ outputs[i] = u.words[i+nargs+4];
+ return u.words[nargs+3];
+}
diff --git a/arch/ppc/syslib/prom_init.c b/arch/ppc/syslib/prom_init.c
new file mode 100644
index 000000000000..2cee87137f2e
--- /dev/null
+++ b/arch/ppc/syslib/prom_init.c
@@ -0,0 +1,1002 @@
+/*
+ * Note that prom_init() and anything called from prom_init()
+ * may be running at an address that is different from the address
+ * that it was linked at. References to static data items are
+ * handled by compiling this file with -mrelocatable-lib.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/threads.h>
+#include <linux/spinlock.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+
+#include <asm/sections.h>
+#include <asm/prom.h>
+#include <asm/page.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/smp.h>
+#include <asm/bootx.h>
+#include <asm/system.h>
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/bootinfo.h>
+#include <asm/btext.h>
+#include <asm/pci-bridge.h>
+#include <asm/open_pic.h>
+#include <asm/cacheflush.h>
+
+#ifdef CONFIG_LOGO_LINUX_CLUT224
+#include <linux/linux_logo.h>
+extern const struct linux_logo logo_linux_clut224;
+#endif
+
+/*
+ * Properties whose value is longer than this get excluded from our
+ * copy of the device tree. This way we don't waste space storing
+ * things like "driver,AAPL,MacOS,PowerPC" properties. But this value
+ * does need to be big enough to ensure that we don't lose things
+ * like the interrupt-map property on a PCI-PCI bridge.
+ */
+#define MAX_PROPERTY_LENGTH 4096
+
+#ifndef FB_MAX /* avoid pulling in all of the fb stuff */
+#define FB_MAX 8
+#endif
+
+#define ALIGNUL(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
+
+typedef u32 prom_arg_t;
+
+struct prom_args {
+ const char *service;
+ int nargs;
+ int nret;
+ prom_arg_t args[10];
+};
+
+struct pci_address {
+ unsigned a_hi;
+ unsigned a_mid;
+ unsigned a_lo;
+};
+
+struct pci_reg_property {
+ struct pci_address addr;
+ unsigned size_hi;
+ unsigned size_lo;
+};
+
+struct pci_range {
+ struct pci_address addr;
+ unsigned phys;
+ unsigned size_hi;
+ unsigned size_lo;
+};
+
+struct isa_reg_property {
+ unsigned space;
+ unsigned address;
+ unsigned size;
+};
+
+struct pci_intr_map {
+ struct pci_address addr;
+ unsigned dunno;
+ phandle int_ctrler;
+ unsigned intr;
+};
+
+static void prom_exit(void);
+static int call_prom(const char *service, int nargs, int nret, ...);
+static int call_prom_ret(const char *service, int nargs, int nret,
+ prom_arg_t *rets, ...);
+static void prom_print_hex(unsigned int v);
+static int prom_set_color(ihandle ih, int i, int r, int g, int b);
+static int prom_next_node(phandle *nodep);
+static unsigned long check_display(unsigned long mem);
+static void setup_disp_fake_bi(ihandle dp);
+static unsigned long copy_device_tree(unsigned long mem_start,
+ unsigned long mem_end);
+static unsigned long inspect_node(phandle node, struct device_node *dad,
+ unsigned long mem_start, unsigned long mem_end,
+ struct device_node ***allnextpp);
+static void prom_hold_cpus(unsigned long mem);
+static void prom_instantiate_rtas(void);
+static void * early_get_property(unsigned long base, unsigned long node,
+ char *prop);
+
+prom_entry prom __initdata;
+ihandle prom_chosen __initdata;
+ihandle prom_stdout __initdata;
+
+static char *prom_display_paths[FB_MAX] __initdata;
+static phandle prom_display_nodes[FB_MAX] __initdata;
+static unsigned int prom_num_displays __initdata;
+static ihandle prom_disp_node __initdata;
+char *of_stdout_device __initdata;
+
+unsigned int rtas_data; /* physical pointer */
+unsigned int rtas_entry; /* physical pointer */
+unsigned int rtas_size;
+unsigned int old_rtas;
+
+boot_infos_t *boot_infos;
+char *bootpath;
+char *bootdevice;
+struct device_node *allnodes;
+
+extern char *klimit;
+
+static void __init
+prom_exit(void)
+{
+ struct prom_args args;
+
+ args.service = "exit";
+ args.nargs = 0;
+ args.nret = 0;
+ prom(&args);
+ for (;;) /* should never get here */
+ ;
+}
+
+static int __init
+call_prom(const char *service, int nargs, int nret, ...)
+{
+ va_list list;
+ int i;
+ struct prom_args prom_args;
+
+ prom_args.service = service;
+ prom_args.nargs = nargs;
+ prom_args.nret = nret;
+ va_start(list, nret);
+ for (i = 0; i < nargs; ++i)
+ prom_args.args[i] = va_arg(list, prom_arg_t);
+ va_end(list);
+ for (i = 0; i < nret; ++i)
+ prom_args.args[i + nargs] = 0;
+ prom(&prom_args);
+ return prom_args.args[nargs];
+}
+
+static int __init
+call_prom_ret(const char *service, int nargs, int nret, prom_arg_t *rets, ...)
+{
+ va_list list;
+ int i;
+ struct prom_args prom_args;
+
+ prom_args.service = service;
+ prom_args.nargs = nargs;
+ prom_args.nret = nret;
+ va_start(list, rets);
+ for (i = 0; i < nargs; ++i)
+ prom_args.args[i] = va_arg(list, int);
+ va_end(list);
+ for (i = 0; i < nret; ++i)
+ prom_args.args[i + nargs] = 0;
+ prom(&prom_args);
+ for (i = 1; i < nret; ++i)
+ rets[i-1] = prom_args.args[nargs + i];
+ return prom_args.args[nargs];
+}
+
+void __init
+prom_print(const char *msg)
+{
+ const char *p, *q;
+
+ if (prom_stdout == 0)
+ return;
+
+ for (p = msg; *p != 0; p = q) {
+ for (q = p; *q != 0 && *q != '\n'; ++q)
+ ;
+ if (q > p)
+ call_prom("write", 3, 1, prom_stdout, p, q - p);
+ if (*q != 0) {
+ ++q;
+ call_prom("write", 3, 1, prom_stdout, "\r\n", 2);
+ }
+ }
+}
+
+static void __init
+prom_print_hex(unsigned int v)
+{
+ char buf[16];
+ int i, c;
+
+ for (i = 0; i < 8; ++i) {
+ c = (v >> ((7-i)*4)) & 0xf;
+ c += (c >= 10)? ('a' - 10): '0';
+ buf[i] = c;
+ }
+ buf[i] = ' ';
+ buf[i+1] = 0;
+ prom_print(buf);
+}
+
+static int __init
+prom_set_color(ihandle ih, int i, int r, int g, int b)
+{
+ return call_prom("call-method", 6, 1, "color!", ih, i, b, g, r);
+}
+
+static int __init
+prom_next_node(phandle *nodep)
+{
+ phandle node;
+
+ if ((node = *nodep) != 0
+ && (*nodep = call_prom("child", 1, 1, node)) != 0)
+ return 1;
+ if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+ return 1;
+ for (;;) {
+ if ((node = call_prom("parent", 1, 1, node)) == 0)
+ return 0;
+ if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+ return 1;
+ }
+}
+
+#ifdef CONFIG_POWER4
+/*
+ * Set up a hash table with a set of entries in it to map the
+ * first 64MB of RAM. This is used on 64-bit machines since
+ * some of them don't have BATs.
+ */
+
+static inline void make_pte(unsigned long htab, unsigned int hsize,
+ unsigned int va, unsigned int pa, int mode)
+{
+ unsigned int *pteg;
+ unsigned int hash, i, vsid;
+
+ vsid = ((va >> 28) * 0x111) << 12;
+ hash = ((va ^ vsid) >> 5) & 0x7fff80;
+ pteg = (unsigned int *)(htab + (hash & (hsize - 1)));
+ for (i = 0; i < 8; ++i, pteg += 4) {
+ if ((pteg[1] & 1) == 0) {
+ pteg[1] = vsid | ((va >> 16) & 0xf80) | 1;
+ pteg[3] = pa | mode;
+ break;
+ }
+ }
+}
+
+extern unsigned long _SDR1;
+extern PTE *Hash;
+extern unsigned long Hash_size;
+
+static void __init
+prom_alloc_htab(void)
+{
+ unsigned int hsize;
+ unsigned long htab;
+ unsigned int addr;
+
+ /*
+ * Because of OF bugs we can't use the "claim" client
+ * interface to allocate memory for the hash table.
+ * This code is only used on 64-bit PPCs, and the only
+ * 64-bit PPCs at the moment are RS/6000s, and their
+ * OF is based at 0xc00000 (the 12M point), so we just
+ * arbitrarily use the 0x800000 - 0xc00000 region for the
+ * hash table.
+ * -- paulus.
+ */
+ hsize = 4 << 20; /* POWER4 has no BATs */
+ htab = (8 << 20);
+ call_prom("claim", 3, 1, htab, hsize, 0);
+ Hash = (void *)(htab + KERNELBASE);
+ Hash_size = hsize;
+ _SDR1 = htab + __ilog2(hsize) - 18;
+
+ /*
+ * Put in PTEs for the first 64MB of RAM
+ */
+ memset((void *)htab, 0, hsize);
+ for (addr = 0; addr < 0x4000000; addr += 0x1000)
+ make_pte(htab, hsize, addr + KERNELBASE, addr,
+ _PAGE_ACCESSED | _PAGE_COHERENT | PP_RWXX);
+#if 0 /* DEBUG stuff mapping the SCC */
+ make_pte(htab, hsize, 0x80013000, 0x80013000,
+ _PAGE_ACCESSED | _PAGE_NO_CACHE | _PAGE_GUARDED | PP_RWXX);
+#endif
+}
+#endif /* CONFIG_POWER4 */
+
+
+/*
+ * If we have a display that we don't know how to drive,
+ * we will want to try to execute OF's open method for it
+ * later. However, OF will probably fall over if we do that
+ * we've taken over the MMU.
+ * So we check whether we will need to open the display,
+ * and if so, open it now.
+ */
+static unsigned long __init
+check_display(unsigned long mem)
+{
+ phandle node;
+ ihandle ih;
+ int i, j;
+ char type[16], *path;
+ static unsigned char default_colors[] = {
+ 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xaa,
+ 0x00, 0xaa, 0x00,
+ 0x00, 0xaa, 0xaa,
+ 0xaa, 0x00, 0x00,
+ 0xaa, 0x00, 0xaa,
+ 0xaa, 0xaa, 0x00,
+ 0xaa, 0xaa, 0xaa,
+ 0x55, 0x55, 0x55,
+ 0x55, 0x55, 0xff,
+ 0x55, 0xff, 0x55,
+ 0x55, 0xff, 0xff,
+ 0xff, 0x55, 0x55,
+ 0xff, 0x55, 0xff,
+ 0xff, 0xff, 0x55,
+ 0xff, 0xff, 0xff
+ };
+ const unsigned char *clut;
+
+ prom_disp_node = 0;
+
+ for (node = 0; prom_next_node(&node); ) {
+ type[0] = 0;
+ call_prom("getprop", 4, 1, node, "device_type",
+ type, sizeof(type));
+ if (strcmp(type, "display") != 0)
+ continue;
+ /* It seems OF doesn't null-terminate the path :-( */
+ path = (char *) mem;
+ memset(path, 0, 256);
+ if (call_prom("package-to-path", 3, 1, node, path, 255) < 0)
+ continue;
+
+ /*
+ * If this display is the device that OF is using for stdout,
+ * move it to the front of the list.
+ */
+ mem += strlen(path) + 1;
+ i = prom_num_displays++;
+ if (of_stdout_device != 0 && i > 0
+ && strcmp(of_stdout_device, path) == 0) {
+ for (; i > 0; --i) {
+ prom_display_paths[i]
+ = prom_display_paths[i-1];
+ prom_display_nodes[i]
+ = prom_display_nodes[i-1];
+ }
+ }
+ prom_display_paths[i] = path;
+ prom_display_nodes[i] = node;
+ if (i == 0)
+ prom_disp_node = node;
+ if (prom_num_displays >= FB_MAX)
+ break;
+ }
+
+ for (j=0; j<prom_num_displays; j++) {
+ path = prom_display_paths[j];
+ node = prom_display_nodes[j];
+ prom_print("opening display ");
+ prom_print(path);
+ ih = call_prom("open", 1, 1, path);
+ if (ih == 0 || ih == (ihandle) -1) {
+ prom_print("... failed\n");
+ for (i=j+1; i<prom_num_displays; i++) {
+ prom_display_paths[i-1] = prom_display_paths[i];
+ prom_display_nodes[i-1] = prom_display_nodes[i];
+ }
+ if (--prom_num_displays > 0) {
+ prom_disp_node = prom_display_nodes[j];
+ j--;
+ } else
+ prom_disp_node = 0;
+ continue;
+ } else {
+ prom_print("... ok\n");
+ call_prom("setprop", 4, 1, node, "linux,opened", 0, 0);
+
+ /*
+ * Setup a usable color table when the appropriate
+ * method is available.
+ * Should update this to use set-colors.
+ */
+ clut = default_colors;
+ for (i = 0; i < 32; i++, clut += 3)
+ if (prom_set_color(ih, i, clut[0], clut[1],
+ clut[2]) != 0)
+ break;
+
+#ifdef CONFIG_LOGO_LINUX_CLUT224
+ clut = PTRRELOC(logo_linux_clut224.clut);
+ for (i = 0; i < logo_linux_clut224.clutsize;
+ i++, clut += 3)
+ if (prom_set_color(ih, i + 32, clut[0],
+ clut[1], clut[2]) != 0)
+ break;
+#endif /* CONFIG_LOGO_LINUX_CLUT224 */
+ }
+ }
+
+ if (prom_stdout) {
+ phandle p;
+ p = call_prom("instance-to-package", 1, 1, prom_stdout);
+ if (p && p != -1) {
+ type[0] = 0;
+ call_prom("getprop", 4, 1, p, "device_type",
+ type, sizeof(type));
+ if (strcmp(type, "display") == 0)
+ call_prom("setprop", 4, 1, p, "linux,boot-display",
+ 0, 0);
+ }
+ }
+
+ return ALIGNUL(mem);
+}
+
+/* This function will enable the early boot text when doing OF booting. This
+ * way, xmon output should work too
+ */
+static void __init
+setup_disp_fake_bi(ihandle dp)
+{
+#ifdef CONFIG_BOOTX_TEXT
+ int width = 640, height = 480, depth = 8, pitch;
+ unsigned address;
+ struct pci_reg_property addrs[8];
+ int i, naddrs;
+ char name[32];
+ char *getprop = "getprop";
+
+ prom_print("Initializing fake screen: ");
+
+ memset(name, 0, sizeof(name));
+ call_prom(getprop, 4, 1, dp, "name", name, sizeof(name));
+ name[sizeof(name)-1] = 0;
+ prom_print(name);
+ prom_print("\n");
+ call_prom(getprop, 4, 1, dp, "width", &width, sizeof(width));
+ call_prom(getprop, 4, 1, dp, "height", &height, sizeof(height));
+ call_prom(getprop, 4, 1, dp, "depth", &depth, sizeof(depth));
+ pitch = width * ((depth + 7) / 8);
+ call_prom(getprop, 4, 1, dp, "linebytes",
+ &pitch, sizeof(pitch));
+ if (pitch == 1)
+ pitch = 0x1000; /* for strange IBM display */
+ address = 0;
+ call_prom(getprop, 4, 1, dp, "address",
+ &address, sizeof(address));
+ if (address == 0) {
+ /* look for an assigned address with a size of >= 1MB */
+ naddrs = call_prom(getprop, 4, 1, dp, "assigned-addresses",
+ addrs, sizeof(addrs));
+ naddrs /= sizeof(struct pci_reg_property);
+ for (i = 0; i < naddrs; ++i) {
+ if (addrs[i].size_lo >= (1 << 20)) {
+ address = addrs[i].addr.a_lo;
+ /* use the BE aperture if possible */
+ if (addrs[i].size_lo >= (16 << 20))
+ address += (8 << 20);
+ break;
+ }
+ }
+ if (address == 0) {
+ prom_print("Failed to get address\n");
+ return;
+ }
+ }
+ /* kludge for valkyrie */
+ if (strcmp(name, "valkyrie") == 0)
+ address += 0x1000;
+
+#ifdef CONFIG_POWER4
+#if CONFIG_TASK_SIZE > 0x80000000
+#error CONFIG_TASK_SIZE cannot be above 0x80000000 with BOOTX_TEXT on G5
+#endif
+ {
+ extern boot_infos_t disp_bi;
+ unsigned long va, pa, i, offset;
+ va = 0x90000000;
+ pa = address & 0xfffff000ul;
+ offset = address & 0x00000fff;
+
+ for (i=0; i<0x4000; i++) {
+ make_pte((unsigned long)Hash - KERNELBASE, Hash_size, va, pa,
+ _PAGE_ACCESSED | _PAGE_NO_CACHE |
+ _PAGE_GUARDED | PP_RWXX);
+ va += 0x1000;
+ pa += 0x1000;
+ }
+ btext_setup_display(width, height, depth, pitch, 0x90000000 | offset);
+ disp_bi.dispDeviceBase = (u8 *)address;
+ }
+#else /* CONFIG_POWER4 */
+ btext_setup_display(width, height, depth, pitch, address);
+ btext_prepare_BAT();
+#endif /* CONFIG_POWER4 */
+#endif /* CONFIG_BOOTX_TEXT */
+}
+
+/*
+ * Make a copy of the device tree from the PROM.
+ */
+static unsigned long __init
+copy_device_tree(unsigned long mem_start, unsigned long mem_end)
+{
+ phandle root;
+ unsigned long new_start;
+ struct device_node **allnextp;
+
+ root = call_prom("peer", 1, 1, (phandle)0);
+ if (root == (phandle)0) {
+ prom_print("couldn't get device tree root\n");
+ prom_exit();
+ }
+ allnextp = &allnodes;
+ mem_start = ALIGNUL(mem_start);
+ new_start = inspect_node(root, NULL, mem_start, mem_end, &allnextp);
+ *allnextp = NULL;
+ return new_start;
+}
+
+static unsigned long __init
+inspect_node(phandle node, struct device_node *dad,
+ unsigned long mem_start, unsigned long mem_end,
+ struct device_node ***allnextpp)
+{
+ int l;
+ phandle child;
+ struct device_node *np;
+ struct property *pp, **prev_propp;
+ char *prev_name, *namep;
+ unsigned char *valp;
+
+ np = (struct device_node *) mem_start;
+ mem_start += sizeof(struct device_node);
+ memset(np, 0, sizeof(*np));
+ np->node = node;
+ **allnextpp = PTRUNRELOC(np);
+ *allnextpp = &np->allnext;
+ if (dad != 0) {
+ np->parent = PTRUNRELOC(dad);
+ /* we temporarily use the `next' field as `last_child'. */
+ if (dad->next == 0)
+ dad->child = PTRUNRELOC(np);
+ else
+ dad->next->sibling = PTRUNRELOC(np);
+ dad->next = np;
+ }
+
+ /* get and store all properties */
+ prev_propp = &np->properties;
+ prev_name = "";
+ for (;;) {
+ pp = (struct property *) mem_start;
+ namep = (char *) (pp + 1);
+ pp->name = PTRUNRELOC(namep);
+ if (call_prom("nextprop", 3, 1, node, prev_name, namep) <= 0)
+ break;
+ mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1);
+ prev_name = namep;
+ valp = (unsigned char *) mem_start;
+ pp->value = PTRUNRELOC(valp);
+ pp->length = call_prom("getprop", 4, 1, node, namep,
+ valp, mem_end - mem_start);
+ if (pp->length < 0)
+ continue;
+#ifdef MAX_PROPERTY_LENGTH
+ if (pp->length > MAX_PROPERTY_LENGTH)
+ continue; /* ignore this property */
+#endif
+ mem_start = ALIGNUL(mem_start + pp->length);
+ *prev_propp = PTRUNRELOC(pp);
+ prev_propp = &pp->next;
+ }
+ if (np->node != 0) {
+ /* Add a "linux,phandle" property" */
+ pp = (struct property *) mem_start;
+ *prev_propp = PTRUNRELOC(pp);
+ prev_propp = &pp->next;
+ namep = (char *) (pp + 1);
+ pp->name = PTRUNRELOC(namep);
+ strcpy(namep, "linux,phandle");
+ mem_start = ALIGNUL((unsigned long)namep + strlen(namep) + 1);
+ pp->value = (unsigned char *) PTRUNRELOC(&np->node);
+ pp->length = sizeof(np->node);
+ }
+ *prev_propp = NULL;
+
+ /* get the node's full name */
+ l = call_prom("package-to-path", 3, 1, node,
+ mem_start, mem_end - mem_start);
+ if (l >= 0) {
+ np->full_name = PTRUNRELOC((char *) mem_start);
+ *(char *)(mem_start + l) = 0;
+ mem_start = ALIGNUL(mem_start + l + 1);
+ }
+
+ /* do all our children */
+ child = call_prom("child", 1, 1, node);
+ while (child != 0) {
+ mem_start = inspect_node(child, np, mem_start, mem_end,
+ allnextpp);
+ child = call_prom("peer", 1, 1, child);
+ }
+
+ return mem_start;
+}
+
+unsigned long smp_chrp_cpu_nr __initdata = 0;
+
+/*
+ * With CHRP SMP we need to use the OF to start the other
+ * processors so we can't wait until smp_boot_cpus (the OF is
+ * trashed by then) so we have to put the processors into
+ * a holding pattern controlled by the kernel (not OF) before
+ * we destroy the OF.
+ *
+ * This uses a chunk of high memory, puts some holding pattern
+ * code there and sends the other processors off to there until
+ * smp_boot_cpus tells them to do something. We do that by using
+ * physical address 0x0. The holding pattern checks that address
+ * until its cpu # is there, when it is that cpu jumps to
+ * __secondary_start(). smp_boot_cpus() takes care of setting those
+ * values.
+ *
+ * We also use physical address 0x4 here to tell when a cpu
+ * is in its holding pattern code.
+ *
+ * -- Cort
+ *
+ * Note that we have to do this if we have more than one CPU,
+ * even if this is a UP kernel. Otherwise when we trash OF
+ * the other CPUs will start executing some random instructions
+ * and crash the system. -- paulus
+ */
+static void __init
+prom_hold_cpus(unsigned long mem)
+{
+ extern void __secondary_hold(void);
+ unsigned long i;
+ int cpu;
+ phandle node;
+ char type[16], *path;
+ unsigned int reg;
+
+ /*
+ * XXX: hack to make sure we're chrp, assume that if we're
+ * chrp we have a device_type property -- Cort
+ */
+ node = call_prom("finddevice", 1, 1, "/");
+ if (call_prom("getprop", 4, 1, node,
+ "device_type", type, sizeof(type)) <= 0)
+ return;
+
+ /* copy the holding pattern code to someplace safe (0) */
+ /* the holding pattern is now within the first 0x100
+ bytes of the kernel image -- paulus */
+ memcpy((void *)0, _stext, 0x100);
+ flush_icache_range(0, 0x100);
+
+ /* look for cpus */
+ *(unsigned long *)(0x0) = 0;
+ asm volatile("dcbf 0,%0": : "r" (0) : "memory");
+ for (node = 0; prom_next_node(&node); ) {
+ type[0] = 0;
+ call_prom("getprop", 4, 1, node, "device_type",
+ type, sizeof(type));
+ if (strcmp(type, "cpu") != 0)
+ continue;
+ path = (char *) mem;
+ memset(path, 0, 256);
+ if (call_prom("package-to-path", 3, 1, node, path, 255) < 0)
+ continue;
+ reg = -1;
+ call_prom("getprop", 4, 1, node, "reg", &reg, sizeof(reg));
+ cpu = smp_chrp_cpu_nr++;
+#ifdef CONFIG_SMP
+ smp_hw_index[cpu] = reg;
+#endif /* CONFIG_SMP */
+ /* XXX: hack - don't start cpu 0, this cpu -- Cort */
+ if (cpu == 0)
+ continue;
+ prom_print("starting cpu ");
+ prom_print(path);
+ *(ulong *)(0x4) = 0;
+ call_prom("start-cpu", 3, 0, node,
+ (char *)__secondary_hold - _stext, cpu);
+ prom_print("...");
+ for ( i = 0 ; (i < 10000) && (*(ulong *)(0x4) == 0); i++ )
+ ;
+ if (*(ulong *)(0x4) == cpu)
+ prom_print("ok\n");
+ else {
+ prom_print("failed: ");
+ prom_print_hex(*(ulong *)0x4);
+ prom_print("\n");
+ }
+ }
+}
+
+static void __init
+prom_instantiate_rtas(void)
+{
+ ihandle prom_rtas;
+ prom_arg_t result;
+
+ prom_rtas = call_prom("finddevice", 1, 1, "/rtas");
+ if (prom_rtas == -1)
+ return;
+
+ rtas_size = 0;
+ call_prom("getprop", 4, 1, prom_rtas,
+ "rtas-size", &rtas_size, sizeof(rtas_size));
+ prom_print("instantiating rtas");
+ if (rtas_size == 0) {
+ rtas_data = 0;
+ } else {
+ /*
+ * Ask OF for some space for RTAS.
+ * Actually OF has bugs so we just arbitrarily
+ * use memory at the 6MB point.
+ */
+ rtas_data = 6 << 20;
+ prom_print(" at ");
+ prom_print_hex(rtas_data);
+ }
+
+ prom_rtas = call_prom("open", 1, 1, "/rtas");
+ prom_print("...");
+ rtas_entry = 0;
+ if (call_prom_ret("call-method", 3, 2, &result,
+ "instantiate-rtas", prom_rtas, rtas_data) == 0)
+ rtas_entry = result;
+ if ((rtas_entry == -1) || (rtas_entry == 0))
+ prom_print(" failed\n");
+ else
+ prom_print(" done\n");
+}
+
+/*
+ * We enter here early on, when the Open Firmware prom is still
+ * handling exceptions and the MMU hash table for us.
+ */
+unsigned long __init
+prom_init(int r3, int r4, prom_entry pp)
+{
+ unsigned long mem;
+ ihandle prom_mmu;
+ unsigned long offset = reloc_offset();
+ int i, l;
+ char *p, *d;
+ unsigned long phys;
+ prom_arg_t result[3];
+ char model[32];
+ phandle node;
+ int rc;
+
+ /* Default */
+ phys = (unsigned long) &_stext;
+
+ /* First get a handle for the stdout device */
+ prom = pp;
+ prom_chosen = call_prom("finddevice", 1, 1, "/chosen");
+ if (prom_chosen == -1)
+ prom_exit();
+ if (call_prom("getprop", 4, 1, prom_chosen, "stdout",
+ &prom_stdout, sizeof(prom_stdout)) <= 0)
+ prom_exit();
+
+ /* Get the full OF pathname of the stdout device */
+ mem = (unsigned long) klimit + offset;
+ p = (char *) mem;
+ memset(p, 0, 256);
+ call_prom("instance-to-path", 3, 1, prom_stdout, p, 255);
+ of_stdout_device = p;
+ mem += strlen(p) + 1;
+
+ /* Get the boot device and translate it to a full OF pathname. */
+ p = (char *) mem;
+ l = call_prom("getprop", 4, 1, prom_chosen, "bootpath", p, 1<<20);
+ if (l > 0) {
+ p[l] = 0; /* should already be null-terminated */
+ bootpath = PTRUNRELOC(p);
+ mem += l + 1;
+ d = (char *) mem;
+ *d = 0;
+ call_prom("canon", 3, 1, p, d, 1<<20);
+ bootdevice = PTRUNRELOC(d);
+ mem = ALIGNUL(mem + strlen(d) + 1);
+ }
+
+ prom_instantiate_rtas();
+
+#ifdef CONFIG_POWER4
+ /*
+ * Find out how much memory we have and allocate a
+ * suitably-sized hash table.
+ */
+ prom_alloc_htab();
+#endif
+ mem = check_display(mem);
+
+ prom_print("copying OF device tree...");
+ mem = copy_device_tree(mem, mem + (1<<20));
+ prom_print("done\n");
+
+ prom_hold_cpus(mem);
+
+ klimit = (char *) (mem - offset);
+
+ node = call_prom("finddevice", 1, 1, "/");
+ rc = call_prom("getprop", 4, 1, node, "model", model, sizeof(model));
+ if (rc > 0 && !strncmp (model, "Pegasos", 7)
+ && strncmp (model, "Pegasos2", 8)) {
+ /* Pegasos 1 has a broken translate method in the OF,
+ * and furthermore the BATs are mapped 1:1 so the phys
+ * address calculated above is correct, so let's use
+ * it directly.
+ */
+ } else if (offset == 0) {
+ /* If we are already running at 0xc0000000, we assume we were
+ * loaded by an OF bootloader which did set a BAT for us.
+ * This breaks OF translate so we force phys to be 0.
+ */
+ prom_print("(already at 0xc0000000) phys=0\n");
+ phys = 0;
+ } else if (call_prom("getprop", 4, 1, prom_chosen, "mmu",
+ &prom_mmu, sizeof(prom_mmu)) <= 0) {
+ prom_print(" no MMU found\n");
+ } else if (call_prom_ret("call-method", 4, 4, result, "translate",
+ prom_mmu, &_stext, 1) != 0) {
+ prom_print(" (translate failed)\n");
+ } else {
+ /* We assume the phys. address size is 3 cells */
+ phys = result[2];
+ }
+
+ if (prom_disp_node != 0)
+ setup_disp_fake_bi(prom_disp_node);
+
+ /* Use quiesce call to get OF to shut down any devices it's using */
+ prom_print("Calling quiesce ...\n");
+ call_prom("quiesce", 0, 0);
+
+ /* Relocate various pointers which will be used once the
+ kernel is running at the address it was linked at. */
+ for (i = 0; i < prom_num_displays; ++i)
+ prom_display_paths[i] = PTRUNRELOC(prom_display_paths[i]);
+
+#ifdef CONFIG_SERIAL_CORE_CONSOLE
+ /* Relocate the of stdout for console autodetection */
+ of_stdout_device = PTRUNRELOC(of_stdout_device);
+#endif
+
+ prom_print("returning 0x");
+ prom_print_hex(phys);
+ prom_print("from prom_init\n");
+ prom_stdout = 0;
+
+ return phys;
+}
+
+/*
+ * early_get_property is used to access the device tree image prepared
+ * by BootX very early on, before the pointers in it have been relocated.
+ */
+static void * __init
+early_get_property(unsigned long base, unsigned long node, char *prop)
+{
+ struct device_node *np = (struct device_node *)(base + node);
+ struct property *pp;
+
+ for (pp = np->properties; pp != 0; pp = pp->next) {
+ pp = (struct property *) (base + (unsigned long)pp);
+ if (strcmp((char *)((unsigned long)pp->name + base),
+ prop) == 0) {
+ return (void *)((unsigned long)pp->value + base);
+ }
+ }
+ return NULL;
+}
+
+/* Is boot-info compatible ? */
+#define BOOT_INFO_IS_COMPATIBLE(bi) ((bi)->compatible_version <= BOOT_INFO_VERSION)
+#define BOOT_INFO_IS_V2_COMPATIBLE(bi) ((bi)->version >= 2)
+#define BOOT_INFO_IS_V4_COMPATIBLE(bi) ((bi)->version >= 4)
+
+void __init
+bootx_init(unsigned long r4, unsigned long phys)
+{
+ boot_infos_t *bi = (boot_infos_t *) r4;
+ unsigned long space;
+ unsigned long ptr, x;
+ char *model;
+
+ boot_infos = PTRUNRELOC(bi);
+ if (!BOOT_INFO_IS_V2_COMPATIBLE(bi))
+ bi->logicalDisplayBase = NULL;
+
+#ifdef CONFIG_BOOTX_TEXT
+ btext_init(bi);
+
+ /*
+ * Test if boot-info is compatible. Done only in config
+ * CONFIG_BOOTX_TEXT since there is nothing much we can do
+ * with an incompatible version, except display a message
+ * and eventually hang the processor...
+ *
+ * I'll try to keep enough of boot-info compatible in the
+ * future to always allow display of this message;
+ */
+ if (!BOOT_INFO_IS_COMPATIBLE(bi)) {
+ btext_drawstring(" !!! WARNING - Incompatible version of BootX !!!\n\n\n");
+ btext_flushscreen();
+ }
+#endif /* CONFIG_BOOTX_TEXT */
+
+ /* New BootX enters kernel with MMU off, i/os are not allowed
+ here. This hack will have been done by the boostrap anyway.
+ */
+ if (bi->version < 4) {
+ /*
+ * XXX If this is an iMac, turn off the USB controller.
+ */
+ model = (char *) early_get_property
+ (r4 + bi->deviceTreeOffset, 4, "model");
+ if (model
+ && (strcmp(model, "iMac,1") == 0
+ || strcmp(model, "PowerMac1,1") == 0)) {
+ out_le32((unsigned *)0x80880008, 1); /* XXX */
+ }
+ }
+
+ /* Move klimit to enclose device tree, args, ramdisk, etc... */
+ if (bi->version < 5) {
+ space = bi->deviceTreeOffset + bi->deviceTreeSize;
+ if (bi->ramDisk)
+ space = bi->ramDisk + bi->ramDiskSize;
+ } else
+ space = bi->totalParamsSize;
+ klimit = PTRUNRELOC((char *) bi + space);
+
+ /* New BootX will have flushed all TLBs and enters kernel with
+ MMU switched OFF, so this should not be useful anymore.
+ */
+ if (bi->version < 4) {
+ /*
+ * Touch each page to make sure the PTEs for them
+ * are in the hash table - the aim is to try to avoid
+ * getting DSI exceptions while copying the kernel image.
+ */
+ for (ptr = ((unsigned long) &_stext) & PAGE_MASK;
+ ptr < (unsigned long)bi + space; ptr += PAGE_SIZE)
+ x = *(volatile unsigned long *)ptr;
+ }
+
+#ifdef CONFIG_BOOTX_TEXT
+ /*
+ * Note that after we call btext_prepare_BAT, we can't do
+ * prom_draw*, flushscreen or clearscreen until we turn the MMU
+ * on, since btext_prepare_BAT sets disp_bi.logicalDisplayBase
+ * to a virtual address.
+ */
+ btext_prepare_BAT();
+#endif
+}
diff --git a/arch/ppc/syslib/qspan_pci.c b/arch/ppc/syslib/qspan_pci.c
new file mode 100644
index 000000000000..57f4ed5e5ae1
--- /dev/null
+++ b/arch/ppc/syslib/qspan_pci.c
@@ -0,0 +1,381 @@
+/*
+ * QSpan pci routines.
+ * Most 8xx boards use the QSpan PCI bridge. The config address register
+ * is located 0x500 from the base of the bridge control/status registers.
+ * The data register is located at 0x504.
+ * This is a two step operation. First, the address register is written,
+ * then the data register is read/written as required.
+ * I don't know what to do about interrupts (yet).
+ *
+ * The RPX Classic implementation shares a chip select for normal
+ * PCI access and QSpan control register addresses. The selection is
+ * further selected by a bit setting in a board control register.
+ * Although it should happen, we disable interrupts during this operation
+ * to make sure some driver doesn't accidentally access the PCI while
+ * we have switched the chip select.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/string.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/mpc8xx.h>
+#include <asm/system.h>
+#include <asm/machdep.h>
+#include <asm/pci-bridge.h>
+
+
+/*
+ * This blows......
+ * When reading the configuration space, if something does not respond
+ * the bus times out and we get a machine check interrupt. So, the
+ * good ol' exception tables come to mind to trap it and return some
+ * value.
+ *
+ * On an error we just return a -1, since that is what the caller wants
+ * returned if nothing is present. I copied this from __get_user_asm,
+ * with the only difference of returning -1 instead of EFAULT.
+ * There is an associated hack in the machine check trap code.
+ *
+ * The QSPAN is also a big endian device, that is it makes the PCI
+ * look big endian to us. This presents a problem for the Linux PCI
+ * functions, which assume little endian. For example, we see the
+ * first 32-bit word like this:
+ * ------------------------
+ * | Device ID | Vendor ID |
+ * ------------------------
+ * If we read/write as a double word, that's OK. But in our world,
+ * when read as a word, device ID is at location 0, not location 2 as
+ * the little endian PCI would believe. We have to switch bits in
+ * the PCI addresses given to us to get the data to/from the correct
+ * byte lanes.
+ *
+ * The QSPAN only supports 4 bits of "slot" in the dev_fn instead of 5.
+ * It always forces the MS bit to zero. Therefore, dev_fn values
+ * greater than 128 are returned as "no device found" errors.
+ *
+ * The QSPAN can only perform long word (32-bit) configuration cycles.
+ * The "offset" must have the two LS bits set to zero. Read operations
+ * require we read the entire word and then sort out what should be
+ * returned. Write operations other than long word require that we
+ * read the long word, update the proper word or byte, then write the
+ * entire long word back.
+ *
+ * PCI Bridge hack. We assume (correctly) that bus 0 is the primary
+ * PCI bus from the QSPAN. If we are called with a bus number other
+ * than zero, we create a Type 1 configuration access that a downstream
+ * PCI bridge will interpret.
+ */
+
+#define __get_qspan_pci_config(x, addr, op) \
+ __asm__ __volatile__( \
+ "1: "op" %0,0(%1)\n" \
+ " eieio\n" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: li %0,-1\n" \
+ " b 2b\n" \
+ ".section __ex_table,\"a\"\n" \
+ " .align 2\n" \
+ " .long 1b,3b\n" \
+ ".text" \
+ : "=r"(x) : "r"(addr) : " %0")
+
+#define QS_CONFIG_ADDR ((volatile uint *)(PCI_CSR_ADDR + 0x500))
+#define QS_CONFIG_DATA ((volatile uint *)(PCI_CSR_ADDR + 0x504))
+
+#define mk_config_addr(bus, dev, offset) \
+ (((bus)<<16) | ((dev)<<8) | (offset & 0xfc))
+
+#define mk_config_type1(bus, dev, offset) \
+ mk_config_addr(bus, dev, offset) | 1;
+
+static spinlock_t pcibios_lock = SPIN_LOCK_UNLOCKED;
+
+int qspan_pcibios_read_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char *val)
+{
+ uint temp;
+ u_char *cp;
+#ifdef CONFIG_RPXCLASSIC
+ unsigned long flags;
+#endif
+
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+#ifdef CONFIG_RPXCLASSIC
+ /* disable interrupts */
+ spin_lock_irqsave(&pcibios_lock, flags);
+ *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
+ eieio();
+#endif
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz");
+
+#ifdef CONFIG_RPXCLASSIC
+ *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
+ eieio();
+ spin_unlock_irqrestore(&pcibios_lock, flags);
+#endif
+
+ offset ^= 0x03;
+ cp = ((u_char *)&temp) + (offset & 0x03);
+ *val = *cp;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int qspan_pcibios_read_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short *val)
+{
+ uint temp;
+ ushort *sp;
+#ifdef CONFIG_RPXCLASSIC
+ unsigned long flags;
+#endif
+
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+#ifdef CONFIG_RPXCLASSIC
+ /* disable interrupts */
+ spin_lock_irqsave(&pcibios_lock, flags);
+ *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
+ eieio();
+#endif
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_qspan_pci_config(temp, QS_CONFIG_DATA, "lwz");
+ offset ^= 0x02;
+
+#ifdef CONFIG_RPXCLASSIC
+ *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
+ eieio();
+ spin_unlock_irqrestore(&pcibios_lock, flags);
+#endif
+
+ sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+ *val = *sp;
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int qspan_pcibios_read_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int *val)
+{
+#ifdef CONFIG_RPXCLASSIC
+ unsigned long flags;
+#endif
+
+ if ((bus > 7) || (dev_fn > 127)) {
+ *val = 0xffffffff;
+ return PCIBIOS_DEVICE_NOT_FOUND;
+ }
+
+#ifdef CONFIG_RPXCLASSIC
+ /* disable interrupts */
+ spin_lock_irqsave(&pcibios_lock, flags);
+ *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
+ eieio();
+#endif
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ __get_qspan_pci_config(*val, QS_CONFIG_DATA, "lwz");
+
+#ifdef CONFIG_RPXCLASSIC
+ *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
+ eieio();
+ spin_unlock_irqrestore(&pcibios_lock, flags);
+#endif
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int qspan_pcibios_write_config_byte(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned char val)
+{
+ uint temp;
+ u_char *cp;
+#ifdef CONFIG_RPXCLASSIC
+ unsigned long flags;
+#endif
+
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+ offset ^= 0x03;
+ cp = ((u_char *)&temp) + (offset & 0x03);
+ *cp = val;
+
+#ifdef CONFIG_RPXCLASSIC
+ /* disable interrupts */
+ spin_lock_irqsave(&pcibios_lock, flags);
+ *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
+ eieio();
+#endif
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *QS_CONFIG_DATA = temp;
+
+#ifdef CONFIG_RPXCLASSIC
+ *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
+ eieio();
+ spin_unlock_irqrestore(&pcibios_lock, flags);
+#endif
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int qspan_pcibios_write_config_word(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned short val)
+{
+ uint temp;
+ ushort *sp;
+#ifdef CONFIG_RPXCLASSIC
+ unsigned long flags;
+#endif
+
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+ qspan_pcibios_read_config_dword(bus, dev_fn, offset, &temp);
+
+ offset ^= 0x02;
+ sp = ((ushort *)&temp) + ((offset >> 1) & 1);
+ *sp = val;
+
+#ifdef CONFIG_RPXCLASSIC
+ /* disable interrupts */
+ spin_lock_irqsave(&pcibios_lock, flags);
+ *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
+ eieio();
+#endif
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *QS_CONFIG_DATA = temp;
+
+#ifdef CONFIG_RPXCLASSIC
+ *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
+ eieio();
+ spin_unlock_irqrestore(&pcibios_lock, flags);
+#endif
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int qspan_pcibios_write_config_dword(unsigned char bus, unsigned char dev_fn,
+ unsigned char offset, unsigned int val)
+{
+#ifdef CONFIG_RPXCLASSIC
+ unsigned long flags;
+#endif
+
+ if ((bus > 7) || (dev_fn > 127))
+ return PCIBIOS_DEVICE_NOT_FOUND;
+
+#ifdef CONFIG_RPXCLASSIC
+ /* disable interrupts */
+ spin_lock_irqsave(&pcibios_lock, flags);
+ *((uint *)RPX_CSR_ADDR) &= ~BCSR2_QSPACESEL;
+ eieio();
+#endif
+
+ if (bus == 0)
+ *QS_CONFIG_ADDR = mk_config_addr(bus, dev_fn, offset);
+ else
+ *QS_CONFIG_ADDR = mk_config_type1(bus, dev_fn, offset);
+ *(unsigned int *)QS_CONFIG_DATA = val;
+
+#ifdef CONFIG_RPXCLASSIC
+ *((uint *)RPX_CSR_ADDR) |= BCSR2_QSPACESEL;
+ eieio();
+ spin_unlock_irqrestore(&pcibios_lock, flags);
+#endif
+
+ return PCIBIOS_SUCCESSFUL;
+}
+
+int qspan_pcibios_find_device(unsigned short vendor, unsigned short dev_id,
+ unsigned short index, unsigned char *bus_ptr,
+ unsigned char *dev_fn_ptr)
+{
+ int num, devfn;
+ unsigned int x, vendev;
+
+ if (vendor == 0xffff)
+ return PCIBIOS_BAD_VENDOR_ID;
+ vendev = (dev_id << 16) + vendor;
+ num = 0;
+ for (devfn = 0; devfn < 32; devfn++) {
+ qspan_pcibios_read_config_dword(0, devfn<<3, PCI_VENDOR_ID, &x);
+ if (x == vendev) {
+ if (index == num) {
+ *bus_ptr = 0;
+ *dev_fn_ptr = devfn<<3;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+int qspan_pcibios_find_class(unsigned int class_code, unsigned short index,
+ unsigned char *bus_ptr, unsigned char *dev_fn_ptr)
+{
+ int devnr, x, num;
+
+ num = 0;
+ for (devnr = 0; devnr < 32; devnr++) {
+ qspan_pcibios_read_config_dword(0, devnr<<3, PCI_CLASS_REVISION, &x);
+ if ((x>>8) == class_code) {
+ if (index == num) {
+ *bus_ptr = 0;
+ *dev_fn_ptr = devnr<<3;
+ return PCIBIOS_SUCCESSFUL;
+ }
+ ++num;
+ }
+ }
+ return PCIBIOS_DEVICE_NOT_FOUND;
+}
+
+void __init
+m8xx_pcibios_fixup(void))
+{
+ /* Lots to do here, all board and configuration specific. */
+}
+
+void __init
+m8xx_setup_pci_ptrs(void))
+{
+ set_config_access_method(qspan);
+
+ ppc_md.pcibios_fixup = m8xx_pcibios_fixup;
+}
+
diff --git a/arch/ppc/syslib/todc_time.c b/arch/ppc/syslib/todc_time.c
new file mode 100644
index 000000000000..1323c641c19d
--- /dev/null
+++ b/arch/ppc/syslib/todc_time.c
@@ -0,0 +1,513 @@
+/*
+ * arch/ppc/syslib/todc_time.c
+ *
+ * Time of Day Clock support for the M48T35, M48T37, M48T59, and MC146818
+ * Real Time Clocks/Timekeepers.
+ *
+ * Author: Mark A. Greer
+ * mgreer@mvista.com
+ *
+ * 2001-2004 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/bcd.h>
+#include <linux/mc146818rtc.h>
+
+#include <asm/machdep.h>
+#include <asm/io.h>
+#include <asm/time.h>
+#include <asm/todc.h>
+
+/*
+ * Depending on the hardware on your board and your board design, the
+ * RTC/NVRAM may be accessed either directly (like normal memory) or via
+ * address/data registers. If your board uses the direct method, set
+ * 'nvram_data' to the base address of your nvram and leave 'nvram_as0' and
+ * 'nvram_as1' NULL. If your board uses address/data regs to access nvram,
+ * set 'nvram_as0' to the address of the lower byte, set 'nvram_as1' to the
+ * address of the upper byte (leave NULL if using mc146818), and set
+ * 'nvram_data' to the address of the 8-bit data register.
+ *
+ * In order to break the assumption that the RTC and NVRAM are accessed by
+ * the same mechanism, you need to explicitly set 'ppc_md.rtc_read_val' and
+ * 'ppc_md.rtc_write_val', otherwise the values of 'ppc_md.rtc_read_val'
+ * and 'ppc_md.rtc_write_val' will be used.
+ *
+ * Note: Even though the documentation for the various RTC chips say that it
+ * take up to a second before it starts updating once the 'R' bit is
+ * cleared, they always seem to update even though we bang on it many
+ * times a second. This is true, except for the Dallas Semi 1746/1747
+ * (possibly others). Those chips seem to have a real problem whenever
+ * we set the 'R' bit before reading them, they basically stop counting.
+ * --MAG
+ */
+
+/*
+ * 'todc_info' should be initialized in your *_setup.c file to
+ * point to a fully initialized 'todc_info_t' structure.
+ * This structure holds all the register offsets for your particular
+ * TODC/RTC chip.
+ * TODC_ALLOC()/TODC_INIT() will allocate and initialize this table for you.
+ */
+
+#ifdef RTC_FREQ_SELECT
+#undef RTC_FREQ_SELECT
+#define RTC_FREQ_SELECT control_b /* Register A */
+#endif
+
+#ifdef RTC_CONTROL
+#undef RTC_CONTROL
+#define RTC_CONTROL control_a /* Register B */
+#endif
+
+#ifdef RTC_INTR_FLAGS
+#undef RTC_INTR_FLAGS
+#define RTC_INTR_FLAGS watchdog /* Register C */
+#endif
+
+#ifdef RTC_VALID
+#undef RTC_VALID
+#define RTC_VALID interrupts /* Register D */
+#endif
+
+/* Access routines when RTC accessed directly (like normal memory) */
+u_char
+todc_direct_read_val(int addr)
+{
+ return readb((void __iomem *)(todc_info->nvram_data + addr));
+}
+
+void
+todc_direct_write_val(int addr, unsigned char val)
+{
+ writeb(val, (void __iomem *)(todc_info->nvram_data + addr));
+ return;
+}
+
+/* Access routines for accessing m48txx type chips via addr/data regs */
+u_char
+todc_m48txx_read_val(int addr)
+{
+ outb(addr, todc_info->nvram_as0);
+ outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
+ return inb(todc_info->nvram_data);
+}
+
+void
+todc_m48txx_write_val(int addr, unsigned char val)
+{
+ outb(addr, todc_info->nvram_as0);
+ outb(addr>>todc_info->as0_bits, todc_info->nvram_as1);
+ outb(val, todc_info->nvram_data);
+ return;
+}
+
+/* Access routines for accessing mc146818 type chips via addr/data regs */
+u_char
+todc_mc146818_read_val(int addr)
+{
+ outb_p(addr, todc_info->nvram_as0);
+ return inb_p(todc_info->nvram_data);
+}
+
+void
+todc_mc146818_write_val(int addr, unsigned char val)
+{
+ outb_p(addr, todc_info->nvram_as0);
+ outb_p(val, todc_info->nvram_data);
+}
+
+
+/*
+ * Routines to make RTC chips with NVRAM buried behind an addr/data pair
+ * have the NVRAM and clock regs appear at the same level.
+ * The NVRAM will appear to start at addr 0 and the clock regs will appear
+ * to start immediately after the NVRAM (actually, start at offset
+ * todc_info->nvram_size).
+ */
+static inline u_char
+todc_read_val(int addr)
+{
+ u_char val;
+
+ if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
+ if (addr < todc_info->nvram_size) { /* NVRAM */
+ ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
+ val = ppc_md.rtc_read_val(todc_info->nvram_data_reg);
+ }
+ else { /* Clock Reg */
+ addr -= todc_info->nvram_size;
+ val = ppc_md.rtc_read_val(addr);
+ }
+ }
+ else {
+ val = ppc_md.rtc_read_val(addr);
+ }
+
+ return val;
+}
+
+static inline void
+todc_write_val(int addr, u_char val)
+{
+ if (todc_info->sw_flags & TODC_FLAG_2_LEVEL_NVRAM) {
+ if (addr < todc_info->nvram_size) { /* NVRAM */
+ ppc_md.rtc_write_val(todc_info->nvram_addr_reg, addr);
+ ppc_md.rtc_write_val(todc_info->nvram_data_reg, val);
+ }
+ else { /* Clock Reg */
+ addr -= todc_info->nvram_size;
+ ppc_md.rtc_write_val(addr, val);
+ }
+ }
+ else {
+ ppc_md.rtc_write_val(addr, val);
+ }
+}
+
+/*
+ * TODC routines
+ *
+ * There is some ugly stuff in that there are assumptions for the mc146818.
+ *
+ * Assumptions:
+ * - todc_info->control_a has the offset as mc146818 Register B reg
+ * - todc_info->control_b has the offset as mc146818 Register A reg
+ * - m48txx control reg's write enable or 'W' bit is same as
+ * mc146818 Register B 'SET' bit (i.e., 0x80)
+ *
+ * These assumptions were made to make the code simpler.
+ */
+long __init
+todc_time_init(void)
+{
+ u_char cntl_b;
+
+ if (!ppc_md.rtc_read_val)
+ ppc_md.rtc_read_val = ppc_md.nvram_read_val;
+ if (!ppc_md.rtc_write_val)
+ ppc_md.rtc_write_val = ppc_md.nvram_write_val;
+
+ cntl_b = todc_read_val(todc_info->control_b);
+
+ if (todc_info->rtc_type == TODC_TYPE_MC146818) {
+ if ((cntl_b & 0x70) != 0x20) {
+ printk(KERN_INFO "TODC %s %s\n",
+ "real-time-clock was stopped.",
+ "Now starting...");
+ cntl_b &= ~0x70;
+ cntl_b |= 0x20;
+ }
+
+ todc_write_val(todc_info->control_b, cntl_b);
+ } else if (todc_info->rtc_type == TODC_TYPE_DS17285) {
+ u_char mode;
+
+ mode = todc_read_val(TODC_TYPE_DS17285_CNTL_A);
+ /* Make sure countdown clear is not set */
+ mode &= ~0x40;
+ /* Enable oscillator, extended register set */
+ mode |= 0x30;
+ todc_write_val(TODC_TYPE_DS17285_CNTL_A, mode);
+
+ } else if (todc_info->rtc_type == TODC_TYPE_DS1501) {
+ u_char month;
+
+ todc_info->enable_read = TODC_DS1501_CNTL_B_TE;
+ todc_info->enable_write = TODC_DS1501_CNTL_B_TE;
+
+ month = todc_read_val(todc_info->month);
+
+ if ((month & 0x80) == 0x80) {
+ printk(KERN_INFO "TODC %s %s\n",
+ "real-time-clock was stopped.",
+ "Now starting...");
+ month &= ~0x80;
+ todc_write_val(todc_info->month, month);
+ }
+
+ cntl_b &= ~TODC_DS1501_CNTL_B_TE;
+ todc_write_val(todc_info->control_b, cntl_b);
+ } else { /* must be a m48txx type */
+ u_char cntl_a;
+
+ todc_info->enable_read = TODC_MK48TXX_CNTL_A_R;
+ todc_info->enable_write = TODC_MK48TXX_CNTL_A_W;
+
+ cntl_a = todc_read_val(todc_info->control_a);
+
+ /* Check & clear STOP bit in control B register */
+ if (cntl_b & TODC_MK48TXX_DAY_CB) {
+ printk(KERN_INFO "TODC %s %s\n",
+ "real-time-clock was stopped.",
+ "Now starting...");
+
+ cntl_a |= todc_info->enable_write;
+ cntl_b &= ~TODC_MK48TXX_DAY_CB;/* Start Oscil */
+
+ todc_write_val(todc_info->control_a, cntl_a);
+ todc_write_val(todc_info->control_b, cntl_b);
+ }
+
+ /* Make sure READ & WRITE bits are cleared. */
+ cntl_a &= ~(todc_info->enable_write |
+ todc_info->enable_read);
+ todc_write_val(todc_info->control_a, cntl_a);
+ }
+
+ return 0;
+}
+
+/*
+ * There is some ugly stuff in that there are assumptions that for a mc146818,
+ * the todc_info->control_a has the offset of the mc146818 Register B reg and
+ * that the register'ss 'SET' bit is the same as the m48txx's write enable
+ * bit in the control register of the m48txx (i.e., 0x80).
+ *
+ * It was done to make the code look simpler.
+ */
+ulong
+todc_get_rtc_time(void)
+{
+ uint year = 0, mon = 0, day = 0, hour = 0, min = 0, sec = 0;
+ uint limit, i;
+ u_char save_control, uip = 0;
+
+ spin_lock(&rtc_lock);
+ save_control = todc_read_val(todc_info->control_a);
+
+ if (todc_info->rtc_type != TODC_TYPE_MC146818) {
+ limit = 1;
+
+ switch (todc_info->rtc_type) {
+ case TODC_TYPE_DS1553:
+ case TODC_TYPE_DS1557:
+ case TODC_TYPE_DS1743:
+ case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
+ case TODC_TYPE_DS1747:
+ case TODC_TYPE_DS17285:
+ break;
+ default:
+ todc_write_val(todc_info->control_a,
+ (save_control | todc_info->enable_read));
+ }
+ }
+ else {
+ limit = 100000000;
+ }
+
+ for (i=0; i<limit; i++) {
+ if (todc_info->rtc_type == TODC_TYPE_MC146818) {
+ uip = todc_read_val(todc_info->RTC_FREQ_SELECT);
+ }
+
+ sec = todc_read_val(todc_info->seconds) & 0x7f;
+ min = todc_read_val(todc_info->minutes) & 0x7f;
+ hour = todc_read_val(todc_info->hours) & 0x3f;
+ day = todc_read_val(todc_info->day_of_month) & 0x3f;
+ mon = todc_read_val(todc_info->month) & 0x1f;
+ year = todc_read_val(todc_info->year) & 0xff;
+
+ if (todc_info->rtc_type == TODC_TYPE_MC146818) {
+ uip |= todc_read_val(todc_info->RTC_FREQ_SELECT);
+ if ((uip & RTC_UIP) == 0) break;
+ }
+ }
+
+ if (todc_info->rtc_type != TODC_TYPE_MC146818) {
+ switch (todc_info->rtc_type) {
+ case TODC_TYPE_DS1553:
+ case TODC_TYPE_DS1557:
+ case TODC_TYPE_DS1743:
+ case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
+ case TODC_TYPE_DS1747:
+ case TODC_TYPE_DS17285:
+ break;
+ default:
+ save_control &= ~(todc_info->enable_read);
+ todc_write_val(todc_info->control_a,
+ save_control);
+ }
+ }
+ spin_unlock(&rtc_lock);
+
+ if ((todc_info->rtc_type != TODC_TYPE_MC146818) ||
+ ((save_control & RTC_DM_BINARY) == 0) ||
+ RTC_ALWAYS_BCD) {
+
+ BCD_TO_BIN(sec);
+ BCD_TO_BIN(min);
+ BCD_TO_BIN(hour);
+ BCD_TO_BIN(day);
+ BCD_TO_BIN(mon);
+ BCD_TO_BIN(year);
+ }
+
+ year = year + 1900;
+ if (year < 1970) {
+ year += 100;
+ }
+
+ return mktime(year, mon, day, hour, min, sec);
+}
+
+int
+todc_set_rtc_time(unsigned long nowtime)
+{
+ struct rtc_time tm;
+ u_char save_control, save_freq_select = 0;
+
+ spin_lock(&rtc_lock);
+ to_tm(nowtime, &tm);
+
+ save_control = todc_read_val(todc_info->control_a);
+
+ /* Assuming MK48T59_RTC_CA_WRITE & RTC_SET are equal */
+ todc_write_val(todc_info->control_a,
+ (save_control | todc_info->enable_write));
+ save_control &= ~(todc_info->enable_write); /* in case it was set */
+
+ if (todc_info->rtc_type == TODC_TYPE_MC146818) {
+ save_freq_select = todc_read_val(todc_info->RTC_FREQ_SELECT);
+ todc_write_val(todc_info->RTC_FREQ_SELECT,
+ save_freq_select | RTC_DIV_RESET2);
+ }
+
+
+ tm.tm_year = (tm.tm_year - 1900) % 100;
+
+ if ((todc_info->rtc_type != TODC_TYPE_MC146818) ||
+ ((save_control & RTC_DM_BINARY) == 0) ||
+ RTC_ALWAYS_BCD) {
+
+ BIN_TO_BCD(tm.tm_sec);
+ BIN_TO_BCD(tm.tm_min);
+ BIN_TO_BCD(tm.tm_hour);
+ BIN_TO_BCD(tm.tm_mon);
+ BIN_TO_BCD(tm.tm_mday);
+ BIN_TO_BCD(tm.tm_year);
+ }
+
+ todc_write_val(todc_info->seconds, tm.tm_sec);
+ todc_write_val(todc_info->minutes, tm.tm_min);
+ todc_write_val(todc_info->hours, tm.tm_hour);
+ todc_write_val(todc_info->month, tm.tm_mon);
+ todc_write_val(todc_info->day_of_month, tm.tm_mday);
+ todc_write_val(todc_info->year, tm.tm_year);
+
+ todc_write_val(todc_info->control_a, save_control);
+
+ if (todc_info->rtc_type == TODC_TYPE_MC146818) {
+ todc_write_val(todc_info->RTC_FREQ_SELECT, save_freq_select);
+ }
+ spin_unlock(&rtc_lock);
+
+ return 0;
+}
+
+/*
+ * Manipulates read bit to reliably read seconds at a high rate.
+ */
+static unsigned char __init todc_read_timereg(int addr)
+{
+ unsigned char save_control = 0, val;
+
+ switch (todc_info->rtc_type) {
+ case TODC_TYPE_DS1553:
+ case TODC_TYPE_DS1557:
+ case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
+ case TODC_TYPE_DS1747:
+ case TODC_TYPE_DS17285:
+ case TODC_TYPE_MC146818:
+ break;
+ default:
+ save_control = todc_read_val(todc_info->control_a);
+ todc_write_val(todc_info->control_a,
+ (save_control | todc_info->enable_read));
+ }
+ val = todc_read_val(addr);
+
+ switch (todc_info->rtc_type) {
+ case TODC_TYPE_DS1553:
+ case TODC_TYPE_DS1557:
+ case TODC_TYPE_DS1746: /* XXXX BAD HACK -> FIX */
+ case TODC_TYPE_DS1747:
+ case TODC_TYPE_DS17285:
+ case TODC_TYPE_MC146818:
+ break;
+ default:
+ save_control &= ~(todc_info->enable_read);
+ todc_write_val(todc_info->control_a, save_control);
+ }
+
+ return val;
+}
+
+/*
+ * This was taken from prep_setup.c
+ * Use the NVRAM RTC to time a second to calibrate the decrementer.
+ */
+void __init
+todc_calibrate_decr(void)
+{
+ ulong freq;
+ ulong tbl, tbu;
+ long i, loop_count;
+ u_char sec;
+
+ todc_time_init();
+
+ /*
+ * Actually this is bad for precision, we should have a loop in
+ * which we only read the seconds counter. todc_read_val writes
+ * the address bytes on every call and this takes a lot of time.
+ * Perhaps an nvram_wait_change method returning a time
+ * stamp with a loop count as parameter would be the solution.
+ */
+ /*
+ * Need to make sure the tbl doesn't roll over so if tbu increments
+ * during this test, we need to do it again.
+ */
+ loop_count = 0;
+
+ sec = todc_read_timereg(todc_info->seconds) & 0x7f;
+
+ do {
+ tbu = get_tbu();
+
+ for (i = 0 ; i < 10000000 ; i++) {/* may take up to 1 second */
+ tbl = get_tbl();
+
+ if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) {
+ break;
+ }
+ }
+
+ sec = todc_read_timereg(todc_info->seconds) & 0x7f;
+
+ for (i = 0 ; i < 10000000 ; i++) { /* Should take 1 second */
+ freq = get_tbl();
+
+ if ((todc_read_timereg(todc_info->seconds) & 0x7f) != sec) {
+ break;
+ }
+ }
+
+ freq -= tbl;
+ } while ((get_tbu() != tbu) && (++loop_count < 2));
+
+ printk("time_init: decrementer frequency = %lu.%.6lu MHz\n",
+ freq/1000000, freq%1000000);
+
+ tb_ticks_per_jiffy = freq / HZ;
+ tb_to_us = mulhwu_scale_factor(freq, 1000000);
+
+ return;
+}
diff --git a/arch/ppc/syslib/xilinx_pic.c b/arch/ppc/syslib/xilinx_pic.c
new file mode 100644
index 000000000000..e0bd66f0847a
--- /dev/null
+++ b/arch/ppc/syslib/xilinx_pic.c
@@ -0,0 +1,157 @@
+/*
+ * arch/ppc/syslib/xilinx_pic.c
+ *
+ * Interrupt controller driver for Xilinx Virtex-II Pro.
+ *
+ * Author: MontaVista Software, Inc.
+ * source@mvista.com
+ *
+ * 2002-2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <asm/io.h>
+#include <asm/xparameters.h>
+#include <asm/ibm4xx.h>
+
+/* No one else should require these constants, so define them locally here. */
+#define ISR 0 /* Interrupt Status Register */
+#define IPR 1 /* Interrupt Pending Register */
+#define IER 2 /* Interrupt Enable Register */
+#define IAR 3 /* Interrupt Acknowledge Register */
+#define SIE 4 /* Set Interrupt Enable bits */
+#define CIE 5 /* Clear Interrupt Enable bits */
+#define IVR 6 /* Interrupt Vector Register */
+#define MER 7 /* Master Enable Register */
+
+#if XPAR_XINTC_USE_DCR == 0
+static volatile u32 *intc;
+#define intc_out_be32(addr, mask) out_be32((addr), (mask))
+#define intc_in_be32(addr) in_be32((addr))
+#else
+#define intc XPAR_INTC_0_BASEADDR
+#define intc_out_be32(addr, mask) mtdcr((addr), (mask))
+#define intc_in_be32(addr) mfdcr((addr))
+#endif
+
+static void
+xilinx_intc_enable(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+ pr_debug("enable: %d\n", irq);
+ intc_out_be32(intc + SIE, mask);
+}
+
+static void
+xilinx_intc_disable(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+ pr_debug("disable: %d\n", irq);
+ intc_out_be32(intc + CIE, mask);
+}
+
+static void
+xilinx_intc_disable_and_ack(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+ pr_debug("disable_and_ack: %d\n", irq);
+ intc_out_be32(intc + CIE, mask);
+ if (!(irq_desc[irq].status & IRQ_LEVEL))
+ intc_out_be32(intc + IAR, mask); /* ack edge triggered intr */
+}
+
+static void
+xilinx_intc_end(unsigned int irq)
+{
+ unsigned long mask = (0x00000001 << (irq & 31));
+
+ pr_debug("end: %d\n", irq);
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+ intc_out_be32(intc + SIE, mask);
+ /* ack level sensitive intr */
+ if (irq_desc[irq].status & IRQ_LEVEL)
+ intc_out_be32(intc + IAR, mask);
+ }
+}
+
+static struct hw_interrupt_type xilinx_intc = {
+ "Xilinx Interrupt Controller",
+ NULL,
+ NULL,
+ xilinx_intc_enable,
+ xilinx_intc_disable,
+ xilinx_intc_disable_and_ack,
+ xilinx_intc_end,
+ 0
+};
+
+int
+xilinx_pic_get_irq(struct pt_regs *regs)
+{
+ int irq;
+
+ /*
+ * NOTE: This function is the one that needs to be improved in
+ * order to handle multiple interrupt controllers. It currently
+ * is hardcoded to check for interrupts only on the first INTC.
+ */
+
+ irq = intc_in_be32(intc + IVR);
+ if (irq != -1)
+ irq = irq;
+
+ pr_debug("get_irq: %d\n", irq);
+
+ return (irq);
+}
+
+void __init
+ppc4xx_pic_init(void)
+{
+ int i;
+
+ /*
+ * NOTE: The assumption here is that NR_IRQS is 32 or less
+ * (NR_IRQS is 32 for PowerPC 405 cores by default).
+ */
+#if (NR_IRQS > 32)
+#error NR_IRQS > 32 not supported
+#endif
+
+#if XPAR_XINTC_USE_DCR == 0
+ intc = ioremap(XPAR_INTC_0_BASEADDR, 32);
+
+ printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX mapped to 0x%08lX\n",
+ (unsigned long) XPAR_INTC_0_BASEADDR, (unsigned long) intc);
+#else
+ printk(KERN_INFO "Xilinx INTC #0 at 0x%08lX (DCR)\n",
+ (unsigned long) XPAR_INTC_0_BASEADDR);
+#endif
+
+ /*
+ * Disable all external interrupts until they are
+ * explicity requested.
+ */
+ intc_out_be32(intc + IER, 0);
+
+ /* Acknowledge any pending interrupts just in case. */
+ intc_out_be32(intc + IAR, ~(u32) 0);
+
+ /* Turn on the Master Enable. */
+ intc_out_be32(intc + MER, 0x3UL);
+
+ ppc_md.get_irq = xilinx_pic_get_irq;
+
+ for (i = 0; i < NR_IRQS; ++i) {
+ irq_desc[i].handler = &xilinx_intc;
+
+ if (XPAR_INTC_0_KIND_OF_INTR & (0x00000001 << i))
+ irq_desc[i].status &= ~IRQ_LEVEL;
+ else
+ irq_desc[i].status |= IRQ_LEVEL;
+ }
+}