aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge
diff options
context:
space:
mode:
authorGreg Watson <jarrah@users.sourceforge.net>2003-06-09 21:29:23 +0000
committerGreg Watson <jarrah@users.sourceforge.net>2003-06-09 21:29:23 +0000
commit032211593248d4d9a569ecfd269a2433ea5b1c7c (patch)
tree0cd5ca04ea4add897a92f4df7c6cc37730d8daf8 /src/northbridge
parentfd958cea68e7df40c47a3a97762d2433b5a52819 (diff)
Moved from freebios
git-svn-id: svn://svn.coreboot.org/coreboot/trunk@862 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1
Diffstat (limited to 'src/northbridge')
-rw-r--r--src/northbridge/motorola/mpc107/epic.c517
-rw-r--r--src/northbridge/motorola/mpc107/i2c.c99
-rw-r--r--src/northbridge/motorola/mpc107/i2c.h57
-rw-r--r--src/northbridge/motorola/mpc107/meminfo.c202
-rw-r--r--src/northbridge/motorola/mpc107/mpc107.c487
-rw-r--r--src/northbridge/motorola/mpc107/mpc107.h122
-rw-r--r--src/northbridge/motorola/mpc107/mpc107_smp.c27
7 files changed, 1511 insertions, 0 deletions
diff --git a/src/northbridge/motorola/mpc107/epic.c b/src/northbridge/motorola/mpc107/epic.c
new file mode 100644
index 0000000000..5db7c107ce
--- /dev/null
+++ b/src/northbridge/motorola/mpc107/epic.c
@@ -0,0 +1,517 @@
+/**************************************************
+ *
+ * copyright @ motorola, 1999
+ *
+ *************************************************/
+#include <pci.h>
+#include <printk.h>
+#include <northbridge/motorola/mpc107/epic.h>
+
+extern struct pci_ops pci_direct_ppc;
+
+typedef void (*VOIDFUNCPTR) (void); /* ptr to function returning void */
+struct SrcVecTable SrcVecTable[MAXVEC] = /* Addr/Vector cross-reference tbl */
+ {
+ { EPIC_EX_INT0_VEC_REG, "External Direct/Serial Source 0"},
+ { EPIC_EX_INT1_VEC_REG, "External Direct/Serial Source 1"},
+ { EPIC_EX_INT2_VEC_REG, "External Direct/Serial Source 2"},
+ { EPIC_EX_INT3_VEC_REG, "External Direct/Serial Source 3"},
+ { EPIC_EX_INT4_VEC_REG, "External Direct/Serial Source 4"},
+
+ { EPIC_SR_INT5_VEC_REG, "External Serial Source 5"},
+ { EPIC_SR_INT6_VEC_REG, "External Serial Source 6"},
+ { EPIC_SR_INT7_VEC_REG, "External Serial Source 7"},
+ { EPIC_SR_INT8_VEC_REG, "External Serial Source 8"},
+ { EPIC_SR_INT9_VEC_REG, "External Serial Source 9"},
+ { EPIC_SR_INT10_VEC_REG, "External Serial Source 10"},
+ { EPIC_SR_INT11_VEC_REG, "External Serial Source 11"},
+ { EPIC_SR_INT12_VEC_REG, "External Serial Source 12"},
+ { EPIC_SR_INT13_VEC_REG, "External Serial Source 13"},
+ { EPIC_SR_INT14_VEC_REG, "External Serial Source 14"},
+ { EPIC_SR_INT15_VEC_REG, "External Serial Source 15"},
+
+ { EPIC_I2C_INT_VEC_REG, "Internal I2C Source"},
+ { EPIC_DMA0_INT_VEC_REG, "Internal DMA0 Source"},
+ { EPIC_DMA1_INT_VEC_REG, "Internal DMA1 Source"},
+ { EPIC_MSG_INT_VEC_REG, "Internal Message Source"},
+ };
+
+VOIDFUNCPTR intVecTbl[MAXVEC]; /* Interrupt vector table */
+
+
+/****************************************************************************
+* epicInit - Initialize the EPIC registers
+*
+* This routine resets the Global Configuration Register, thus it:
+* - Disables all interrupts
+* - Sets epic registers to reset values
+* - Sets the value of the Processor Current Task Priority to the
+* highest priority (0xF).
+* epicInit then sets the EPIC operation mode to Mixed Mode (vs. Pass
+* Through or 8259 compatible mode).
+*
+* If IRQType (input) is Direct IRQs:
+* - IRQType is written to the SIE bit of the EPIC Interrupt
+* Configuration register (ICR).
+* - clkRatio is ignored.
+* If IRQType is Serial IRQs:
+* - both IRQType and clkRatio will be written to the ICR register
+*/
+
+void epicInit
+ (
+ unsigned int IRQType, /* Direct or Serial */
+ unsigned int clkRatio /* Clk Ratio for Serial IRQs */
+ )
+ {
+ ULONG tmp;
+
+ tmp = sysEUMBBARRead(EPIC_GLOBAL_REG);
+ tmp |= 0xa0000000; /* Set the Global Conf. register */
+ sysEUMBBARWrite(EPIC_GLOBAL_REG, tmp);
+ /*
+ * Wait for EPIC to reset - CLH
+ */
+ while( (sysEUMBBARRead(EPIC_GLOBAL_REG) & 0x80000000) == 1);
+ sysEUMBBARWrite(EPIC_GLOBAL_REG, 0x20000000);
+ tmp = sysEUMBBARRead(EPIC_INT_CONF_REG); /* Read interrupt conf. reg */
+
+ if (IRQType == EPIC_DIRECT_IRQ) /* direct mode */
+ sysEUMBBARWrite(EPIC_INT_CONF_REG, tmp & 0xf7ffffff);
+ else /* Serial mode */
+ {
+ tmp = (clkRatio << 28) | 0x08000000; /* Set clock ratio */
+ sysEUMBBARWrite(EPIC_INT_CONF_REG, tmp);
+ }
+
+ while (epicIntAck() != 0xff) /* Clear all pending interrupts */
+ epicEOI();
+}
+
+/****************************************************************************
+ * epicIntEnable - Enable an interrupt source
+ *
+ * This routine clears the mask bit of an external, an internal or
+ * a Timer register to enable the interrupt.
+ *
+ * RETURNS: None
+ */
+void epicIntEnable(int intVec)
+{
+ ULONG tmp;
+ ULONG srAddr;
+
+ srAddr = SrcVecTable[intVec].srcAddr; /* Retrieve src Vec/Prio register */
+ tmp = sysEUMBBARRead(srAddr);
+ tmp &= ~EPIC_VEC_PRI_MASK; /* Clear the mask bit */
+ tmp |= (EPIC_VEC_PRI_DFLT_PRI << 16); /* Set priority to Default - CLH */
+ tmp |= intVec; /* Set Vector number */
+ sysEUMBBARWrite(srAddr, tmp);
+
+ return;
+ }
+
+/****************************************************************************
+ * epicIntDisable - Disable an interrupt source
+ *
+ * This routine sets the mask bit of an external, an internal or
+ * a Timer register to disable the interrupt.
+ *
+ * RETURNS: OK or ERROR
+ *
+ */
+
+void epicIntDisable
+ (
+ int intVec /* Interrupt vector number */
+ )
+ {
+
+ ULONG tmp, srAddr;
+
+ srAddr = SrcVecTable[intVec].srcAddr;
+ tmp = sysEUMBBARRead(srAddr);
+ tmp |= 0x80000000; /* Set the mask bit */
+ sysEUMBBARWrite(srAddr, tmp);
+ return;
+ }
+
+/****************************************************************************
+ * epicIntSourceConfig - Set properties of an interrupt source
+ *
+ * This function sets interrupt properites (Polarity, Sense, Interrupt
+ * Prority, and Interrupt Vector) of an Interrupt Source. The properties
+ * can be set when the current source is not in-request or in-service,
+ * which is determined by the Activity bit. This routine return ERROR
+ * if the the Activity bit is 1 (in-request or in-service).
+ *
+ * This function assumes that the Source Vector/Priority register (input)
+ * is a valid address.
+ *
+ * RETURNS: OK or ERROR
+ */
+
+int epicIntSourceConfig
+ (
+ int Vect, /* interrupt source vector number */
+ int Polarity, /* interrupt source polarity */
+ int Sense, /* interrupt source Sense */
+ int Prio /* interrupt source priority */
+ )
+
+ {
+ ULONG tmp, newVal;
+ ULONG actBit, srAddr;
+
+ srAddr = SrcVecTable[Vect].srcAddr;
+ tmp = sysEUMBBARRead(srAddr);
+ actBit = (tmp & 40000000) >> 30; /* retrieve activity bit - bit 30 */
+ if (actBit == 1)
+ return ERROR;
+
+ tmp &= 0xff30ff00; /* Erase previously set P,S,Prio,Vector bits */
+ newVal = (Polarity << 23) | (Sense << 22) | (Prio << 16) | Vect;
+ sysEUMBBARWrite(srAddr, tmp | newVal );
+ return (OK);
+ }
+
+/****************************************************************************
+ * epicIntAck - acknowledge an interrupt
+ *
+ * This function reads the Interrupt acknowldge register and return
+ * the vector number of the highest pending interrupt.
+ *
+ * RETURNS: Interrupt Vector number.
+ */
+
+unsigned int epicIntAck(void)
+{
+ return(sysEUMBBARRead( EPIC_PROC_INT_ACK_REG ));
+}
+
+/****************************************************************************
+ * epicEOI - signal an end of interrupt
+ *
+ * This function writes 0x0 to the EOI register to signal end of interrupt.
+ * It is usually called after an interrupt routine is served.
+ *
+ * RETURNS: None
+ */
+
+void epicEOI(void)
+ {
+ sysEUMBBARWrite(EPIC_PROC_EOI_REG, 0x0);
+ }
+
+/****************************************************************************
+ * epicCurTaskPrioSet - sets the priority of the Processor Current Task
+ *
+ * This function should be called after epicInit() to lower the priority
+ * of the processor current task.
+ *
+ * RETURNS: OK or ERROR
+ */
+
+int epicCurTaskPrioSet
+ (
+ int prioNum /* New priority value */
+ )
+ {
+
+ if ( (prioNum < 0) || (prioNum > 0xF))
+ return ERROR;
+ sysEUMBBARWrite(EPIC_PROC_CTASK_PRI_REG, prioNum);
+ return OK;
+ }
+
+
+/************************************************************************
+ * function: epicIntTaskGet
+ *
+ * description: Get value of processor current interrupt task priority register
+ *
+ * note:
+ ***********************************************************************/
+unsigned char epicIntTaskGet()
+{
+ /* get the interrupt task priority register */
+ ULONG reg;
+ unsigned char rec;
+
+ reg = sysEUMBBARRead( EPIC_PROC_CTASK_PRI_REG );
+ rec = ( reg & 0x0F );
+ return rec;
+}
+
+
+/**************************************************************
+ * function: epicISR
+ *
+ * description: EPIC service routine called by the core exception
+ * at 0x500
+ *
+ * note:
+ **************************************************************/
+unsigned int epicISR(void)
+{
+ return 0;
+}
+
+
+/************************************************************
+ * function: epicModeGet
+ *
+ * description: query EPIC mode, return 0 if pass through mode
+ * return 1 if mixed mode
+ *
+ * note:
+ *************************************************************/
+unsigned int epicModeGet(void)
+{
+ ULONG val;
+
+ val = sysEUMBBARRead( EPIC_GLOBAL_REG );
+ return (( val & 0x20000000 ) >> 29);
+}
+
+
+/*********************************************
+ * function: epicConfigGet
+ *
+ * description: Get the EPIC interrupt Configuration
+ * return 0 if not error, otherwise return 1
+ *
+ * note:
+ ********************************************/
+void epicConfigGet( unsigned int *clkRatio, unsigned int *serEnable)
+{
+ ULONG val;
+
+ val = sysEUMBBARRead( EPIC_INT_CONF_REG );
+ *clkRatio = ( val & 0x70000000 ) >> 28;
+ *serEnable = ( val & 0x8000000 ) >> 27;
+}
+
+
+/*******************************************************************
+ * sysEUMBBARRead - Read a 32-bit EUMBBAR register
+ *
+ * This routine reads the content of a register in the Embedded
+ * Utilities Memory Block, and swaps to big endian before returning
+ * the value.
+ *
+ * RETURNS: The content of the specified EUMBBAR register.
+ */
+
+ULONG sysEUMBBARRead
+ (
+ ULONG regNum
+ )
+ {
+ u32 temp;
+
+ pci_direct_ppc.read_dword(0, 0, regNum, &temp);
+ return ( temp );
+ }
+
+/*******************************************************************
+ * sysEUMBBARWrite - Write a 32-bit EUMBBAR register
+ *
+ * This routine swaps the value to little endian then writes it to
+ * a register in the Embedded Utilities Memory Block address space.
+ *
+ * RETURNS: N/A
+ */
+
+void sysEUMBBARWrite
+ (
+ ULONG regNum, /* EUMBBAR register address */
+ u32 regVal /* Value to be written */
+ )
+ {
+
+ pci_direct_ppc.read_dword(0, 0, regNum, &regVal);
+ return ;
+ }
+
+
+/********************************************************
+ * function: epicVendorId
+ *
+ * description: return the EPIC Vendor Identification
+ * register:
+ *
+ * siliccon version, device id, and vendor id
+ *
+ * note:
+ ********************************************************/
+void epicVendorId
+ (
+ unsigned int *step,
+ unsigned int *devId,
+ unsigned int *venId
+ )
+ {
+ ULONG val;
+ val = sysEUMBBARRead( EPIC_VENDOR_ID_REG );
+ *step = ( val & 0x00FF0000 ) >> 16;
+ *devId = ( val & 0x0000FF00 ) >> 8;
+ *venId = ( val & 0x000000FF );
+ }
+
+/**************************************************
+ * function: epicFeatures
+ *
+ * description: return the number of IRQ supported,
+ * number of CPU, and the version of the
+ * OpenEPIC
+ *
+ * note:
+ *************************************************/
+void epicFeatures
+ (
+ unsigned int *noIRQs,
+ unsigned int *noCPUs,
+ unsigned int *verId
+ )
+ {
+ ULONG val;
+
+ val = sysEUMBBARRead( EPIC_FEATURES_REG );
+ *noIRQs = ( val & 0x07FF0000 ) >> 16;
+ *noCPUs = ( val & 0x00001F00 ) >> 8;
+ *verId = ( val & 0x000000FF );
+}
+
+
+/*********************************************************
+ * function: epciTmFrequncySet
+ *
+ * description: Set the timer frequency reporting register
+ ********************************************************/
+void epicTmFrequencySet( unsigned int frq )
+{
+ sysEUMBBARWrite(EPIC_TM_FREQ_REG, frq);
+}
+
+/*******************************************************
+ * function: epicTmFrequncyGet
+ *
+ * description: Get the current value of the Timer Frequency
+ * Reporting register
+ *
+ ******************************************************/
+unsigned int epicTmFrequencyGet(void)
+{
+ return( sysEUMBBARRead(EPIC_TM_FREQ_REG)) ;
+}
+
+
+/****************************************************
+ * function: epicTmBaseSet
+ *
+ * description: Set the #n global timer base count register
+ * return 0 if no error, otherwise return 1.
+ *
+ * note:
+ ****************************************************/
+unsigned int epicTmBaseSet
+ (
+ ULONG srcAddr, /* Address of the Timer Base register */
+ unsigned int cnt, /* Base count */
+ unsigned int inhibit /* 1 - count inhibit */
+ )
+{
+
+ unsigned int val = 0x80000000;
+ /* First inhibit counting the timer */
+ sysEUMBBARWrite(srcAddr, val) ;
+
+ /* set the new value */
+ val = (cnt & 0x7fffffff) | ((inhibit & 0x1) << 31);
+ sysEUMBBARWrite(srcAddr, val) ;
+ return 0;
+}
+
+/***********************************************************************
+ * function: epicTmBaseGet
+ *
+ * description: Get the current value of the global timer base count register
+ * return 0 if no error, otherwise return 1.
+ *
+ * note:
+ ***********************************************************************/
+unsigned int epicTmBaseGet( ULONG srcAddr, unsigned int *val )
+{
+ *val = sysEUMBBARRead( srcAddr );
+ *val = *val & 0x7fffffff;
+ return 0;
+}
+
+/***********************************************************
+ * function: epicTmCountGet
+ *
+ * description: Get the value of a given global timer
+ * current count register
+ * return 0 if no error, otherwise return 1
+ * note:
+ **********************************************************/
+unsigned int epicTmCountGet( ULONG srcAddr, unsigned int *val )
+{
+ *val = sysEUMBBARRead( srcAddr );
+ *val = *val & 0x7fffffff;
+ return 0;
+}
+
+
+
+/***********************************************************
+ * function: epicTmInhibit
+ *
+ * description: Stop counting of a given global timer
+ * return 0 if no error, otherwise return 1
+ *
+ * note:
+ ***********************************************************/
+unsigned int epicTmInhibit( unsigned int srcAddr )
+{
+ ULONG val;
+
+ val = sysEUMBBARRead( srcAddr );
+ val |= 0x80000000;
+ sysEUMBBARWrite( srcAddr, val );
+ return 0;
+}
+
+/******************************************************************
+ * function: epicTmEnable
+ *
+ * description: Enable counting of a given global timer
+ * return 0 if no error, otherwise return 1
+ *
+ * note:
+ *****************************************************************/
+unsigned int epicTmEnable( ULONG srcAddr )
+{
+ ULONG val;
+
+ val = sysEUMBBARRead( srcAddr );
+ val &= 0x7fffffff;
+ sysEUMBBARWrite( srcAddr, val );
+ return 0;
+}
+
+void epicSourcePrint(int Vect)
+ {
+ ULONG srcVal;
+
+ srcVal = sysEUMBBARRead(SrcVecTable[Vect].srcAddr);
+ printk_info("%s\n", SrcVecTable[Vect].srcName);
+ printk_info("Address = 0x%lx\n", SrcVecTable[Vect].srcAddr);
+ printk_info("Vector = %ld\n", (srcVal & 0x000000FF) );
+ printk_info("Mask = %ld\n", srcVal >> 31);
+ printk_info("Activitiy = %ld\n", (srcVal & 40000000) >> 30);
+ printk_info("Polarity = %ld\n", (srcVal & 0x00800000) >> 23);
+ printk_info("Sense = %ld\n", (srcVal & 0x00400000) >> 22);
+ printk_info("Priority = %ld\n", (srcVal & 0x000F0000) >> 16);
+ }
diff --git a/src/northbridge/motorola/mpc107/i2c.c b/src/northbridge/motorola/mpc107/i2c.c
new file mode 100644
index 0000000000..4ead02331b
--- /dev/null
+++ b/src/northbridge/motorola/mpc107/i2c.c
@@ -0,0 +1,99 @@
+/* $Id$
+ * (C) Copyright 2002
+ * Humboldt Solutions Ltd, <adrian@humboldt.co.uk>
+ *
+ * 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 <types.h>
+#include <stdlib.h>
+#include <string.h>
+#include "i2c.h"
+
+static i2c_bus *first_i2c = NULL;
+
+#if 0
+int register_i2c_bus(const i2c_fn *fn, char *tag, void *data)
+{
+ i2c_bus *bus = malloc (sizeof (i2c_bus));
+
+ if (bus)
+ {
+ bus->fn = fn;
+ bus->tag = tag;
+ bus->data = data;
+ bus->next = first_i2c;
+ first_i2c = bus;
+ return 0;
+ }
+ return -1;
+}
+#endif
+
+i2c_bus *find_i2c_bus(const char *name)
+{
+ int len;
+
+ if (! name)
+ return first_i2c;
+
+ if (first_i2c)
+ {
+ i2c_bus *i2c;
+
+ len = strlen(name);
+
+ for (i2c = first_i2c; i2c; i2c = i2c->next)
+ if (strlen(i2c->tag) == len && memcmp (name, i2c->tag, len) == 0)
+ return i2c;
+ }
+ return NULL;
+}
+
+void i2c_start(struct i2c_bus *bus)
+{
+ if (! bus)
+ bus = first_i2c;
+
+ bus->fn->start(bus);
+}
+
+void i2c_stop(struct i2c_bus *bus)
+{
+ if (! bus)
+ bus = first_i2c;
+
+ bus->fn->stop(bus);
+}
+
+int i2c_master_write(struct i2c_bus *bus, int target, int address,
+ const u8 *data, int length)
+{
+ if (! bus)
+ bus = first_i2c;
+
+ return bus->fn->master_write(bus, target, address, data, length);
+}
+
+int i2c_master_read(struct i2c_bus *bus, int target, int address,
+ u8 *data, int length)
+{
+ if (! bus)
+ bus = first_i2c;
+
+ return bus->fn->master_read(bus, target, address, data, length);
+}
+
diff --git a/src/northbridge/motorola/mpc107/i2c.h b/src/northbridge/motorola/mpc107/i2c.h
new file mode 100644
index 0000000000..868c94a082
--- /dev/null
+++ b/src/northbridge/motorola/mpc107/i2c.h
@@ -0,0 +1,57 @@
+/* $Id$
+ * (C) Copyright 2002
+ * Humboldt Solutions Ltd, <adrian@humboldt.co.uk>
+ *
+ * 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
+ */
+
+#ifndef _I2C_H
+#define _I2C_H
+
+struct i2c_bus;
+
+typedef struct i2c_fn
+{
+ void (* start)(struct i2c_bus *bus);
+ void (* stop)(struct i2c_bus *bus);
+ int (* master_write)(struct i2c_bus *bus, int target, int address,
+ const u8 *data, int length);
+ int (* master_read)(struct i2c_bus *bus, int target, int address,
+ u8 *data, int length);
+} i2c_fn;
+
+typedef struct i2c_bus
+{
+ const i2c_fn *fn;
+ char *tag;
+ void *data;
+ struct i2c_bus *next;
+} i2c_bus;
+
+i2c_bus *find_i2c_bus(const char *name);
+int register_i2c_bus(const i2c_fn *fn, char *tag, void *data);
+
+void i2c_start(struct i2c_bus *bus);
+void i2c_stop(struct i2c_bus *bus);
+int i2c_master_write(struct i2c_bus *bus, int target, int address,
+ const u8 *data, int length);
+int i2c_master_read(struct i2c_bus *bus, int target, int address,
+ u8 *data, int length);
+void init_i2c_nvram(const char *i2c_tag);
+
+extern i2c_fn mpc107_i2c_fn;
+
+#endif
diff --git a/src/northbridge/motorola/mpc107/meminfo.c b/src/northbridge/motorola/mpc107/meminfo.c
new file mode 100644
index 0000000000..7245fc419d
--- /dev/null
+++ b/src/northbridge/motorola/mpc107/meminfo.c
@@ -0,0 +1,202 @@
+/*
+ * (C) Copyright 2001
+ * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
+ *
+ * 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 <printk.h>
+#include "mpc107.h"
+
+void
+sdram_dimm_to_bank_info(const char *data, sdram_dimm_info *dimm, int verbose)
+{
+ sdram_bank_info *bank1 = dimm->bank1;
+ sdram_bank_info *bank2 = dimm->bank2;
+ unsigned char csum = 0;
+ unsigned char x;
+ int i;
+ int no_cas_latencies = 0;
+ char latency[3];
+
+ /* Mark banks initially broken */
+ bank1->size = 0;
+ bank2->size = 0;
+
+ if (data[0] < 64)
+ {
+ if (verbose)
+ printk_info("SPD data too short\n");
+ return;
+ }
+
+ for(i = 0; i < 63; i++)
+ csum += data[i];
+
+ if (csum != data[63])
+ {
+ if (verbose)
+ printk_info("Broken checksum\n");
+ return;
+ }
+
+ if (data[2] != 0x04)
+ {
+ if (verbose)
+ printk_info("SDRAM Only\n");
+ return;
+ }
+
+ bank1->row_bits = data[3] & 0x0f;
+ if (data[3] >> 4)
+ bank2->row_bits = data[3] >> 4;
+ else
+ bank2->row_bits = bank1->row_bits;
+
+ bank1->internal_banks = bank2->internal_banks = data[17];
+
+ bank1->col_bits = data[4] & 0x0f;
+ if (data[4] >> 4)
+ bank2->col_bits = data[4] >> 4;
+ else
+ bank2->col_bits = bank1->col_bits;
+
+ if (data[7] || (data[6] != 80 && data[6] != 72 && data[6] != 64))
+ {
+ if (verbose)
+ printk_info("Data width incorrect\n");
+ return;
+ }
+
+ if (data[8] != 0x01)
+ {
+ if (verbose)
+ printk_info("3.3V TTL DIMMS only\n");
+ return;
+ }
+
+ /* Extract CAS latencies in reverse order, as we only get info on
+ the highest ones. */
+ x = data[18];
+ for(i = 7; i > 0; i--)
+ {
+ if (x & 0x40)
+ {
+ if (no_cas_latencies < 3)
+ latency[no_cas_latencies] = i;
+ no_cas_latencies++;
+ }
+ x <<= 1;
+ }
+
+ /* Now fill in other timings - we're most interested in the lowest
+ CAS latency, so we shuffle data to put that first. */
+ for(i = no_cas_latencies; i >= 0; i--)
+ bank1->cas_latency[no_cas_latencies - i - 1] =
+ bank2->cas_latency[no_cas_latencies - i - 1] =
+ latency[i];
+ for(i = no_cas_latencies; i < 3; i++)
+ bank1->cas_latency[i] = bank2->cas_latency[i] = 0;
+
+ /* Store values for the highest cas latency */
+ bank1->cycle_time[no_cas_latencies - 1] =
+ bank2->cycle_time[no_cas_latencies- 1] =
+ 100 * (data[9] >> 4) + 10 * (data[9] & 0xf);
+ bank1->access_time[no_cas_latencies - 1] =
+ bank2->access_time[no_cas_latencies - 1] =
+ 100 * (data[10] >> 4) + 10 * (data[10] & 0xf);
+ /* Then the second highest */
+ if (no_cas_latencies > 1)
+ {
+ bank1->cycle_time[no_cas_latencies - 2] =
+ bank2->cycle_time[no_cas_latencies- 2] =
+ 100 * (data[23] >> 4) + 10 * (data[23] & 0xf);
+ bank1->access_time[no_cas_latencies - 2] =
+ bank2->access_time[no_cas_latencies - 2] =
+ 100 * (data[24] >> 4) + 10 * (data[24] & 0xf);
+ }
+ /* Then the third highest */
+ if (no_cas_latencies > 2)
+ {
+ bank1->cycle_time[no_cas_latencies - 3] =
+ bank2->cycle_time[no_cas_latencies- 3] =
+ 100 * (data[25] >> 2) + 25 * (data[25] & 0x3);
+ bank1->access_time[no_cas_latencies - 3] =
+ bank2->access_time[no_cas_latencies - 3] =
+ 100 * (data[26] >> 2) + 25 * (data[26] & 0x3);
+ }
+ if (verbose)
+ for(i = 0; i < no_cas_latencies; i++)
+ printk_info("CL %d: cycle %dns access %dns\n",
+ bank1->cas_latency[i], bank1->cycle_time[i] / 100,
+ bank1->access_time[i] / 100);
+
+ /* Other timings */
+ bank1->min_back_to_back = bank2->min_back_to_back = data[15];
+ bank1->min_row_precharge = bank2->min_row_precharge = data[27];
+ bank1->min_active_to_active = bank2->min_active_to_active = data[28];
+ bank1->min_ras_to_cas = bank2->min_ras_to_cas = data[29];
+ bank1->min_ras = bank2->min_ras = data[30];
+
+ /* Error detection type */
+ bank1->error_detect = bank2->error_detect = data[11];
+
+ /* Crucial row sizes - these mark the data as valid */
+ for(i = 7; i >= 0; i--)
+ {
+ if (data[31] & (1 << i))
+ {
+ bank1->size = (4*1024*1024) << i;
+ break;
+ }
+ }
+ if (data[5] > 1)
+ {
+ for(i-- ; i >= 0; i--)
+ {
+ if (data[31] & (1 << i))
+ {
+ bank2->size = (4*1024*1024) << i;
+ break;
+ }
+ }
+ if (! bank2->size)
+ bank2->size = bank1->size;
+ }
+ dimm->size = bank1->size + bank2->size;
+}
+
+void
+print_sdram_bank_info(const sdram_bank_info *bank)
+{
+ printk_info("Bank %d: %dMB\n", bank->number, bank->size / (1024*1024));
+}
+
+static const char *error_types[] = {"", "Parity ", "ECC "};
+
+void
+print_sdram_dimm_info(const sdram_dimm_info *dimm)
+{
+ printk_info("Dimm %d: ", dimm->number);
+ if (dimm->size)
+ printk_info("%dMB CL%d (%s): Running at CL%d %s\n",
+ dimm->size / (1024*1024), dimm->bank1->cas_latency[0],
+ dimm->part_number,
+ dimm->bank1->actual_cas,
+ error_types[dimm->bank1->actual_detect]);
+ else
+ printk_info("(none)\n");
+}
diff --git a/src/northbridge/motorola/mpc107/mpc107.c b/src/northbridge/motorola/mpc107/mpc107.c
new file mode 100644
index 0000000000..76f368a9a5
--- /dev/null
+++ b/src/northbridge/motorola/mpc107/mpc107.c
@@ -0,0 +1,487 @@
+/*
+ * (C) Copyright 2001
+ * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
+ *
+ * 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 <bsp.h>
+#include <ppc.h>
+#include <pci.h>
+#include <mem.h>
+#include <types.h>
+#include <string.h>
+#include <printk.h>
+#include <arch/io.h>
+#include "i2c.h"
+#include "mpc107.h"
+#include <timer.h>
+
+#define NUM_DIMMS 1
+#define NUM_BANKS 2
+
+extern struct pci_ops pci_direct_ppc;
+
+struct mem_range *
+getmeminfo(void)
+{
+ int i;
+ sdram_dimm_info dimm[NUM_DIMMS];
+ sdram_bank_info bank[NUM_BANKS];
+ static struct mem_range meminfo;
+
+ hostbridge_probe_dimms(NUM_DIMMS, dimm, bank);
+
+ meminfo.basek = 0;
+ meminfo.sizek = 0;
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ meminfo.sizek += bank[i].size;
+ }
+
+ meminfo.sizek >>= 10;
+
+ return &meminfo;
+}
+
+/*
+ * Memory is already turned on, but with pessimistic settings. Now
+ * we optimize settings to the actual memory configuration.
+ */
+unsigned
+mpc107_config_memory(void)
+{
+ sdram_dimm_info sdram_dimms[NUM_DIMMS];
+ sdram_bank_info sdram_banks[NUM_BANKS];
+
+ hostbridge_probe_dimms(NUM_DIMMS, sdram_dimms, sdram_banks);
+ return hostbridge_config_memory(NUM_BANKS, sdram_banks, 2);
+}
+
+/*
+ * Configure memory settings.
+ */
+unsigned long
+hostbridge_config_memory(int no_banks, sdram_bank_info * bank, int for_real)
+{
+ int i, j;
+ char ignore[8];
+ /* Convert bus clock to cycle time in 100ns units */
+ unsigned cycle_time = 10 * (2500000000U / bsp_clock_speed());
+ /* Approximate */
+ unsigned access_time = cycle_time - 300;
+ unsigned cas_latency = 0;
+ unsigned rdlat;
+ unsigned refint;
+ unsigned refrec;
+ unsigned acttorw, acttopre;
+ unsigned pretoact, bstopre;
+ enum sdram_error_detect error_detect;
+ u32 mccr1;
+ u32 mccr2;
+ u32 mccr3;
+ u32 mccr4;
+ u8 bank_enable;
+ u32 memstart1, memstart2;
+ u32 extmemstart1, extmemstart2;
+ u32 memend1, memend2;
+ u32 extmemend1, extmemend2;
+ u32 address;
+
+ /* Set up the ignore mask */
+ for(i = 0; i < no_banks; i++)
+ ignore[i] = (bank[i].size == 0);
+
+ /* Pick best CAS latency possible */
+ for (i = 0; i < no_banks; i++)
+ {
+ if (! ignore[i])
+ {
+ for (j = 0; j < 3; j++)
+ {
+ if (cycle_time >= bank[i].cycle_time[j] &&
+ access_time >= bank[i].access_time[j])
+ {
+ cas_latency = bank[i].cas_latency[j];
+ break;
+ }
+ }
+ }
+ }
+ if (!cas_latency)
+ return 0;
+
+ /* For various parameters there is a risk of clashing between banks */
+ error_detect = (for_real > 1) ? ERRORS_ECC : ERRORS_NONE;
+ for (i = 0; i < no_banks; i++)
+ {
+ if (! ignore[i])
+ {
+ {
+ for (j = 0; j < 3; j++)
+ if (bank[i].cas_latency[j] == cas_latency)
+ break;
+ if (j == 3)
+ {
+ ignore[i] = 1;
+ if (! for_real)
+ printk_info("Disabling memory bank %d (cas latency)\n", i);
+ }
+ if (bank[i].error_detect < error_detect)
+ error_detect = bank[i].error_detect;
+ }
+ }
+ }
+
+ /* Read in configuration of port X */
+ pci_direct_ppc.read_dword(0, 0, 0xf0, &mccr1);
+ pci_direct_ppc.read_dword(0, 0, 0xf4, &mccr2);
+ pci_direct_ppc.read_dword(0, 0, 0xfc, &mccr4);
+ mccr1 &= 0xfff00000;
+ mccr2 &= 0xffe00000;
+ mccr3 = 0;
+ mccr4 &= 0x00230000;
+
+ pretoact = 0;
+ acttorw = 0;
+ acttopre = 0;
+ for (i = 0; i < no_banks; i++)
+ if (! ignore[i])
+ {
+ int rowcode = -1;
+ if (for_real)
+ {
+ bank[i].actual_detect = error_detect;
+ bank[i].actual_cas = cas_latency;
+ }
+
+ switch (bank[i].row_bits) {
+ case 13:
+ if (bank[i].internal_banks == 4)
+ rowcode = 2;
+ else if (bank[i].internal_banks == 2)
+ rowcode = 1;
+ break;
+ case 12:
+ if (bank[i].internal_banks == 4)
+ rowcode = 0;
+ else if (bank[i].internal_banks == 2)
+ rowcode = 1;
+ break;
+ case 11:
+ if (bank[i].internal_banks == 4)
+ rowcode = 0;
+ else if (bank[i].internal_banks == 2)
+ rowcode = 3;
+ break;
+ }
+ if (rowcode == -1) {
+ ignore[i] = 1;
+ if (! for_real)
+ printk_info("Memory bank %d disabled: row bits %d and banks %d not supported\n", i, bank[i].row_bits, bank[i].internal_banks);
+ } else
+ mccr1 |= rowcode << (2 * i);
+
+ /* Update worst case settings */
+ if (! ignore[i]) {
+ if (bank[i].min_row_precharge > pretoact)
+ pretoact = bank[i].min_row_precharge;
+ if (bank[i].min_ras_to_cas > acttorw)
+ acttorw = bank[i].min_ras_to_cas;
+ if (bank[i].min_ras > acttopre)
+ acttopre = bank[i].min_ras;
+ }
+ }
+
+ /* Now convert to clock cycles, rounding up */
+ pretoact = (100 * pretoact + cycle_time - 1) / cycle_time;
+ acttopre = (100 * acttopre + cycle_time - 1) / cycle_time;
+ acttorw = (100 * acttorw + cycle_time - 1) / cycle_time;
+ refrec = acttopre;
+ bstopre = 0x240; /* Set conservative values, because we can't derive */
+ refint = 1000;
+
+ if (error_detect == ERRORS_ECC)
+ {
+ rdlat = cas_latency + 2;
+ mccr4 |= 0x00400000;
+ mccr2 |= 0x000c0001;
+ }
+ else
+ {
+ rdlat = cas_latency + 1;
+ mccr4 |= 0x00100000;
+ }
+
+ if (pretoact > 16 || acttopre > 16 || acttorw > 16)
+ if (! for_real)
+ printk_info("Timings out of range\n");
+ mccr4 |= ((pretoact & 0x0f) << 28) | ((acttopre & 0xf) << 24) |
+ ((acttorw & 0x0f) << 4) |
+ ((bstopre & 0x003) << 18) | ((bstopre & 0x3c0) >> 6) |
+ (cas_latency << 12) | 0x00000200 /* burst length */ ;
+ mccr3 |= ((bstopre & 0x03c) << 26) |
+ ((refrec & 0x0f) << 24) | (rdlat << 20);
+ mccr2 |= refint << 2;
+ mccr1 |= 0x00080000; /* memgo */
+
+ address = 0;
+ memstart1 = memstart2 = 0;
+ extmemstart1 = extmemstart2 = 0;
+ memend1 = memend2 = 0;
+ extmemend1 = extmemend2 = 0;
+ bank_enable = 0;
+ for (i = 0; i < no_banks; i++) {
+ if (! ignore[i]) {
+ u32 end = address + bank[i].size - 1;
+ bank_enable |= 1 << i;
+ if (i < 4) {
+ memstart1 |= ((address >> 20) & 0xff) << (8 * i);
+ extmemstart1 |= ((address >> 28) & 0x03) << (8 * i);
+ memend1 |= ((end >> 20) & 0xff) << (8 * i);
+ extmemend1 |= ((end >> 28) & 0x03) << (8 * i);
+ } else {
+ int k = i - 4;
+ memstart2 |= ((address >> 20) & 0xff) << (8 * k);
+ extmemstart2 |= ((address >> 28) & 0x03) << (8 * k);
+ memend2 |= ((end >> 20) & 0xff) << (8 * k);
+ extmemend2 |= ((end >> 28) & 0x03) << (8 * k);
+ }
+ address += bank[i].size;
+ }
+ }
+
+ if (for_real)
+ {
+ pci_direct_ppc.write_byte(0, 0, 0xa0, bank_enable);
+ pci_direct_ppc.write_dword(0, 0, 0x80, memstart1);
+ pci_direct_ppc.write_dword(0, 0, 0x84, memstart2);
+ pci_direct_ppc.write_dword(0, 0, 0x88, extmemstart1);
+ pci_direct_ppc.write_dword(0, 0, 0x8c, extmemstart2);
+ pci_direct_ppc.write_dword(0, 0, 0x90, memend1);
+ pci_direct_ppc.write_dword(0, 0, 0x94, memend2);
+ pci_direct_ppc.write_dword(0, 0, 0x98, extmemend1);
+ pci_direct_ppc.write_dword(0, 0, 0x9c, extmemend2);
+
+ pci_direct_ppc.write_dword(0, 0, 0xfc, mccr4);
+ pci_direct_ppc.write_dword(0, 0, 0xf8, mccr3);
+ pci_direct_ppc.write_dword(0, 0, 0xf4, mccr2);
+ pci_direct_ppc.write_dword(0, 0, 0xf0, mccr1);
+ }
+
+ return address;
+}
+
+static int
+i2c_wait(unsigned timeout, int writing)
+{
+ u32 x;
+ while (((x = readl(MPC107_BASE + MPC107_I2CSR)) & (MPC107_I2C_CSR_MCF | MPC107_I2C_CSR_MIF))
+ != (MPC107_I2C_CSR_MCF | MPC107_I2C_CSR_MIF)) {
+ if (ticks_since_boot() > timeout)
+ return -1;
+ }
+
+ if (x & MPC107_I2C_CSR_MAL) {
+ return -1;
+ }
+ if (writing && (x & MPC107_I2C_CSR_RXAK)) {
+ printk_info("No RXAK\n");
+ /* generate stop */
+ writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR);
+ return -1;
+ }
+ writel(0, MPC107_BASE + MPC107_I2CSR);
+ return 0;
+}
+
+static void
+mpc107_i2c_start(struct i2c_bus *bus)
+{
+ /* Set clock */
+ writel(0x1031, MPC107_BASE + MPC107_I2CFDR);
+ /* Clear arbitration */
+ writel(0, MPC107_BASE + MPC107_I2CSR);
+}
+
+static void
+mpc107_i2c_stop(struct i2c_bus *bus)
+{
+ /* After last DIMM shut down I2C */
+ writel(0x0, MPC107_BASE + MPC107_I2CCR);
+}
+
+static int
+mpc107_i2c_byte_write(struct i2c_bus *bus, int target, int address, u8 data)
+{
+ unsigned timeout = ticks_since_boot() + 3 * get_hz();
+
+ /* Must wait here for clocks to start */
+ sleep_ticks(get_hz() / 40);
+ /* Start with MEN */
+ writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR);
+ /* Start as master */
+ writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_MTX, MPC107_BASE + MPC107_I2CCR);
+ /* Write target byte */
+ writel(target, MPC107_BASE + MPC107_I2CDR);
+
+ if (i2c_wait(timeout, 1) < 0)
+ return -1;
+
+ /* Write data address byte */
+ writel(address, MPC107_BASE + MPC107_I2CDR);
+
+ if (i2c_wait(timeout, 1) < 0)
+ return -1;
+
+ /* Write data byte */
+ writel(data, MPC107_BASE + MPC107_I2CDR);
+
+ if (i2c_wait(timeout, 1) < 0)
+ return -1;
+
+ /* generate stop */
+ writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR);
+ return 0;
+}
+
+static int
+mpc107_i2c_master_write(struct i2c_bus *bus, int target, int address, const u8 *data, int length)
+{
+ unsigned count;
+ for(count = 0; count < length; count++)
+ {
+ if (mpc107_i2c_byte_write(bus, target, address, data[count]) < 0)
+ return -1;
+ }
+ return count;
+}
+
+#define DIMM_LENGTH 0xfff
+
+static int
+mpc107_i2c_master_read(struct i2c_bus *bus, int target, int address,
+ u8 *data, int length)
+{
+ unsigned timeout = ticks_since_boot() + 3 * get_hz();
+ unsigned count;
+
+ /* Must wait here for clocks to start */
+ sleep_ticks(get_hz() / 40);
+ /* Start with MEN */
+ writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR);
+ /* Start as master */
+ writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_MTX, MPC107_BASE + MPC107_I2CCR);
+ /* Write target byte */
+ writel(target, MPC107_BASE + MPC107_I2CDR);
+
+ if (i2c_wait(timeout, 1) < 0)
+ return -1;
+
+ /* Write data address byte */
+ writel(address, MPC107_BASE + MPC107_I2CDR);
+
+ if (i2c_wait(timeout, 1) < 0)
+ return -1;
+
+ /* Switch to read - restart */
+ writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_MTX | MPC107_I2C_CCR_RSTA, MPC107_BASE + MPC107_I2CCR);
+ /* Write target address byte - this time with the read flag set */
+ writel(target | 1, MPC107_BASE + MPC107_I2CDR);
+
+ if (i2c_wait(timeout, 0) < 0)
+ return -1;
+
+ if (length == 1)
+ writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_TXAK, MPC107_BASE + MPC107_I2CCR);
+ else
+ writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA, MPC107_BASE + MPC107_I2CCR);
+ /* Dummy read */
+ readl(MPC107_BASE + MPC107_I2CDR);
+
+ count = 0;
+ while (count < length) {
+
+ if (i2c_wait(timeout, 0) < 0)
+ return -1;
+
+ /* Generate txack on next to last byte */
+ if (count == length - 2)
+ writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_MSTA | MPC107_I2C_CCR_TXAK, MPC107_BASE + MPC107_I2CCR);
+ /* Generate stop on last byte */
+ if (count == length - 1)
+ writel(MPC107_I2C_CCR_MEN | MPC107_I2C_CCR_TXAK, MPC107_BASE + MPC107_I2CCR);
+ data[count] = readl(MPC107_BASE + MPC107_I2CDR);
+ if (count == 0 && length == DIMM_LENGTH) {
+ if (data[0] == 0xff) {
+ printk_debug("I2C device not present\n");
+ length = 3;
+ } else {
+ length = data[0];
+ if (length < 3)
+ length = 3;
+ }
+ }
+ count++;
+ }
+
+ /* Finish with disable master */
+ writel(MPC107_I2C_CCR_MEN, MPC107_BASE + MPC107_I2CCR);
+ return length;
+}
+
+i2c_fn mpc107_i2c_fn = {
+ mpc107_i2c_start, mpc107_i2c_stop,
+ mpc107_i2c_master_write, mpc107_i2c_master_read
+};
+
+/*
+ * Find dimm information.
+ */
+void
+hostbridge_probe_dimms(int no_dimms, sdram_dimm_info *dimms, sdram_bank_info * bank)
+{
+ unsigned char data[256];
+ unsigned dimm;
+
+ printk_debug("i2c testing\n");
+ mpc107_i2c_start(NULL);
+
+ for(dimm = 0; dimm < no_dimms; dimm++)
+ {
+ dimms[dimm].number = dimm;
+ dimms[dimm].bank1 = bank + dimm*2;
+ dimms[dimm].bank2 = bank + dimm*2 + 1;
+ bank[dimm*2].size = 0;
+ bank[dimm*2+1].size = 0;
+ bank[dimm*2].number = 0;
+ bank[dimm*2+1].number = 1;
+ }
+
+
+ for (dimm = 0; dimm < no_dimms; dimm ++) {
+ unsigned limit = mpc107_i2c_master_read(NULL, 0xa0 + 2*dimm, 0,
+ data, DIMM_LENGTH);
+
+ if (limit > 3) {
+ sdram_dimm_to_bank_info(data, dimms + dimm, 0);
+ memcpy(dimms[dimm].part_number, data + 73, 18);
+ dimms[dimm].part_number[18] = 0;
+ printk_debug("Part Number: %s\n", dimms[dimm].part_number);
+ }
+ }
+
+ mpc107_i2c_stop(NULL);
+}
diff --git a/src/northbridge/motorola/mpc107/mpc107.h b/src/northbridge/motorola/mpc107/mpc107.h
new file mode 100644
index 0000000000..89352342ff
--- /dev/null
+++ b/src/northbridge/motorola/mpc107/mpc107.h
@@ -0,0 +1,122 @@
+/*
+ * (C) Copyright 2001
+ * Humboldt Solutions Ltd, adrian@humboldt.co.uk.
+ *
+ * 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
+ */
+
+#ifndef _MPC107_H
+#define _MPC107_H
+
+#ifdef ASM
+#define BMC_BASE 0x8000 /* Bridge memory controller base address */
+#else
+
+#define MPC107_BASE 0xfc000000
+
+#define MPC107_EUMBBAR 0x78
+
+#define MPC107_PIC1 0xa8
+#define MPC107_PIC1_CF_MP 0x000003
+#define MPC107_PIC1_SPEC_PCI 0x000004
+#define MPC107_PIC1_CF_APARK 0x000008
+#define MPC107_PIC1_CF_LOOP_SNOOP 0x000010
+#define MPC107_PIC1_LE_MODE 0x000020
+#define MPC107_PIC1_ST_GATH_EN 0x000040
+#define MPC107_PIC1_NO_BUS_WIDTH_CHECK 0x000080
+#define MPC107_PIC1_TEA_EN 0x000400
+#define MPC107_PIC1_MCP_EN 0x000800
+#define MPC107_PIC1_FLASH_WR_EN 0x001000
+#define MPC107_PIC1_CF_LBA_EN 0x002000
+#define MPC107_PIC1_CF_MP_ID 0x00c000
+#define MPC107_PIC1_ADDRESS_MAP 0x010000
+#define MPC107_PIC1_PROC_TYPE 0x050000
+#define MPC107_PIC1_RCS0 0x100000
+#define MPC107_PIC1_CF_BREAD_WS 0xc00000
+
+#define MPC107_I2CADR 0x3000
+#define MPC107_I2CFDR 0x3004
+#define MPC107_I2CCR 0x3008
+#define MPC107_I2CSR 0x300c
+#define MPC107_I2CDR 0x3010
+
+#define MPC107_I2C_CCR_MEN 0x80
+#define MPC107_I2C_CCR_MIEN 0x40
+#define MPC107_I2C_CCR_MSTA 0x20
+#define MPC107_I2C_CCR_MTX 0x10
+#define MPC107_I2C_CCR_TXAK 0x08
+#define MPC107_I2C_CCR_RSTA 0x04
+
+#define MPC107_I2C_CSR_MCF 0x80
+#define MPC107_I2C_CSR_MAAS 0x40
+#define MPC107_I2C_CSR_MBB 0x20
+#define MPC107_I2C_CSR_MAL 0x10
+#define MPC107_I2C_CSR_SRW 0x04
+#define MPC107_I2C_CSR_MIF 0x02
+#define MPC107_I2C_CSR_RXAK 0x01
+
+enum sdram_error_detect {
+ ERRORS_NONE, ERRORS_PARITY, ERRORS_ECC
+};
+
+typedef struct sdram_dimm_info
+{
+ unsigned size;
+ unsigned number;
+ char part_number[20];
+ struct sdram_bank_info *bank1;
+ struct sdram_bank_info *bank2;
+} sdram_dimm_info;
+
+typedef struct sdram_bank_info
+{
+ unsigned number;
+ unsigned char row_bits;
+ unsigned char internal_banks;
+ unsigned char col_bits;
+ unsigned char data_width;
+ /* Cycle and access times are stored with lowest CAS latency first. Units
+ are 0.01ns */
+ unsigned short cycle_time[3];
+ unsigned short access_time[3];
+ /* Best CAS latencies */
+ unsigned char cas_latency[3];
+ unsigned char cs_latency;
+ unsigned char we_latency;
+ unsigned char min_back_to_back;
+ unsigned char min_row_precharge;
+ unsigned char min_active_to_active;
+ unsigned char min_ras_to_cas;
+ unsigned char min_ras;
+ unsigned char burst_mask;
+ enum sdram_error_detect error_detect;
+ /* Bank size */
+ unsigned size;
+ unsigned long start;
+ unsigned long end;
+ enum sdram_error_detect actual_detect;
+ unsigned char actual_cas;
+} sdram_bank_info;
+
+void sdram_dimm_to_bank_info(const char *dimm_data, sdram_dimm_info *dimm, int verbose);
+void print_sdram_dimm_info(const sdram_dimm_info *dimm);
+void print_sdram_bank_info(const sdram_bank_info *bank);
+
+unsigned long hostbridge_config_memory(int no_banks, sdram_bank_info *bank, int for_real);
+void hostbridge_probe_dimms(int no_dimms, sdram_dimm_info *dimm, sdram_bank_info * bank);
+unsigned mpc107_config_memory(void);
+#endif
+#endif
diff --git a/src/northbridge/motorola/mpc107/mpc107_smp.c b/src/northbridge/motorola/mpc107/mpc107_smp.c
new file mode 100644
index 0000000000..408a87fddb
--- /dev/null
+++ b/src/northbridge/motorola/mpc107/mpc107_smp.c
@@ -0,0 +1,27 @@
+#include <pci.h>
+#include "mpc107.h"
+
+void
+wait_for_other_cpus(void)
+{
+}
+
+unsigned long
+this_processors_id(void)
+{
+ u32 pic1;
+
+ pcibios_read_config_dword(0, 0, MPC107_PIC1, &pic1);
+ return (pic1 & MPC107_PIC1_CF_MP_ID) >> 14;
+}
+
+unsigned long
+processor_index(unsigned long id)
+{
+ return id;
+}
+
+void
+startup_other_cpus(unsigned long *map)
+{
+}