mc-opl.c revision 0b240fcdeb4772e65fed050aee3e3dc63308ae72
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* All Rights Reserved, Copyright (c) FUJITSU LIMITED 2008
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
#include <sys/machcpuvar.h>
#include <sys/machsystm.h>
#include <sys/opl_dimm.h>
#include <sys/cpu_module.h>
#include <vm/seg_kmem.h>
#include <vm/hat_sfmmu.h>
/*
* Function prototypes
*/
static int mc_poll_init(void);
static void mc_poll_fini(void);
int opl_mc_suspend(void);
int opl_mc_resume(void);
int mc_get_mem_unum(int, uint64_t, char *, int, int *);
static void mc_free_dimm_list(mc_dimm_info_t *d);
static void mc_get_mlist(mc_opl_t *);
static void mc_polling(void);
static int mc_opl_get_physical_board(int);
#ifdef DEBUG
#endif
#pragma weak opl_get_physical_board
extern int opl_get_physical_board(int);
extern int plat_max_boards(void);
/*
* Configuration data structures
*/
mc_open, /* open */
mc_close, /* close */
nulldev, /* strategy */
nulldev, /* print */
nodev, /* dump */
nulldev, /* read */
nulldev, /* write */
mc_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
CB_REV, /* rev */
nodev, /* cb_aread */
nodev /* cb_awrite */
};
DEVO_REV, /* rev */
0, /* refcnt */
ddi_getinfo_1to1, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
mc_attach, /* attach */
mc_detach, /* detach */
nulldev, /* reset */
&mc_cb_ops, /* cb_ops */
(struct bus_ops *)0, /* bus_ops */
nulldev /* power */
};
/*
* Driver globals
*/
static enum {
static struct plat_model_names {
const char *unit_name;
const char *mem_name;
} model_names[] = {
{ "MBU_A", "MEMB" },
{ "MBU_B", "MEMB" },
{ "CMU", "" },
{ "MBU_A", "" }
};
/*
* The DIMM Names for DC platform.
* The index into this table is made up of (bank, dslot),
* Where dslot occupies bits 0-1 and bank occupies 2-4.
*/
static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = {
/* --------CMUnn----------- */
/* --CS0-----|--CS1------ */
/* -H-|--L-- | -H- | -L-- */
"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
"13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */
"23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */
"33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */
"01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */
"11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */
"21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */
"31A", "30A", "31B", "30B" /* Bank 7 (MAC 3 bank 1) */
};
/*
* The index into this table is made up of (board, bank, dslot),
* Where dslot occupies bits 0-1, bank occupies 2-4 and
* board occupies the bit 5.
*/
/* --------CMU0---------- */
/* --CS0-----|--CS1------ */
/* -H-|--L-- | -H- | -L-- */
"03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */
"01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */
"13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */
"11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */
"23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */
"21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */
"33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */
"31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */
/* --------CMU1---------- */
/* --CS0-----|--CS1------ */
/* -H-|--L-- | -H- | -L-- */
"43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */
"41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */
"53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */
"51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */
"63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */
"61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */
"73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */
"71A", "70A", "71B", "70B" /* Bank 7 (MAC 3 bank 1) */
};
#define INDEX_TO_SLOT(i) ((i) & 0x03)
/* Isolation unit size is 64 MB */
#define MC_MAX_SPEEDS 7
typedef struct {
#define MC_CNTL_SPEED_SHIFT 26
/*
* In mirror mode, we normalized the bank idx to "even" since
* the HW treats them as one unit w.r.t programming.
* This bank index will be the "effective" bank index.
* All mirrored bank state info on mc_period, mc_speedup_period
* will be stored in the even bank structure to avoid code duplication.
*/
{0x6 << MC_CNTL_SPEED_SHIFT, 0},
};
int mc_max_scf_retry = 16;
int mc_max_scf_logs = 64;
int mc_max_rewrite_loop = 100;
int mc_rewrite_delay = 10;
/*
* it takes SCF about 300 m.s. to process a requst. We can bail out
* if it is busy. It does not pay to wait for it too long.
*/
int mc_max_scf_loop = 2;
int mc_scf_delay = 100;
int mc_pce_dropped = 0;
int mc_poll_priority = MINCLSYSPRI;
/*
* Mutex hierarchy in mc-opl
* If both mcmutex and mc_lock must be held,
* mcmutex must be acquired first, and then mc_lock.
*/
static kmutex_t mc_polling_lock;
static kcondvar_t mc_polling_cv;
static kcondvar_t mc_poll_exit_cv;
static int mc_poll_cmd = 0;
static int mc_pollthr_running = 0;
int mc_timeout_period = 0; /* this is in m.s. */
void *mc_statep;
#ifdef DEBUG
int oplmc_debug = 0;
#endif
static int mc_debug_show_all = 0;
extern struct mod_ops mod_driverops;
&mod_driverops, /* module type, this one is a driver */
"OPL Memory-controller %I%", /* module name */
&mc_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1, /* rev */
(void *)&modldrv,
};
#pragma weak opl_get_mem_unum
#pragma weak opl_get_mem_sid
#pragma weak opl_get_mem_offset
#pragma weak opl_get_mem_addr
extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *);
/*
* pseudo-mc node portid format
*
* [10] = 0
* [9] = 1
* [8] = LSB_ID[4] = 0
* [7:4] = LSB_ID[3:0]
* [3:0] = 0
*
*/
/*
* These are the module initialization routines.
*/
int
_init(void)
{
int error;
int plen;
char model[20];
sizeof (mc_opl_t), 1)) != 0)
return (error);
if ((error = mc_poll_init()) != 0) {
return (error);
}
if (&opl_get_mem_unum)
if (&opl_get_mem_sid)
if (&opl_get_mem_offset)
if (&opl_get_mem_addr)
node = prom_rootnode();
}
if (error != 0) {
if (&opl_get_mem_unum)
if (&opl_get_mem_sid)
if (&opl_get_mem_offset)
if (&opl_get_mem_addr)
mc_poll_fini();
}
return (error);
}
int
_fini(void)
{
int error;
return (error);
if (&opl_get_mem_unum)
if (&opl_get_mem_sid)
if (&opl_get_mem_offset)
if (&opl_get_mem_addr)
mc_poll_fini();
return (0);
}
int
{
}
static void
{
mc_pollthr_running = 1;
while (!(mc_poll_cmd & MC_POLL_EXIT)) {
mc_polling();
}
mc_pollthr_running = 0;
/*
* signal if any one is waiting for this thread to exit.
*/
thread_exit();
/* NOTREACHED */
}
static int
{
return (0);
}
static void
{
if (mc_pollthr_running) {
while (mc_pollthr_running) {
}
}
}
static int
{
int instance;
int rv;
/* get the instance of this devi */
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (rv);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
"ddi_mem_ctrl", 0) != DDI_SUCCESS) {
MC_LOG("mc_attach: create_minor_node failed\n");
return (DDI_FAILURE);
}
goto bad;
}
if (mc_timeout_period == 0) {
DDI_PROP_DONTPASS, "mc-timeout-interval-sec",
}
/* set informations in mc state */
if (mc_board_add(mcp))
goto bad;
/*
* Start the polling thread if it is not running already.
*/
if (!mc_pollthr_running) {
}
return (DDI_SUCCESS);
bad:
return (DDI_FAILURE);
}
/* ARGSUSED */
static int
{
int rv;
int instance;
/* get the instance of this devi */
return (DDI_FAILURE);
}
switch (cmd) {
case DDI_SUSPEND:
return (rv);
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
/* free up the soft state */
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static int
int *rvalp)
{
if (cmd == MCIOC_FAULT_PAGE) {
return (EINVAL);
sizeof (mc_flt_page_t), 0) < 0)
return (EFAULT);
return (mc_scf_log_event(&flt_page));
}
#ifdef DEBUG
#else
return (ENOTTY);
#endif
}
/*
* PA validity check:
* This function return 1 if the PA is a valid PA
* in the running Solaris instance i.e. in physinstall
* Otherwise, return 0.
*/
/* ARGSUSED */
static int
{
return (1);
}
return (0);
}
/*
* mac-pa translation routines.
*
* Input: mc driver state, (LSB#, Bank#, DIMM address)
* Output: physical address
*
* Valid - return value: 0
* Invalid - return value: -1
*/
static int
{
int i;
/* loc validity check */
/* Do translation */
for (i = 0; i < PA_BITS_FOR_MAC; i++) {
int pa_bit = 0;
if (mc_bit < MC_ADDRESS_BITS) {
pa_bit = 0;
}
}
return (-1);
}
/*
* In mirror mode, PA is always translated to the even bank.
*/
} else {
}
/*
* there is no need to check ma_bd because it is generated from
* mcp. They are the same.
*/
maddr1.ma_dimm_addr)) {
return (0);
} else {
MC_LOG("Translation error source /LSB%d/B%d/%x, "
return (-1);
}
}
/*
* PA to CS (used by pa_to_maddr).
*/
static int
{
int i;
int cs = 1;
for (i = 0; i < PA_BITS_FOR_MAC; i++) {
/* MAC address bit<29> is arranged on the same PA bit */
/* on both table. So we may use any table. */
break;
}
}
return (cs);
}
/*
* PA to DIMM (used by pa_to_maddr).
*/
/* ARGSUSED */
static uint32_t
{
int i;
for (i = 0; i < PA_BITS_FOR_MAC; i++) {
if (mc_bit < MC_ADDRESS_BITS) {
}
}
return (dimm_addr);
}
/*
* PA to Bank (used by pa_to_maddr).
*/
static int
{
int i;
for (i = 0; i < PA_BITS_FOR_MAC; i++) {
switch (mc_bit) {
case MP_BANK_0:
bankno |= pa_bit_value;
break;
case MP_BANK_1:
break;
case MP_BANK_2:
break;
}
}
return (bankno);
}
/*
* PA to MAC address translation
*
* Input: MAC driver state, physicall adress
* Output: LSB#, Bank id, mac address
*
* Valid - return value: 0
* Invalid - return value: -1
*/
int
{
return (-1);
/* Do translation */
return (0);
}
/*
* nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3.
* x = MAC 0..3
* y = 0..3 (slot info).
* Z = 'A' or 'B'
*
* x = 0..3 (MEMB number)
* y = 0..3 (slot info).
* Z = 'A' or 'B'
*
* x = 0..7 (MEMB number)
* y = 0..3 (slot info).
* Z = 'A' or 'B'
*
* y = 0..3 (slot info).
* Z = 'A' or 'B'
*
*/
int
{
char *dimmnm;
char memb_num;
int cs;
int i;
int j;
switch (plat_model) {
case MODEL_DC:
if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
mf_type == FLT_TYPE_PERMANENT_CE) {
dimmnm = mc_dc_dimm_unum_table[i];
} else {
i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
j = (cs == 0) ? i : i + 2;
mc_dc_dimm_unum_table[j + 1]);
}
break;
case MODEL_FF1:
case MODEL_FF2:
if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
mf_type == FLT_TYPE_PERMANENT_CE) {
dimmnm = mc_ff_dimm_unum_table[i];
} else {
j = (cs == 0) ? i : i + 2;
memb_num = mc_ff_dimm_unum_table[i][0],
&mc_ff_dimm_unum_table[j][1],
}
break;
case MODEL_IKKAKU:
if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
mf_type == FLT_TYPE_PERMANENT_CE) {
dimmnm = mc_ff_dimm_unum_table[i];
} else {
j = (cs == 0) ? i : i + 2;
memb_num = mc_ff_dimm_unum_table[i][0],
&mc_ff_dimm_unum_table[j][1],
}
break;
default:
return (-1);
}
return (0);
}
static void
{
char buf[FM_MAX_CLASS];
char device_path[MAXPATHLEN];
char sid[MAXPATHLEN];
int nflts;
int i, n;
int blen = MAXPATHLEN;
char *p, *s = NULL;
int ret = -1;
if (panicstr) {
return;
} else {
}
/*
* Create the scheme "dev" FMRI.
*/
device_path, NULL);
/*
* Encode all the common data into the ereport.
*/
/*
* Set payload.
*/
ECC_STICKY, NULL);
}
for (i = 0; i < nflts; i++)
for (i = 0; i < nflts; i++)
for (i = 0; i < nflts; i++)
/* offset is set only for PCE and ICE */
}
for (i = 0; i < nflts; i++)
for (i = 0; i < nflts; i++) {
if (flt_stat->mf_errlog_valid) {
} else {
synd[i] = 0;
dslot[i] = 0;
values[i] = 0;
}
}
device_path[0] = 0;
p = &device_path[0];
sid[0] = 0;
s = &sid[0];
ret = 0;
for (i = 0; i < nflts; i++) {
int bank;
if (ret != 0) {
"mc_ereport_post: Failed to determine the unum "
"for board=%d bank=%d type=0x%x slot=0x%x",
continue;
}
n = strlen(device_path);
blen = MAXPATHLEN - n;
p = &device_path[n];
if (i < (nflts - 1)) {
blen--;
p++;
}
if (ret == 0) {
}
}
(uint64_t)-1);
NULL);
if (panicstr) {
} else {
}
}
static void
{
int rv;
int i;
/*
* we come here only when we have:
* In mirror mode: MUE, SUE
* In normal mode: UE, Permanent CE, Intermittent CE
*/
for (i = 0; i < mc_aflt->mflt_nflts; i++) {
/* Ensure the pa is valid (not in isolated memory block) */
else
}
case 0:
case EAGAIN:
MC_LOG("Page retired or pending\n");
return;
case EIO:
/*
* Do page retirement except for the PCE and ICE cases.
* This is taken care by the OPL DE
*/
}
break;
case EINVAL:
default:
/*
* Some memory do not have page structure so
* we keep going in case of EINVAL.
*/
break;
}
for (i = 0; i < mc_aflt->mflt_nflts; i++) {
}
}
}
/*
* The restart address is actually defined in unit of PA[37:6]
* the mac patrol will convert that to dimm offset. If the
* address is not in the bank, it will continue to search for
* the next PA that is within the bank.
*
* Also the mac patrol scans the dimms based on PA, not
* dimm offset.
*/
static int
{
int rv;
return (0);
}
return (0);
}
if (rv != 0) {
MC_LOG("cannot convert mcaddr to pa. use auto restart\n");
return (0);
}
/* pa is not on this board, just retry */
return (0);
}
if (!rsaddr_info->mi_injectrestart) {
/*
* For non-error injection restart we need to
* a "good" page. A "good" page is a page that
* has not been page retired. If the current
* page that contains the pa is "good", we will
* do a HW auto restart and let HW patrol continue
* where it last stopped. Most desired scenario.
*
* If the current page is not "good", we will advance
* to the next page to find the next "good" page and
* restart the patrol from there.
*/
int wrapcount = 0;
while (wrapcount < 2) {
/*
* Not in physinstall - advance to the
* next memory isolation blocksize
*/
MC_LOG("Invalid PA\n");
} else {
int rv;
/*
* The page is "good" (not retired),
* we will use automatic HW restart
* algorithm if this is the original
* current starting page.
*/
MC_LOG("Page has no error. "
"Auto restart\n");
return (0);
} else {
/*
* found a subsequent good page
*/
break;
}
}
/*
* Skip to the next page
*/
}
/* Check to see if we hit the end of the memory range */
MC_LOG("Wrap around\n");
wrapcount++;
}
}
if (wrapcount > 1) {
MC_LOG("Failed to find a good page. Just restart\n");
return (0);
}
}
/*
* We reached here either:
* 1. We are doing an error injection restart that specify
* 2. We found a subsequent good page different from the
* Restart MAC patrol: PA[37:6]
*/
return (0);
}
static void
{
p->ri_next = *q;
*q = p;
}
static mc_retry_info_t *
{
mc_retry_info_t *p;
if ((p = *q) != NULL) {
*q = p->ri_next;
return (p);
} else {
return (NULL);
}
}
/*
* Rewriting is used for two purposes.
* - to correct the error in memory.
* - to determine whether the error is permanent or intermittent.
* It's done by writing the address in MAC_BANKm_REWRITE_ADD
* and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that,
* rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM.
*
* Note that rewrite operation doesn't change RAW_UE to Marked UE.
* Therefore, we use it only CE case.
*/
static uint32_t
{
int count = 0;
int max_count;
int retry_state;
if (retrying)
max_count = 1;
else
goto timeout;
}
/* first wait to make sure PTRL_STATUS is 0 */
if (!(cntl & MAC_CNTL_PTRL_STATUS)) {
count = 0;
break;
}
}
goto timeout;
count = 0;
do {
goto timeout;
} else {
}
/*
* If there are other MEMORY or PCI activities, this
* will be BUSY, else it should be set immediately
*/
} while (!(cntl & MAC_CNTL_REW_END));
return (cntl);
return (0);
}
void
{
bankp->mcb_rewrite_count = 0;
break;
}
/* we break out if no more pending rewrite or we got timeout again */
} else {
} else {
goto again;
}
}
}
}
void
{
/*
* previous rewrite request has not completed yet.
* So we discard this rewrite request.
*/
" for 0x%lx (/LSB%d/B%d/%x).\n",
} else {
" for /LSB%d/B%d/%x.\n",
}
return;
}
if ((state > RETRY_STATE_PENDING)) {
} else {
}
}
}
void
{
int count;
int n = 0;
scf_log_t *p;
int bank;
(n < mc_max_errlog_processed)) {
count = 0;
& MAC_STATIC_ERR_VLD)) {
if (count++ >= (mc_max_scf_loop)) {
break;
}
}
if (count < mc_max_scf_loop) {
p->sl_err_log);
} else {
/*
* if we try too many times, just drop the req
*/
return;
} else {
if ((++mc_pce_dropped & 0xff) == 0) {
"report CE to SCF\n");
}
}
}
n++;
}
}
}
void
{
scf_log_t *p;
if ((++mc_pce_dropped & 0xff) == 0) {
}
return;
}
p->sl_next = 0;
/*
* we rely on mc_scf_log to detect NULL queue.
* mc_scf_log_tail is irrelevant is such case.
*/
} else {
}
}
/*
* This routine determines what kind of CE happens, intermittent
* or permanent as follows. (See 4.7.3 in Columbus2 PRM.)
* - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register.
* - If CE is still detected on the same address even after doing
* rewrite operation twice, it is determined as permanent error.
* - If error is not detected anymore, it is determined as intermittent
* error.
* - If UE is detected due to rewrite operation, it should be treated
* as UE.
*/
/* ARGSUSED */
static void
{
int i;
/*
* rewrite request 1st time reads and correct error data
* and write to DIMM. 2nd rewrite request must be issued
* if REW_CE = 1, then it is permanent CE.
*/
for (i = 0; i < 2; i++) {
if (cntl == 0) {
/* timeout case */
return;
}
/*
* If the error becomes UE or CMPE
* we return to the caller immediately.
*/
if (cntl & MAC_CNTL_REW_UE) {
if (ptrl_error)
else
return;
}
if (cntl & MAC_CNTL_REW_CMPE) {
if (ptrl_error)
else
return;
}
}
if (!(cntl & MAC_CNTL_REW_CE)) {
}
/* report PERMANENT_CE to SP via SCF */
}
}
}
static int
{
if (ptrl_error) {
} else {
}
}
void
{
value |= mc_max_speed;
else
}
static void
{
}
static void
{
old_status = 0;
/* we keep reading until the status is stable */
while (old_status != status) {
old_status = status;
if (status == old_status) {
break;
}
}
}
/*
* Error philosophy for mirror mode:
*
* PTRL (The error address for both banks are same, since ptrl stops if it
* detects error.)
* - Compare error log CMPE.
*
* - UE-UE Report MUE. No rewrite.
*
*
* If CE is permanent, inform SCF. Once for each
* Dimm. If CE becomes UE or CMPE, go back to above.
*
*
* MI (The error addresses for each bank are the same or different.)
* - Compare error If addresses are the same. Just CMPE, so log CMPE.
* If addresses are different (this could happen
* as a result of scrubbing. Report each separately.
* Only report error info on each side.
*
* - UE-UE Addresses are the same. Report MUE.
* Addresses are different. Report SUE on each bank.
* Rewrite to clear UE.
*
* Rewrite to clear UE. Report SUE for the bank.
*
* If CE becomes UE or CMPE, go back to above.
*
*/
static int
{
int i;
int rv = 0;
int bank;
int rewrite_timeout = 0;
MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n",
if (ptrl_error) {
MAC_CNTL_PTRL_ERRS) == 0)
return (0);
} else {
MAC_CNTL_MI_ERRS) == 0)
return (0);
}
/*
* First we take care of the case of CE
* because they can become UE or CMPE
*/
for (i = 0; i < 2; i++) {
rewrite_timeout = 1;
}
rv = 1;
}
}
if (rewrite_timeout)
return (0);
/* The above scrubbing can turn CE into UE or CMPE */
/*
* Now we distinguish two cases: same address or not
* the same address. It might seem more intuitive to
* distinguish PTRL v.s. MI error but it is more
* complicated that way.
*/
/*
* Compare error is result of MAC internal error, so
* simply log it instead of publishing an ereport. SCF
* diagnoses all the MAC internal and its i/f error.
*/
MC_LOG("cmpe error detected\n");
return (1);
}
/* Both side are UE's */
MAC_SET_ERRLOG_INFO(&flt_stat[0]);
MC_LOG("MUE detected\n");
return (1);
}
for (i = 0; i < 2; i++) {
/* rewrite can clear the one side UE error */
(void) do_rewrite(mcp,
}
MAC_SET_ERRLOG_INFO(&flt_stat[i]);
return (1);
}
}
} else {
/*
* addresses are different. That means errors
* on the 2 banks are not related at all.
*/
for (i = 0; i < 2; i++) {
/*
* Compare error is result of MAC internal
* error, so simply log it instead of
* publishing an ereport. SCF diagnoses all
* the MAC internal and its interface error.
*/
MC_LOG("cmpe error detected\n");
/* no more report on this bank */
rv = 1;
}
}
/* rewrite can clear the one side UE error */
for (i = 0; i < 2; i++) {
(void) do_rewrite(mcp,
0);
MAC_SET_ERRLOG_INFO(&flt_stat[i]);
rv = 1;
}
}
}
return (rv);
}
static void
{
int i;
int mi_valid;
/* Now read all the registers into flt_stat */
for (i = 0; i < 2; i++) {
/* patrol registers */
/*
* In mirror mode, it is possible that only one bank
* may report the error. We need to check for it to
* ensure we pick the right addr value for patrol restart.
* Note that if both banks reported errors, we pick the
* 2nd one. Both banks should reported the same error address.
*/
MC_LOG("ptrl registers cntl %x add %x log %x\n",
flt_stat[i].mf_err_log);
/* MI registers */
MC_LOG("MI registers cntl %x add %x log %x\n",
mi_flt_stat[i].mf_err_log);
}
/* clear errors once we read all the registers */
/* Process MI errors first */
/* if not error mode, cntl1 is 0 */
mi_flt_stat[0].mf_cntl = 0;
mc_aflt.mflt_is_ptrl = 0;
MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
(flt_stat[0].mf_err_add ==
MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
#ifdef DEBUG
MC_LOG("discarding PTRL error because "
"it is the same as MI\n");
#endif
return;
}
/* if not error mode, cntl1 is 0 */
}
static int
{
int rv = 0;
MC_LOG("UE detected\n");
rv = 1;
MC_LOG("CE detected\n");
/* Error type can change after scrubbing */
return (0);
}
}
rv = 1;
}
if (mc_aflt->mflt_erpt_class) {
}
return (rv);
}
static void
{
int mi_valid;
/* patrol registers */
/* MI registers */
/* clear errors once we read all the registers */
mc_aflt.mflt_is_ptrl = 0;
}
MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) &&
(flt_stat.mf_err_add ==
#ifdef DEBUG
MC_LOG("discarding PTRL error because "
"it is the same as MI\n");
#endif
return;
}
&flt_stat);
}
}
/*
* memory patrol error handling algorithm:
* timeout() is used to do periodic polling
* This is the flow chart.
* timeout ->
* mc_check_errors()
* if memory bank is installed, read the status register
* if any error bit is set,
* -> mc_error_handler()
* -> read all error registers
* -> mc_process_error()
* determine error type
* rewrite to clear error or scrub to determine CE type
* inform SCF on permanent CE
* -> mc_err_drain
* page offline processing
* -> mc_ereport_post()
*/
static void
{
return;
if (cntl & MAC_CNTL_PTRL_STATUS)
return;
}
if (cntl & MAC_CNTL_REW_END) {
} else {
/*
* If the rewrite does not complete in
* 1 hour, we have to consider this a HW
* failure. However, there is no recovery
* mechanism. The only thing we can do
* to to print a warning message to the
* console. We continue to increment the
* counter but we only print the message
* once. It will take the counter a long
* time to wrap around and the user might
* see a second message. In practice,
* we have never hit this condition but
* we have to keep the code here just in case.
*/
== mc_max_rewrite_retry) {
" partly suspended on /LSB%d/B%d"
" due to heavy memory load,"
" and it will restart"
bank);
}
}
}
static void
{
int i, error_count = 0;
int running;
int wrapped;
int ebk;
/*
* scan errors.
*/
return;
for (i = 0; i < BANKNUM_PER_SB; i++) {
if (MC_REWRITE_ACTIVE(mcp, i)) {
mc_process_rewrite(mcp, i);
}
/* Compute the effective bank idx */
if (mc_debug_show_all || stat) {
MC_LOG("/LSB%d/B%d stat %x cntl %x\n",
}
/*
* Update stats and reset flag if the HW patrol
* wrapped around in its scan.
*/
if (wrapped) {
MAC_CLEAR_MAX(mcp, i);
MC_LOG("mirror mc period %ld on "
mcp->mc_board_num, i);
} else {
MC_LOG("mc period %ld on "
mcp->mc_board_num, i);
}
}
if (running) {
/*
* Mac patrol HW is still running.
* Normally when an error is detected,
* the HW patrol will stop so that we
* can collect error data for reporting.
* Certain errors (MI errors) detected may not
* cause the HW patrol to stop which is a
* problem since we cannot read error data while
* the HW patrol is running. SW is not allowed
* to stop the HW patrol while it is running
* as it may cause HW inconsistency. This is
* described in a HW errata.
* In situations where we detected errors
* that may not cause the HW patrol to stop.
* We speed up the HW patrol scanning in
* the hope that it will find the 'real' PTRL
* errors associated with the previous errors
* causing the HW to finally stop so that we
* can do the reporting.
*/
/*
* Check to see if we did speed up
* the HW patrol due to previous errors
* detected that did not cause the patrol
* to stop. We only do it if HW patrol scan
* wrapped (counted as completing a 'period').
*/
if (wrapped &&
0)) {
/*
* We did try to speed up.
* The speed up period has
* expired and the HW patrol
* is still running. The
* errors must be intermittent.
* We have no choice but to
* ignore them, reset the scan
* speed to normal and clear
* the MI error bits. For
* mirror mode, we need to
* clear errors on both banks.
*/
MC_LOG("Clearing MI errors\n");
MAC_CLEAR_ERRS(mcp, i,
MC_LOG("Clearing "
"Mirror MI errs\n");
i^1,
}
}
} else if (stat & MAC_STAT_MI_ERRS) {
/*
* MI errors detected but we cannot
* report them since the HW patrol
* is still running.
* We will attempt to speed up the
* scanning and hopefully the HW
* can detect PRTL errors at the same
* location that cause the HW patrol
* to stop.
*/
}
} else if (stat & (MAC_STAT_PTRL_ERRS |
MAC_STAT_MI_ERRS)) {
/*
* HW Patrol has stopped and we found errors.
* Proceed to collect and report error info.
*/
rsaddr_info.mi_valid = 0;
&rsaddr_info);
} else {
}
error_count++;
} else {
/*
* HW patrol scan has apparently stopped
* Restart the HW patrol just to be sure.
* In mirror mode, the odd bank might have
* reported errors that caused the patrol to
* stop. We'll defer the restart to the odd
* bank in this case.
*/
}
}
}
if (error_count > 0)
else
mcp->mc_last_error = 0;
}
/*
* mc_polling -- Check errors for only one instance,
* but process errors for all instances to make sure we drain the errors
* faster than they can be accumulated.
*
* Polling on each board should be done only once per each
* mc_patrol_interval_sec. This is equivalent to setting mc_tick_left
* to OPL_MAX_BOARDS and decrement by 1 on each timeout.
* Once mc_tick_left becomes negative, the board becomes a candidate
* for polling because it has waited for at least
* mc_patrol_interval_sec's long. If mc_timeout_period is calculated
* differently, this has to be updated accordingly.
*/
static void
mc_polling(void)
{
int i, scan_error;
scan_error = 1;
for (i = 0; i < OPL_MAX_BOARDS; i++) {
continue;
}
continue;
}
mc_check_errors_func((void *)mcp);
scan_error = 0;
} else {
mcp->mc_tick_left--;
}
}
}
static void
{
maddr->ma_dimm_addr = 0;
}
typedef struct mc_mem_range {
static int
{
int len;
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
struct mc_addr_spec {
};
static char *mc_tbl_name[] = {
"cs0-mc-pa-trans-table",
"cs1-mc-pa-trans-table"
};
/*
* This routine performs a rangecheck for a given PA
* to see if it belongs to the memory range for this board.
* Return 1 if it is valid (within the range) and 0 otherwise
*/
static int
{
return (0);
else
return (1);
}
static void
{
}
}
static struct memlist *
{
return (NULL);
}
return (hl);
}
static struct memlist *
{
return (NULL);
return (mlist);
break;
else
} else {
/*
* splitting an memlist entry.
*/
KM_SLEEP);
}
}
break;
} else {
}
}
}
}
return (mlist);
}
static void
{
if (mlist) {
}
if (mlist) {
}
}
if (mlist) {
}
}
int
{
struct mc_addr_spec *macaddr;
int nbanks = 0;
int mirror_mode = 0;
int ret;
/*
* Get configurations from "pseudo-mc" node which includes:
* board# : LSB number
* mac-addr : physical base address of MAC registers
* csX-mac-pa-trans-table: translation table from DIMM address
* to physical address or vice versa.
*/
return (DDI_FAILURE);
}
/*
* Get start address in this CAB. It can be gotten from
* "sb-mem-ranges" property.
*/
return (DDI_FAILURE);
}
/* get mac-pa trans tables */
for (i = 0; i < MC_TT_CS; i++) {
len = MC_TT_ENTRIES;
if (cc != DDI_SUCCESS) {
}
}
/* initialize bank informations */
if (cc != DDI_SUCCESS) {
return (DDI_FAILURE);
}
if (cc != DDI_SUCCESS) {
if (len > 0)
return (DDI_FAILURE);
}
/* get the physical board number for a given logical board number */
if (mcp->mc_phys_board_num < 0) {
if (len > 0)
return (DDI_FAILURE);
}
for (i = 0; i < len1 / sizeof (cs_status_t); i++) {
}
if (len1 > 0)
if (nbanks > 0)
else {
/* No need to free macaddr because len must be 0 */
return (DDI_SUCCESS);
}
for (i = 0; i < BANKNUM_PER_SB; i++) {
mcp->mc_scf_retry[i] = 0;
mcp->mc_speedup_period[i] = 0;
}
/*
* Get the memory size here. Let it be B (bytes).
* Let T be the time in u.s. to scan 64 bytes.
* If we want to complete 1 round of scanning in P seconds.
*
* B * T * 10^(-6) = P
* ---------------
* 64
*
* T = P * 64 * 10^6
* -------------
* B
*
* = P * 64 * 10^6
* -------------
* B
*
* The timing bits are set in PTRL_CNTL[28:26] where
*
* 0 - 1 m.s
* 1 - 512 u.s.
* 10 - 256 u.s.
* 11 - 128 u.s.
* 100 - 64 u.s.
* 101 - 32 u.s.
* 110 - 0 u.s.
* 111 - reserved.
*
*
* a[0] = 110, a[1] = 101, ... a[6] = 0
*
* cs-status property is int x 7
* 0 - cs#
* 1 - cs-status
* 2 - cs-avail.hi
* 3 - cs-avail.lo
* 4 - dimm-capa.hi
* 5 - dimm-capa.lo
* 6 - #of dimms
*/
if (nbytes > 0) {
int i;
for (i = 0; i < MC_MAX_SPEEDS - 1; i++) {
break;
}
}
} else
for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) {
int k;
/*
* setup bank
*/
for (k = 0; k < MC_RETRY_COUNT; k++, retry++) {
}
/*
* check if mirror mode
*/
if (mirr & MAC_MIRR_MIRROR_MODE) {
bk);
mirror_mode = 1;
/*
* The following bit is only used for
* error injection. We should clear it
*/
if (mirr & MAC_MIRR_BANK_EXCLUSIVE)
}
/*
* restart if not mirror mode or the other bank
* of the mirror is not running
*/
if (!(mirr & MAC_MIRR_MIRROR_MODE) ||
bk);
rsaddr.mi_injectrestart = 0;
} else {
MC_LOG("Not starting up /LSB%d/B%d\n",
}
}
if (len > 0)
if (ret != DDI_PROP_SUCCESS) {
}
/*
* set interval in HZ.
*/
mcp->mc_last_error = 0;
/* restart memory patrol checking */
return (DDI_SUCCESS);
}
int
{
int i;
scf_log_t *p;
/*
* cleanup mac state
*/
return (DDI_SUCCESS);
}
for (i = 0; i < BANKNUM_PER_SB; i++) {
}
}
/* stop memory patrol checking */
/* just throw away all the scf logs */
for (i = 0; i < BANKNUM_PER_SB; i++) {
mcp->mc_scf_total[i]--;
}
}
if (mcp->mc_dimm_list)
return (DDI_SUCCESS);
}
int
{
/* stop memory patrol checking */
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
}
void
opl_mc_update_mlist(void)
{
int i;
/*
* memory information is not updated until
* This interface is used by dr_mem to inform
* mc-opl to update the mlist.
*/
for (i = 0; i < OPL_MAX_BOARDS; i++) {
continue;
}
}
/* caller must clear the SUSPEND bits or this will do nothing */
int
{
int i;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
}
return (DDI_SUCCESS);
}
/* restart memory patrol checking */
for (i = 0; i < BANKNUM_PER_SB; i++) {
}
}
}
return (DDI_SUCCESS);
}
static mc_opl_t *
{
int i;
for (i = 0; i < OPL_MAX_BOARDS; i++) {
continue;
/* if mac patrol is suspended, we cannot rely on it */
continue;
return (mcp);
}
}
return (NULL);
}
/*
* Get Physical Board number from Logical one.
*/
static int
{
if (&opl_get_physical_board) {
return (opl_get_physical_board(sb));
}
return (-1);
}
/* ARGSUSED */
int
int *lenp)
{
int i;
int j;
int sb;
int bank;
int cs;
int rv = 0;
char memb_num;
return (ENOSPC);
} else {
if (lenp)
}
return (0);
}
if (sb == -1) {
return (ENXIO);
}
switch (plat_model) {
case MODEL_DC:
i = BD_BK_SLOT_TO_INDEX(0, bank, 0);
j = (cs == 0) ? i : i + 2;
mc_dc_dimm_unum_table[j + 1]);
break;
case MODEL_FF2:
case MODEL_FF1:
j = (cs == 0) ? i : i + 2;
memb_num = mc_ff_dimm_unum_table[i][0];
&mc_ff_dimm_unum_table[j][1],
break;
case MODEL_IKKAKU:
j = (cs == 0) ? i : i + 2;
&mc_ff_dimm_unum_table[j][1],
break;
default:
}
if (lenp) {
}
return (rv);
}
int
opl_mc_suspend(void)
{
int i;
for (i = 0; i < OPL_MAX_BOARDS; i++) {
continue;
}
return (0);
}
int
opl_mc_resume(void)
{
int i;
for (i = 0; i < OPL_MAX_BOARDS; i++) {
continue;
}
return (0);
}
static void
{
MC_LOG("mc-opl instance for board# %d already exists\n",
mcp->mc_board_num);
}
}
static void
{
}
/* Error injection interface */
static void
{
}
static void
{
}
/* ARGSUSED */
int
{
int bank;
int both_sides = 0;
int extra_injection_needed = 0;
extern void cpu_flush_ecache(void);
MC_LOG("mc_inject_error: invalid pa\n");
return (ENOTSUP);
}
MC_LOG("mc-opl has been suspended. No error injection.\n");
return (EBUSY);
}
/* convert pa to offset within the board */
return (EINVAL);
}
if (flags & MC_INJECT_FLAG_OTHER)
MC_LOG("Not mirror mode\n");
return (EINVAL);
}
switch (error_type) {
case MC_INJECT_PERMANENT_MCE:
case MC_INJECT_MUE:
both_sides = 1;
}
if (flags & MC_INJECT_FLAG_RESET)
if (both_sides) {
}
switch (error_type) {
case MC_INJECT_SUE:
/*FALLTHROUGH*/
case MC_INJECT_UE:
case MC_INJECT_MUE:
if (flags & MC_INJECT_FLAG_PATH) {
} else {
}
break;
if (flags & MC_INJECT_FLAG_PATH) {
} else {
}
break;
case MC_INJECT_PERMANENT_CE:
case MC_INJECT_PERMANENT_MCE:
if (flags & MC_INJECT_FLAG_PATH) {
} else {
}
break;
case MC_INJECT_CMPE:
data = 0xabcdefab;
membar_sync();
cntl = 0;
break;
case MC_INJECT_NOP:
cntl = 0;
break;
default:
MC_LOG("mc_inject_error: invalid option\n");
cntl = 0;
}
if (cntl) {
if (both_sides) {
}
}
/*
* For all injection cases except compare error, we
* must write to the PA to trigger the error.
*/
if (flags & MC_INJECT_FLAG_ST) {
data = 0xf0e0d0c0;
}
if (flags & MC_INJECT_FLAG_LD) {
if (flags & MC_INJECT_FLAG_PREFETCH) {
/*
* Use strong prefetch operation to
* inject MI errors.
*/
extern void mc_prefetch(caddr_t);
MC_LOG("prefetch\n");
(caddr_t)-1);
/*
* For MI errors, we need one extra
* injection for HW patrol to stop.
*/
} else {
" for PA %lx\n", pa);
}
} else {
}
if (extra_injection_needed) {
/*
* These are the injection cases where the
* requested injected errors will not cause the HW
* patrol to stop. For these cases, we need to inject
* an extra 'real' PTRL error to force the
* HW patrol to stop so that we can report the
* errors injected. Note that we cannot read
* and report error status while the HW patrol
* is running.
*/
if (both_sides) {
}
data = 0xf0e0d0c0;
}
}
if (flags & MC_INJECT_FLAG_RESTART) {
MC_LOG("Restart patrol\n");
}
if (flags & MC_INJECT_FLAG_POLL) {
int running;
MC_LOG("Poll patrol error\n");
if (!running &&
/*
* HW patrol stopped and we have errors to
* report. Do it.
*/
rsaddr.mi_injectrestart = 0;
} else {
}
} else {
/*
* We are expecting to report injected
* errors but the HW patrol is still running.
* Speed up the scanning
*/
}
}
return (0);
}
void
{
/* force the above write to be processed by mac patrol */
}
{
return (rv);
}
/*
* parse_unum_memory -- extract the board number and the DIMM name from
* the unum.
*
* Return 0 for success and non-zero for a failure.
*/
int
{
char *c;
char x, y, z;
/* DC Model */
c += 3;
return (1);
}
c += 3;
if (strlen(c) < 3) {
return (2);
}
((c[2] != 'A') && (c[2] != 'B'))) {
return (3);
}
x = c[0];
y = c[1];
z = c[2];
c += 4;
if ((c[0] != 'A') && (c[0] != 'B')) {
return (4);
}
if (plat_model == MODEL_IKKAKU) {
/* Ikkaku Model */
x = '0';
*board = 0;
} else {
return (5);
}
c += 4;
x = c[0];
}
return (6);
}
c += 3;
if (strlen(c) < 2) {
return (7);
}
return (8);
}
y = c[0];
z = c[1];
} else {
return (9);
}
if (*board < 0) {
return (10);
}
dname[0] = x;
dname[1] = y;
dname[2] = z;
return (0);
}
/*
* mc_get_mem_sid_dimm -- Get the serial-ID for a given board and
* the DIMM name.
*/
int
{
mc_dimm_info_t *d = NULL;
return (ENOTSUP);
break;
}
}
if (d != NULL) {
"buflen is smaller than %d\n", *lenp);
} else {
d->md_serial, d->md_partnum);
ret = 0;
}
}
MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n",
return (ret);
}
int
{
int id;
int ret;
char *dimmnm;
if (mf_type == FLT_TYPE_INTERMITTENT_CE ||
mf_type == FLT_TYPE_PERMANENT_CE) {
if (plat_model == MODEL_DC) {
/*
* All DC models
*/
} else {
/*
* All FF and Ikkaku models
*/
}
&lenp)) != 0) {
return (ret);
}
} else {
return (1);
}
return (0);
}
/*
* mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum.
*/
int
{
int i;
int board;
MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
return (EINVAL);
}
if (board < 0) {
MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n",
return (EINVAL);
}
/*
* return ENOENT if we can not find the matching board.
*/
for (i = 0; i < OPL_MAX_BOARDS; i++) {
continue;
continue;
}
if (ret == 0) {
break;
}
}
return (ret);
}
/*
* mc_get_mem_offset -- get the offset in a DIMM for a given physical address.
*/
int
{
int i;
for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) {
continue;
continue;
}
ret = 0;
}
}
MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n",
return (ret);
}
/*
* dname_to_bankslot - Get the bank and slot number from the DIMM name.
*/
int
{
int i;
int tsz;
char **tbl;
if (plat_model == MODEL_DC) {
/*
* All DC models
*/
tsz = OPL_MAX_DIMMS;
} else {
/*
* All FF and Ikkaku models
*/
}
for (i = 0; i < tsz; i++) {
break;
}
}
if (i == tsz) {
return (1);
}
*bank = INDEX_TO_BANK(i);
*slot = INDEX_TO_SLOT(i);
return (0);
}
/*
* mc_get_mem_addr -- get the physical address of a DIMM corresponding
* to the unum and sid.
*/
int
{
int board;
int bank;
int slot;
int i;
MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n",
MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n",
return (EINVAL);
}
if (board < 0) {
MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n",
return (EINVAL);
}
for (i = 0; i < OPL_MAX_BOARDS; i++) {
continue;
continue;
}
if (ret != 0) {
MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n");
} else {
if (ret != 0) {
MC_LOG("mc_get_mem_addr: "
"mcaddr_to_pa failed\n");
continue;
}
break;
}
}
return (ret);
}
static void
{
while (d != NULL) {
kmem_free(d, sizeof (mc_dimm_info_t));
d = next;
}
}
/*
* mc_get_dimm_list -- get the list of dimms with serial-id info
* from the SP.
*/
{
int ret;
int sexp;
if (ret == 0) {
sexp = sizeof (board_dimm_info_t) +
#ifdef DEBUG
if (oplmc_debug)
#endif
} else {
}
}
return (dimm_list);
}
/*
* mc_prepare_dimmlist - Prepare the dimm list from the information
* received from the SP.
*/
{
char *dimm_name;
char *serial;
char *part;
int dimm;
mc_dimm_info_t *d;
KM_SLEEP);
d->md_dimmname[dnamesz] = 0;
d->md_partnum[partsz] = 0;
dimm_list = d;
}
return (dimm_list);
}
static int
{
return (EINVAL);
return (EFAULT);
}
return (0);
}
static int
{
if ((sid_sz = cpu_get_name_bufsize()) == 0)
return (ENOTSUP);
MC_LOG("mc_scf_log_event: mc_get_mem_fmri failed\n");
return (rv);
}
MC_LOG("mc_scf_log_event: mc_get_mem_sid failed\n");
goto out;
}
&pa)) != 0) {
MC_LOG("mc_scf_log_event: mc_get_mem_addr failed\n");
goto out;
}
MC_LOG("mc_scf_log_event: parse_unum_memory failed\n");
goto out;
}
if (board < 0) {
MC_LOG("mc_scf_log_event: Invalid board=%d dimm=%s\n",
goto out;
}
MC_LOG("mc_scf_log_event: dname_to_bankslot failed\n");
goto out;
}
MC_LOG("mc_scf_log_event: invalid pa\n");
goto out;
}
MC_LOG("mc_scf_log_event: DIMM%s, /LSB%d/B%d/%x, pa %lx elog %x\n",
goto out;
}
rv = 0;
out:
return (rv);
}
#ifdef DEBUG
void
{
char *b;
b = buf;
b += dnamesz;
b += serialsz;
}
void
{
int dimm;
char *buf;
printf("Version=%d Board=%02d DIMMs=%d NameSize=%d "
printf("======================================================\n");
}
printf("======================================================\n");
}
/* ARGSUSED */
static int
int *rvalp)
{
int rv = 0;
int i;
cmd &= 0xf;
sizeof (uint64_t), 0) < 0) {
return (rv);
}
} else {
offset += 64;
offset = 0;
}
switch (cmd) {
case MCI_CE:
break;
case MCI_PERM_CE:
break;
case MCI_UE:
break;
case MCI_M_CE:
break;
case MCI_M_PCE:
break;
case MCI_M_UE:
break;
case MCI_CMP:
break;
case MCI_NOP:
case MCI_SHOW_ALL:
mc_debug_show_all = 1;
break;
case MCI_SHOW_NONE:
mc_debug_show_all = 0;
break;
case MCI_ALLOC:
/*
* just allocate some kernel memory and never free it
* 512 MB seems to be the maximum size supported.
*/
for (i = 0; i < flags; i++) {
}
break;
case MCI_SUSPEND:
(void) opl_mc_suspend();
break;
case MCI_RESUME:
(void) opl_mc_resume();
break;
default:
}
if (buf)
return (rv);
}
#endif /* DEBUG */