/*-
* Copyright (c) 2001 Matt Thomas.
* Copyright (c) 2001 Tsubai Masanari.
* Copyright (c) 1998, 1999, 2001 Internet Research Institute, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by
* Internet Research Institute, Inc.
* 4. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* This should be one per CPU but since we only support it on 750 variants it
* doesn't really matter since none of them support SMP
*/
envsys_data_t sensor;
/* This is to be called from locore.S, and nowhere else. */
void
cpu_model_init(void)
{
/*
* This is just a wrapper for backwards-compatibility, and will
* probably be garbage-collected in the near future.
*/
cpu_features_probe();
cpu_features_enable();
}
ci = &cpu_info[id];
#ifndef MULTIPROCESSOR
/*
* If this isn't the primary CPU, print an error message
* and just bail out.
*/
if (id != 0) {
aprint_naive("\n");
aprint_normal(": ID %d\n", id);
aprint_normal_dev(self,
"processor off-line; "
"multiprocessor support not present in kernel\n");
return (NULL);
}
#endif
#ifdef MULTIPROCESSOR
/* Register IPI Interrupt */
if ((ipiops.ppc_establish_ipi) && (id == 0))
ipiops.ppc_establish_ipi(IST_LEVEL, IPL_HIGH, NULL);
#endif
pvr = mfpvr();
vers = (pvr >> 16) & 0xffff;
switch (id) {
case 0:
/* load my cpu_number to PIR */
switch (vers) {
case MPC601:
case MPC604:
case MPC604e:
case MPC604ev:
case MPC7400:
case MPC7410:
case MPC7447A:
case MPC7448:
case MPC7450:
case MPC7455:
case MPC7457:
mtspr(SPR_PIR, id);
}
cpu_setup(self, ci);
break;
default:
aprint_naive("\n");
if (id >= CPU_MAXNUM) {
aprint_normal(": more than %d cpus?\n", CPU_MAXNUM);
panic("cpuattach");
}
#ifndef MULTIPROCESSOR
aprint_normal(" not configured\n");
return NULL;
#else
mi_cpu_attach(ci);
break;
#endif
}
return (ci);
}
/* set the cpu number */
ci->ci_cpuid = cpu_number();
#if defined(_ARCH_PPC64)
__asm volatile("mfspr %0,%1" : "=r"(hid64_0) : "K"(SPR_HID0));
hid64_0_save = hid64_0;
#else
#if defined(PPC_OEA64_BRIDGE)
if ((oeacpufeat & OEACPU_64_BRIDGE) != 0)
hid64_0_save = hid64_0 = mfspr(SPR_HID0);
else
#endif
hid0_save = hid0 = mfspr(SPR_HID0);
#endif
cpu_probe_cache();
/*
* Configure power-saving mode.
*/
switch (vers) {
#if !defined(_ARCH_PPC64)
case MPC604:
case MPC604e:
case MPC604ev:
/*
* Do not have HID0 support settings, but can support
* MSR[POW] off
*/
powersave = 1;
break;
case MPC603:
case MPC603e:
case MPC603ev:
case MPC7400:
case MPC7410:
case MPC8240:
case MPC8245:
case MPCG2:
/* Select DOZE mode. */
hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP);
hid0 |= HID0_DOZE | HID0_DPM;
powersave = 1;
break;
case MPC750:
case IBM750FX:
case IBM750GX:
/* Select NAP mode. */
hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP);
hid0 |= HID0_NAP | HID0_DPM;
powersave = 1;
break;
case MPC7447A:
case MPC7448:
case MPC7457:
case MPC7455:
case MPC7450:
/* Enable the 7450 branch caches */
hid0 |= HID0_SGE | HID0_BTIC;
hid0 |= HID0_LRSTK | HID0_FOLD | HID0_BHT;
/* Disable BTIC on 7450 Rev 2.0 or earlier */
if (vers == MPC7450 && (pvr & 0xFFFF) <= 0x0200)
hid0 &= ~HID0_BTIC;
/* Select NAP mode. */
hid0 &= ~HID0_SLEEP;
/* XXX my quicksilver hangs if nap is enabled */
if (vers != MPC7450) {
hid0 |= HID0_NAP | HID0_DPM;
powersave = 1;
}
break;
#endif
case IBM970:
case IBM970FX:
case IBM970MP:
#if defined(_ARCH_PPC64) || defined (PPC_OEA64_BRIDGE)
#if !defined(_ARCH_PPC64)
KASSERT((oeacpufeat & OEACPU_64_BRIDGE) != 0);
#endif
hid64_0 &= ~(HID0_64_DOZE | HID0_64_NAP | HID0_64_DEEPNAP);
hid64_0 |= HID0_64_NAP | HID0_64_DPM | HID0_64_EX_TBEN |
HID0_64_TB_CTRL | HID0_64_EN_MCHK;
powersave = 1;
break;
#endif
case IBMPOWER3II:
default:
/* No power-saving mode is available. */ ;
}
#ifdef NAPMODE
switch (vers) {
case IBM750FX:
case IBM750GX:
case MPC750:
case MPC7400:
/* Select NAP mode. */
hid0 &= ~(HID0_DOZE | HID0_NAP | HID0_SLEEP);
hid0 |= HID0_NAP;
break;
}
#endif
switch (vers) {
case IBM750FX:
case IBM750GX:
case MPC750:
hid0 &= ~HID0_DBP; /* XXX correct? */
hid0 |= HID0_EMCP | HID0_BTIC | HID0_SGE | HID0_BHT;
break;
/*
* Display speed and cache configuration.
*/
switch (vers) {
case MPC604:
case MPC604e:
case MPC604ev:
case MPC750:
case IBM750FX:
case IBM750GX:
case MPC7400:
case MPC7410:
case MPC7447A:
case MPC7448:
case MPC7450:
case MPC7455:
case MPC7457:
aprint_normal_dev(self, "");
cpu_probe_speed(ci);
aprint_normal("%u.%02u MHz",
ci->ci_khz / 1000, (ci->ci_khz / 10) % 100);
switch (vers) {
case MPC7450: /* 7441 does not have L3! */
case MPC7455: /* 7445 does not have L3! */
case MPC7457: /* 7447 does not have L3! */
cpu_config_l3cr(vers);
break;
case IBM750FX:
case IBM750GX:
case MPC750:
case MPC7400:
case MPC7410:
case MPC7447A:
case MPC7448:
cpu_config_l2cr(pvr);
break;
default:
break;
}
aprint_normal("\n");
break;
}
#if NSYSMON_ENVSYS > 0
/*
* Attach MPC750 temperature sensor to the envsys subsystem.
* XXX the 74xx series also has this sensor, but it is not
* XXX supported by Motorola and may return values that are off by
* XXX 35-55 degrees C.
*/
if (vers == MPC750 || vers == IBM750FX || vers == IBM750GX)
cpu_tau_setup(ci);
#endif
#if defined(PPC_OEA64) || defined(PPC_OEA64_BRIDGE)
if (vers == IBM970MP)
init_scom_speedctl();
#endif
/*
* According to a document labeled "PVR Register Settings":
** For integrated microprocessors the PVR register inside the device
** will identify the version of the microprocessor core. You must also
** read the Device ID, PCI register 02, to identify the part and the
** Revision ID, PCI register 08, to identify the revision of the
** integrated microprocessor.
* This apparently applies to 8240/8245/8241, PVR 00810101 and 80811014
*/
if (cp->name[0] != '\0') {
n = snprintf(str, len, "%s (Revision ", cp->name);
} else {
n = snprintf(str, len, "Version %#x (Revision ", vers);
}
if (len > n) {
switch (revfmt) {
case REVFMT_MAJMIN:
snprintf(str + n, len - n, "%u.%u)", major, minor);
break;
case REVFMT_HEX:
snprintf(str + n, len - n, "0x%04x)", rev);
break;
case REVFMT_DEC:
snprintf(str + n, len - n, "%u)", rev);
break;
}
}
}
/* By The Book (numbered steps from section 3.7.1.3 of MPC7450UM) */
/*
* 1: Set all L3CR bits for final config except L3E, L3I, L3PE, and
* L3CLKEN. (also mask off reserved bits in case they were included
* in L3CR_CONFIG)
*/
l3cr &= ~(L3CR_L3E|L3CR_L3I|L3CR_L3PE|L3CR_L3CLKEN|L3CR_RESERVED);
mtspr(SPR_L3CR, l3cr);
/* 2: Set L3CR[5] (otherwise reserved bit) to 1 */
l3cr |= 0x04000000;
mtspr(SPR_L3CR, l3cr);
/* 3: Set L3CLKEN to 1*/
l3cr |= L3CR_L3CLKEN;
mtspr(SPR_L3CR, l3cr);
/* 4/5: Perform a global cache invalidate (ref section 3.7.3.6) */
__asm volatile("dssall;sync");
/* L3 cache is already disabled, no need to clear L3E */
mtspr(SPR_L3CR, l3cr|L3CR_L3I);
do {
x = mfspr(SPR_L3CR);
} while (x & L3CR_L3I);
/*
* For MP systems, the firmware may only configure the L2 cache
* on the first CPU. In this case, assume that the other CPUs
* should use the same value for L2CR.
*/
if ((l2cr & L2CR_L2E) != 0 && l2cr_config == 0) {
l2cr_config = l2cr;
}
/*
* Configure L2 cache if not enabled.
*/
if ((l2cr & L2CR_L2E) == 0 && l2cr_config != 0) {
cpu_enable_l2cr(l2cr_config);
l2cr = mfspr(SPR_L2CR);
}
if ((l2cr & L2CR_L2E) == 0) {
aprint_normal(" L2 cache present but not enabled ");
return;
}
aprint_normal(",");
switch (vers) {
case IBM750FX:
case IBM750GX:
cpu_fmttab_print(cpu_ibm750_l2cr_formats, l2cr);
break;
case MPC750:
if ((pvr & 0xffffff00) == 0x00082200 /* IBM750CX */ ||
(pvr & 0xffffef00) == 0x00082300 /* IBM750CXe */) {
cpu_fmttab_print(cpu_ibm750_l2cr_formats, l2cr);
} else if ((pvr & 0xfffff0e0) == 0x00087000 /* IBM750CL */) {
cpu_fmttab_print(cpu_ibm750cl_l2cr_formats, l2cr);
} else {
cpu_fmttab_print(cpu_l2cr_formats, l2cr);
}
break;
case MPC7447A:
case MPC7457:
cpu_fmttab_print(cpu_7457_l2cr_formats, l2cr);
return;
case MPC7448:
cpu_fmttab_print(cpu_7448_l2cr_formats, l2cr);
return;
case MPC7450:
case MPC7455:
cpu_fmttab_print(cpu_7450_l2cr_formats, l2cr);
break;
default:
cpu_fmttab_print(cpu_l2cr_formats, l2cr);
break;
}
}
/*
* For MP systems, the firmware may only configure the L2 cache
* on the first CPU. In this case, assume that the other CPUs
* should use the same value for L2CR.
*/
if ((l2cr & L2CR_L2E) != 0 && l2cr_config == 0) {
l2cr_config = l2cr;
}
/*
* Configure L2 cache if not enabled.
*/
if ((l2cr & L2CR_L2E) == 0 && l2cr_config != 0) {
cpu_enable_l2cr(l2cr_config);
l2cr = mfspr(SPR_L2CR);
}
aprint_normal(",");
switch (vers) {
case MPC7447A:
case MPC7457:
cpu_fmttab_print(cpu_7457_l2cr_formats, l2cr);
return;
case MPC7448:
cpu_fmttab_print(cpu_7448_l2cr_formats, l2cr);
return;
default:
cpu_fmttab_print(cpu_7450_l2cr_formats, l2cr);
break;
}
l3cr = mfspr(SPR_L3CR);
/*
* For MP systems, the firmware may only configure the L3 cache
* on the first CPU. In this case, assume that the other CPUs
* should use the same value for L3CR.
*/
if ((l3cr & L3CR_L3E) != 0 && l3cr_config == 0) {
l3cr_config = l3cr;
}
/*
* Configure L3 cache if not enabled.
*/
if ((l3cr & L3CR_L3E) == 0 && l3cr_config != 0) {
cpu_enable_l3cr(l3cr_config);
l3cr = mfspr(SPR_L3CR);
}
if (l3cr & L3CR_L3E) {
aprint_normal(",");
cpu_fmttab_print(cpu_7450_l3cr_formats, l3cr);
}
}
if ((error = sysmon_envsys_register(sme)) != 0) {
aprint_error_dev(ci->ci_dev,
" unable to register with sysmon (%d)\n", error);
sysmon_envsys_destroy(sme);
}
}
/* Find the temperature of the CPU. */
void
cpu_tau_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
{
int i, threshold, count;
threshold = 64; /* Half of the 7-bit sensor range */
/* Successive-approximation code adapted from Motorola
* application note AN1800/D, "Programming the Thermal Assist
* Unit in the MPC750 Microprocessor".
*/
for (i = 5; i >= 0 ; i--) {
mtspr(SPR_THRM1,
SPR_THRM_THRESHOLD(threshold) | SPR_THRM_VALID);
count = 0;
while ((count < 100000) &&
((mfspr(SPR_THRM1) & SPR_THRM_TIV) == 0)) {
count++;
delay(1);
}
if (mfspr(SPR_THRM1) & SPR_THRM_TIN) {
/* The interrupt bit was set, meaning the
* temperature was above the threshold
*/
threshold += 1 << i;
} else {
/* Temperature was below the threshold */
threshold -= 1 << i;
}
}
threshold += 2;
/* Convert the temperature in degrees C to microkelvin */
edata->value_cur = (threshold * 1000000) + 273150000;
edata->state = ENVSYS_SVALID;
}
#endif /* NSYSMON_ENVSYS > 0 */
/*
* Set PIR (Processor Identification Register). i.e. whoami
* Note that PIR is read-only on some CPU versions, so we write to it
* only if it has a different value than we need.
*/
msr = mfspr(SPR_PIR);
if (msr != h->hatch_pir)
mtspr(SPR_PIR, h->hatch_pir);