/*-
* Copyright (c) 2000, 2002 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Steve C. Woodford.
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
/*
* Support for the MEMECC and MEMC40 memory controllers on MVME68K
* and MVME88K boards.
*/
/*
* The following stuff is used to decode the ECC syndrome code so
* that we can figure out exactly which address/bit needed to be
* corrected.
*/
#define MEMECC_SYN_BIT_MASK 0x0fu
#define MEMECC_SYN_BANK_A (0x00u << 4)
#define MEMECC_SYN_BANK_B (0x01u << 4)
#define MEMECC_SYN_BANK_C (0x02u << 4)
#define MEMECC_SYN_BANK_D (0x03u << 4)
#define MEMECC_SYN_BANK_SHIFT 4
#define MEMECC_SYN_BANK_MASK 0x03u
#define MEMECC_SYN_CHECKBIT_ERR 0x80u
#define MEMECC_SYN_INVALID 0xffu
/*
* Set up the scrubber to run roughly once every 24 hours
* with minimal impact on the local bus. With these on/off
* time settings, a scrub of a 32MB DRAM board will take
* roughly half a minute.
*/
memc_reg_write(sc, MEMECC_REG_SCRUB_PERIOD_HI,
MEMECC_SCRUB_PERIOD_HI(MEMECC_SCRUBBER_PERIOD));
memc_reg_write(sc, MEMECC_REG_SCRUB_PERIOD_LO,
MEMECC_SCRUB_PERIOD_LO(MEMECC_SCRUBBER_PERIOD));
memc_reg_write(sc, MEMECC_REG_SCRUB_TIME_ONOFF,
MEMECC_SCRUB_TIME_ON_1 | MEMECC_SCRUB_TIME_OFF_16);
/*
* Start the scrubber, and enable interrupts on Correctable errors
*/
memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL,
memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL) |
MEMECC_SCRUB_CONTROL_SCRBEN | MEMECC_SCRUB_CONTROL_SBEIEN);
printf("%s: Logging ECC errors at ipl %d\n", device_xname(sc->sc_dev),
MEMC_IRQ_LEVEL);
}
static void
memc_hook_error_intr(struct memc_softc *sc, int (*func)(void *))
{
/*
* On boards without a VMEChip2, the interrupt is routed
* via the MCChip (mvme162/mvme172).
*/
if (vmetwo_not_present)
pcctwointr_establish(MCCHIPV_PARITY_ERR, func, MEMC_IRQ_LEVEL,
sc, &sc->sc_evcnt);
else
vmetwo_local_intr_establish(MEMC_IRQ_LEVEL,
VME2_VEC_PARITY_ERROR, func, sc, &sc->sc_evcnt);
}
/* ARGSUSED */
static int
memecc_err_intr(void *arg)
{
struct memc_softc *sc;
u_int8_t rv;
int i, j, cnt = 0;
/*
* For each memory controller we found ...
*/
for (i = 0; i < memc_softc_count; i++) {
sc = memc_softcs[i];
/*
* There are two error loggers per controller, the registers of
* the 2nd are offset from the 1st by 2 bytes.
*/
for (j = 0; j <= 2; j += 2) {
rv = memc_reg_read(sc, MEMECC_REG_ERROR_LOGGER + j);
if ((rv & MEMECC_ERROR_LOGGER_MASK) != 0) {
memecc_log_error(sc, rv, j, 1);
memc_reg_write(sc, MEMECC_REG_ERROR_LOGGER + j,
MEMECC_ERROR_LOGGER_ERRLOG);
cnt++;
}
}
}
return (cnt);
}
/*
* Log an ECC error to the console.
* Note: Since this usually runs at an elevated ipl (above clock), we
* should probably schedule a soft interrupt to log the error details.
* (But only for errors where we would not normally panic.)
*/
static void
memecc_log_error(struct memc_softc *sc, u_int8_t errlog, int off, int mbepanic)
{
u_int32_t addr;
u_int8_t rv, syndrome;
const char *bm = "CPU";
const char *rdwr;
const char *etype;
char syntext[32];
/*
* If an uncorrectable error was detected by an alternate
* bus master or the scrubber, panic immediately.
* We can't rely on the contents of memory at this point.
*
* Uncorrectable errors detected when the CPU was accessing
* DRAM will cause the CPU to take a bus error trap. Depending
* on whether the error was in kernel or user mode, the system
* with either panic or kill the affected process. Basically,
* we don't have to deal with it here.
*
* XXX: I'm not sure whether it's our responsibility to
* perform some dummy writes to the offending address in this
* case to re-generate a good ECC. Note that we'd have to write
* an entire block of 4 words since we can only narrow down the
* faulty address for correctable errors...
*/
if (mbepanic && (errlog & MEMECC_ERROR_LOGGER_MBE) &&
(errlog & (MEMECC_ERROR_LOGGER_ESCRB|MEMECC_ERROR_LOGGER_EALT))) {
/*
* Ensure we don't get a Bus Error while panicking...
*/
rv = memc_reg_read(sc, MEMECC_REG_DRAM_CONTROL + off);
rv &= ~(MEMECC_DRAM_CONTROL_NCEBEN |
MEMECC_DRAM_CONTROL_NCEIEN);
memc_reg_write(sc, MEMECC_REG_DRAM_CONTROL + off, rv);
rv = memc_reg_read(sc, MEMECC_REG_SCRUB_CONTROL + off);
rv &= ~(MEMECC_SCRUB_CONTROL_SBEIEN |
MEMECC_SCRUB_CONTROL_SCRBEN);
memc_reg_write(sc, MEMECC_REG_SCRUB_CONTROL + off, rv);
panic("%s: Halting system to preserve data integrity.",
device_xname(sc->sc_dev));
}
}