From 032211593248d4d9a569ecfd269a2433ea5b1c7c Mon Sep 17 00:00:00 2001 From: Greg Watson Date: Mon, 9 Jun 2003 21:29:23 +0000 Subject: Moved from freebios git-svn-id: svn://svn.coreboot.org/coreboot/trunk@862 2b7e53f0-3cfb-0310-b3e9-8179ed1497e1 --- src/northbridge/motorola/mpc107/epic.c | 517 +++++++++++++++++++++++++++ src/northbridge/motorola/mpc107/i2c.c | 99 +++++ src/northbridge/motorola/mpc107/i2c.h | 57 +++ src/northbridge/motorola/mpc107/meminfo.c | 202 +++++++++++ src/northbridge/motorola/mpc107/mpc107.c | 487 +++++++++++++++++++++++++ src/northbridge/motorola/mpc107/mpc107.h | 122 +++++++ src/northbridge/motorola/mpc107/mpc107_smp.c | 27 ++ 7 files changed, 1511 insertions(+) create mode 100644 src/northbridge/motorola/mpc107/epic.c create mode 100644 src/northbridge/motorola/mpc107/i2c.c create mode 100644 src/northbridge/motorola/mpc107/i2c.h create mode 100644 src/northbridge/motorola/mpc107/meminfo.c create mode 100644 src/northbridge/motorola/mpc107/mpc107.c create mode 100644 src/northbridge/motorola/mpc107/mpc107.h create mode 100644 src/northbridge/motorola/mpc107/mpc107_smp.c (limited to 'src/northbridge/motorola') 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 +#include +#include + +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, ®Val); + 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, + * + * 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 +#include +#include +#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, + * + * 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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include "i2c.h" +#include "mpc107.h" +#include + +#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 +#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) +{ +} -- cgit v1.2.3