125cf1a30Sjl /* 225cf1a30Sjl * CDDL HEADER START 325cf1a30Sjl * 425cf1a30Sjl * The contents of this file are subject to the terms of the 525cf1a30Sjl * Common Development and Distribution License (the "License"). 625cf1a30Sjl * You may not use this file except in compliance with the License. 725cf1a30Sjl * 825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925cf1a30Sjl * or http://www.opensolaris.org/os/licensing. 1025cf1a30Sjl * See the License for the specific language governing permissions 1125cf1a30Sjl * and limitations under the License. 1225cf1a30Sjl * 1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each 1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the 1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying 1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner] 1825cf1a30Sjl * 1925cf1a30Sjl * CDDL HEADER END 2025cf1a30Sjl */ 2125cf1a30Sjl /* 2225cf1a30Sjl * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 2325cf1a30Sjl */ 2425cf1a30Sjl 2525cf1a30Sjl #pragma ident "%Z%%M% %I% %E% SMI" 2625cf1a30Sjl 2725cf1a30Sjl #include <sys/types.h> 2825cf1a30Sjl #include <sys/sysmacros.h> 2925cf1a30Sjl #include <sys/conf.h> 3025cf1a30Sjl #include <sys/modctl.h> 3125cf1a30Sjl #include <sys/stat.h> 3225cf1a30Sjl #include <sys/async.h> 3325cf1a30Sjl #include <sys/machsystm.h> 34*0cc8ae86Sav #include <sys/promif.h> 3525cf1a30Sjl #include <sys/ksynch.h> 3625cf1a30Sjl #include <sys/ddi.h> 3725cf1a30Sjl #include <sys/sunddi.h> 3825cf1a30Sjl #include <sys/ddifm.h> 3925cf1a30Sjl #include <sys/fm/protocol.h> 4025cf1a30Sjl #include <sys/fm/util.h> 4125cf1a30Sjl #include <sys/kmem.h> 4225cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h> 4325cf1a30Sjl #include <sys/memlist.h> 4425cf1a30Sjl #include <sys/param.h> 45*0cc8ae86Sav #include <sys/disp.h> 4625cf1a30Sjl #include <sys/ontrap.h> 4725cf1a30Sjl #include <vm/page.h> 4825cf1a30Sjl #include <sys/mc-opl.h> 49*0cc8ae86Sav #include <sys/opl.h> 50*0cc8ae86Sav #include <sys/opl_dimm.h> 51*0cc8ae86Sav #include <sys/scfd/scfostoescf.h> 5225cf1a30Sjl 5325cf1a30Sjl /* 5425cf1a30Sjl * Function prototypes 5525cf1a30Sjl */ 5625cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *); 5725cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *); 5825cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 5925cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t); 6025cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t); 6125cf1a30Sjl 62*0cc8ae86Sav static int mc_poll_init(void); 63*0cc8ae86Sav static void mc_poll_fini(void); 6425cf1a30Sjl static int mc_board_add(mc_opl_t *mcp); 6525cf1a30Sjl static int mc_board_del(mc_opl_t *mcp); 6625cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag); 6725cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag); 68*0cc8ae86Sav int opl_mc_suspend(void); 69*0cc8ae86Sav int opl_mc_resume(void); 7025cf1a30Sjl 7125cf1a30Sjl static void insert_mcp(mc_opl_t *mcp); 7225cf1a30Sjl static void delete_mcp(mc_opl_t *mcp); 7325cf1a30Sjl 7425cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr); 7525cf1a30Sjl 7625cf1a30Sjl static int mc_valid_pa(mc_opl_t *mcp, uint64_t pa); 7725cf1a30Sjl 7825cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *); 79*0cc8ae86Sav int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr); 80*0cc8ae86Sav int mc_get_mem_offset(uint64_t paddr, uint64_t *offp); 81*0cc8ae86Sav int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp); 82*0cc8ae86Sav int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 83*0cc8ae86Sav int buflen, int *lenp); 84*0cc8ae86Sav mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp); 85*0cc8ae86Sav mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp); 86*0cc8ae86Sav int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank, 87*0cc8ae86Sav uint32_t mf_type, uint32_t d_slot); 88*0cc8ae86Sav static void mc_free_dimm_list(mc_dimm_info_t *d); 8925cf1a30Sjl static void mc_get_mlist(mc_opl_t *); 90*0cc8ae86Sav static void mc_polling(void); 91*0cc8ae86Sav static int mc_opl_get_physical_board(int); 92*0cc8ae86Sav 93*0cc8ae86Sav #ifdef DEBUG 94*0cc8ae86Sav static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *); 95*0cc8ae86Sav void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz); 96*0cc8ae86Sav void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp); 97*0cc8ae86Sav #endif 9825cf1a30Sjl 9925cf1a30Sjl #pragma weak opl_get_physical_board 10025cf1a30Sjl extern int opl_get_physical_board(int); 101*0cc8ae86Sav extern int plat_max_boards(void); 10225cf1a30Sjl 10325cf1a30Sjl /* 10425cf1a30Sjl * Configuration data structures 10525cf1a30Sjl */ 10625cf1a30Sjl static struct cb_ops mc_cb_ops = { 10725cf1a30Sjl mc_open, /* open */ 10825cf1a30Sjl mc_close, /* close */ 10925cf1a30Sjl nulldev, /* strategy */ 11025cf1a30Sjl nulldev, /* print */ 11125cf1a30Sjl nodev, /* dump */ 11225cf1a30Sjl nulldev, /* read */ 11325cf1a30Sjl nulldev, /* write */ 11425cf1a30Sjl mc_ioctl, /* ioctl */ 11525cf1a30Sjl nodev, /* devmap */ 11625cf1a30Sjl nodev, /* mmap */ 11725cf1a30Sjl nodev, /* segmap */ 11825cf1a30Sjl nochpoll, /* poll */ 11925cf1a30Sjl ddi_prop_op, /* cb_prop_op */ 12025cf1a30Sjl 0, /* streamtab */ 12125cf1a30Sjl D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 12225cf1a30Sjl CB_REV, /* rev */ 12325cf1a30Sjl nodev, /* cb_aread */ 12425cf1a30Sjl nodev /* cb_awrite */ 12525cf1a30Sjl }; 12625cf1a30Sjl 12725cf1a30Sjl static struct dev_ops mc_ops = { 12825cf1a30Sjl DEVO_REV, /* rev */ 12925cf1a30Sjl 0, /* refcnt */ 13025cf1a30Sjl ddi_getinfo_1to1, /* getinfo */ 13125cf1a30Sjl nulldev, /* identify */ 13225cf1a30Sjl nulldev, /* probe */ 13325cf1a30Sjl mc_attach, /* attach */ 13425cf1a30Sjl mc_detach, /* detach */ 13525cf1a30Sjl nulldev, /* reset */ 13625cf1a30Sjl &mc_cb_ops, /* cb_ops */ 13725cf1a30Sjl (struct bus_ops *)0, /* bus_ops */ 13825cf1a30Sjl nulldev /* power */ 13925cf1a30Sjl }; 14025cf1a30Sjl 14125cf1a30Sjl /* 14225cf1a30Sjl * Driver globals 14325cf1a30Sjl */ 14425cf1a30Sjl 145*0cc8ae86Sav static enum { 146*0cc8ae86Sav MODEL_FF1 = 0, 147*0cc8ae86Sav MODEL_FF2 = 1, 148*0cc8ae86Sav MODEL_DC = 2 149*0cc8ae86Sav } plat_model = MODEL_DC; /* The default behaviour is DC */ 150*0cc8ae86Sav 151*0cc8ae86Sav static struct plat_model_names { 152*0cc8ae86Sav const char *unit_name; 153*0cc8ae86Sav const char *mem_name; 154*0cc8ae86Sav } model_names[] = { 155*0cc8ae86Sav { "MBU_A", "MEMB" }, 156*0cc8ae86Sav { "MBU_B", "MEMB" }, 157*0cc8ae86Sav { "CMU", "" } 158*0cc8ae86Sav }; 15925cf1a30Sjl 160*0cc8ae86Sav /* 161*0cc8ae86Sav * The DIMM Names for DC platform. 162*0cc8ae86Sav * The index into this table is made up of (bank, dslot), 163*0cc8ae86Sav * Where dslot occupies bits 0-1 and bank occupies 2-4. 164*0cc8ae86Sav */ 165*0cc8ae86Sav static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = { 166*0cc8ae86Sav /* --------CMUnn----------- */ 167*0cc8ae86Sav /* --CS0-----|--CS1------ */ 168*0cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 169*0cc8ae86Sav "03A", "02A", "03B", "02B", /* MAC 0 bank 0 */ 170*0cc8ae86Sav "13A", "12A", "13B", "12B", /* MAC 0 bank 1 */ 171*0cc8ae86Sav "23A", "22A", "23B", "22B", /* MAC 1 bank 2 */ 172*0cc8ae86Sav "33A", "32A", "33B", "32B", /* MAC 1 bank 3 */ 173*0cc8ae86Sav "01A", "00A", "01B", "00B", /* MAC 2 bank 4 */ 174*0cc8ae86Sav "11A", "10A", "11B", "10B", /* MAC 2 bank 5 */ 175*0cc8ae86Sav "21A", "20A", "21B", "20B", /* MAC 3 bank 6 */ 176*0cc8ae86Sav "31A", "30A", "31B", "30B" /* MAC 3 bank 7 */ 177*0cc8ae86Sav }; 178*0cc8ae86Sav 179*0cc8ae86Sav /* 180*0cc8ae86Sav * The DIMM Names for FF1/FF2 platforms. 181*0cc8ae86Sav * The index into this table is made up of (board, bank, dslot), 182*0cc8ae86Sav * Where dslot occupies bits 0-1, bank occupies 2-4 and 183*0cc8ae86Sav * board occupies the bit 5. 184*0cc8ae86Sav */ 185*0cc8ae86Sav static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = { 186*0cc8ae86Sav /* --------CMU0---------- */ 187*0cc8ae86Sav /* --CS0-----|--CS1------ */ 188*0cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 189*0cc8ae86Sav "03A", "02A", "03B", "02B", /* MAC 0 bank 0 */ 190*0cc8ae86Sav "01A", "00A", "01B", "00B", /* MAC 0 bank 1 */ 191*0cc8ae86Sav "13A", "12A", "13B", "12B", /* MAC 1 bank 2 */ 192*0cc8ae86Sav "11A", "10A", "11B", "10B", /* MAC 1 bank 3 */ 193*0cc8ae86Sav "23A", "20A", "23B", "20B", /* MAC 2 bank 4 */ 194*0cc8ae86Sav "21A", "20A", "21B", "20B", /* MAC 2 bank 5 */ 195*0cc8ae86Sav "33A", "32A", "33B", "32B", /* MAC 3 bank 6 */ 196*0cc8ae86Sav "31A", "30A", "31B", "30B", /* MAC 3 bank 7 */ 197*0cc8ae86Sav /* --------CMU1---------- */ 198*0cc8ae86Sav /* --CS0-----|--CS1------ */ 199*0cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 200*0cc8ae86Sav "43A", "42A", "43B", "42B", /* MAC 0 bank 0 */ 201*0cc8ae86Sav "41A", "40A", "41B", "40B", /* MAC 0 bank 1 */ 202*0cc8ae86Sav "53A", "52A", "53B", "50B", /* MAC 1 bank 2 */ 203*0cc8ae86Sav "51A", "50A", "51B", "50B", /* MAC 1 bank 3 */ 204*0cc8ae86Sav "63A", "62A", "63B", "62B", /* MAC 2 bank 4 */ 205*0cc8ae86Sav "61A", "60A", "61B", "60B", /* MAC 2 bank 5 */ 206*0cc8ae86Sav "73A", "72A", "73B", "72B", /* MAC 3 bank 6 */ 207*0cc8ae86Sav "71A", "70A", "71B", "70B" /* MAC 3 bank 7 */ 208*0cc8ae86Sav }; 209*0cc8ae86Sav 210*0cc8ae86Sav #define BD_BK_SLOT_TO_INDEX(bd, bk, s) \ 211*0cc8ae86Sav (((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03)) 212*0cc8ae86Sav 213*0cc8ae86Sav #define INDEX_TO_BANK(i) (((i) & 0x1C) >> 2) 214*0cc8ae86Sav #define INDEX_TO_SLOT(i) ((i) & 0x03) 215*0cc8ae86Sav 216*0cc8ae86Sav /* Isolation unit size is 64 MB */ 217*0cc8ae86Sav #define MC_ISOLATION_BSIZE (64 * 1024 * 1024) 218*0cc8ae86Sav 219*0cc8ae86Sav #define MC_MAX_SPEEDS 7 220*0cc8ae86Sav 221*0cc8ae86Sav /* make this a structure */ 222*0cc8ae86Sav 223*0cc8ae86Sav typedef struct { 224*0cc8ae86Sav uint32_t mc_speeds; 225*0cc8ae86Sav uint32_t mc_period; 226*0cc8ae86Sav } mc_scan_speed_t; 227*0cc8ae86Sav 228*0cc8ae86Sav #define MC_CNTL_SPEED_SHIFT 26 229*0cc8ae86Sav 230*0cc8ae86Sav static mc_scan_speed_t mc_scan_speeds[MC_MAX_SPEEDS] = { 231*0cc8ae86Sav {0x6 << MC_CNTL_SPEED_SHIFT, 0}, 232*0cc8ae86Sav {0x5 << MC_CNTL_SPEED_SHIFT, 32}, 233*0cc8ae86Sav {0x4 << MC_CNTL_SPEED_SHIFT, 64}, 234*0cc8ae86Sav {0x3 << MC_CNTL_SPEED_SHIFT, 128}, 235*0cc8ae86Sav {0x2 << MC_CNTL_SPEED_SHIFT, 256}, 236*0cc8ae86Sav {0x1 << MC_CNTL_SPEED_SHIFT, 512}, 237*0cc8ae86Sav {0x0 << MC_CNTL_SPEED_SHIFT, 1024} 238*0cc8ae86Sav }; 239*0cc8ae86Sav 240*0cc8ae86Sav static uint32_t mc_max_speed = (0x6 << 26); 241*0cc8ae86Sav 242*0cc8ae86Sav /* we have to measure these delays */ 243*0cc8ae86Sav 244*0cc8ae86Sav int mc_isolation_bsize = MC_ISOLATION_BSIZE; 245*0cc8ae86Sav int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC; 246*0cc8ae86Sav int mc_max_scf_retry = 16; 247*0cc8ae86Sav int mc_max_scf_logs = 64; 248*0cc8ae86Sav int mc_max_errlog_processed = BANKNUM_PER_SB*2; 249*0cc8ae86Sav int mc_scan_period = 12 * 60 * 60; /* 12 hours period */ 250*0cc8ae86Sav int mc_max_rewrite_loop = 100; 251*0cc8ae86Sav int mc_rewrite_delay = 10; 252*0cc8ae86Sav /* 253*0cc8ae86Sav * it takes SCF about 300 m.s. to process a requst. We can bail out 254*0cc8ae86Sav * if it is busy. It does not pay to wait for it too long. 255*0cc8ae86Sav */ 256*0cc8ae86Sav int mc_max_scf_loop = 2; 257*0cc8ae86Sav int mc_scf_delay = 100; 258*0cc8ae86Sav int mc_pce_dropped = 0; 259*0cc8ae86Sav int mc_poll_priority = MINCLSYSPRI; 26025cf1a30Sjl 261*0cc8ae86Sav 262*0cc8ae86Sav /* 263*0cc8ae86Sav * Mutex heierachy in mc-opl 264*0cc8ae86Sav * If both mcmutex and mc_lock must be held, 265*0cc8ae86Sav * mcmutex must be acquired first, and then mc_lock. 266*0cc8ae86Sav */ 267*0cc8ae86Sav 268*0cc8ae86Sav static kmutex_t mcmutex; 269*0cc8ae86Sav mc_opl_t *mc_instances[OPL_MAX_BOARDS]; 270*0cc8ae86Sav 271*0cc8ae86Sav static kmutex_t mc_polling_lock; 272*0cc8ae86Sav static kcondvar_t mc_polling_cv; 273*0cc8ae86Sav static kcondvar_t mc_poll_exit_cv; 274*0cc8ae86Sav static int mc_poll_cmd = 0; 275*0cc8ae86Sav static int mc_pollthr_running = 0; 276*0cc8ae86Sav int mc_timeout_period = 0; /* this is in m.s. */ 27725cf1a30Sjl void *mc_statep; 27825cf1a30Sjl 27925cf1a30Sjl #ifdef DEBUG 2802742aa22Shyw int oplmc_debug = 0; 28125cf1a30Sjl #endif 28225cf1a30Sjl 283*0cc8ae86Sav static int mc_debug_show_all = 0; 28425cf1a30Sjl 28525cf1a30Sjl extern struct mod_ops mod_driverops; 28625cf1a30Sjl 28725cf1a30Sjl static struct modldrv modldrv = { 28825cf1a30Sjl &mod_driverops, /* module type, this one is a driver */ 289*0cc8ae86Sav "OPL Memory-controller %I%", /* module name */ 29025cf1a30Sjl &mc_ops, /* driver ops */ 29125cf1a30Sjl }; 29225cf1a30Sjl 29325cf1a30Sjl static struct modlinkage modlinkage = { 29425cf1a30Sjl MODREV_1, /* rev */ 29525cf1a30Sjl (void *)&modldrv, 29625cf1a30Sjl NULL 29725cf1a30Sjl }; 29825cf1a30Sjl 29925cf1a30Sjl #pragma weak opl_get_mem_unum 300*0cc8ae86Sav #pragma weak opl_get_mem_sid 301*0cc8ae86Sav #pragma weak opl_get_mem_offset 302*0cc8ae86Sav #pragma weak opl_get_mem_addr 303*0cc8ae86Sav 30425cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *); 305*0cc8ae86Sav extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp); 306*0cc8ae86Sav extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp); 307*0cc8ae86Sav extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset, 308*0cc8ae86Sav uint64_t *paddr); 309*0cc8ae86Sav 31025cf1a30Sjl 31125cf1a30Sjl /* 31225cf1a30Sjl * pseudo-mc node portid format 31325cf1a30Sjl * 31425cf1a30Sjl * [10] = 0 31525cf1a30Sjl * [9] = 1 31625cf1a30Sjl * [8] = LSB_ID[4] = 0 31725cf1a30Sjl * [7:4] = LSB_ID[3:0] 31825cf1a30Sjl * [3:0] = 0 31925cf1a30Sjl * 32025cf1a30Sjl */ 32125cf1a30Sjl 32225cf1a30Sjl /* 32325cf1a30Sjl * These are the module initialization routines. 32425cf1a30Sjl */ 32525cf1a30Sjl int 32625cf1a30Sjl _init(void) 32725cf1a30Sjl { 328*0cc8ae86Sav int error; 329*0cc8ae86Sav int plen; 330*0cc8ae86Sav char model[20]; 331*0cc8ae86Sav pnode_t node; 33225cf1a30Sjl 33325cf1a30Sjl 33425cf1a30Sjl if ((error = ddi_soft_state_init(&mc_statep, 33525cf1a30Sjl sizeof (mc_opl_t), 1)) != 0) 33625cf1a30Sjl return (error); 33725cf1a30Sjl 338*0cc8ae86Sav if ((error = mc_poll_init()) != 0) { 339*0cc8ae86Sav ddi_soft_state_fini(&mc_statep); 340*0cc8ae86Sav return (error); 341*0cc8ae86Sav } 342*0cc8ae86Sav 34325cf1a30Sjl mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL); 34425cf1a30Sjl if (&opl_get_mem_unum) 34525cf1a30Sjl opl_get_mem_unum = mc_get_mem_unum; 346*0cc8ae86Sav if (&opl_get_mem_sid) 347*0cc8ae86Sav opl_get_mem_sid = mc_get_mem_sid; 348*0cc8ae86Sav if (&opl_get_mem_offset) 349*0cc8ae86Sav opl_get_mem_offset = mc_get_mem_offset; 350*0cc8ae86Sav if (&opl_get_mem_addr) 351*0cc8ae86Sav opl_get_mem_addr = mc_get_mem_addr; 352*0cc8ae86Sav 353*0cc8ae86Sav node = prom_rootnode(); 354*0cc8ae86Sav plen = prom_getproplen(node, "model"); 355*0cc8ae86Sav 356*0cc8ae86Sav if (plen > 0 && plen < sizeof (model)) { 357*0cc8ae86Sav (void) prom_getprop(node, "model", model); 358*0cc8ae86Sav model[plen] = '\0'; 359*0cc8ae86Sav if (strcmp(model, "FF1") == 0) 360*0cc8ae86Sav plat_model = MODEL_FF1; 361*0cc8ae86Sav else if (strcmp(model, "FF2") == 0) 362*0cc8ae86Sav plat_model = MODEL_FF2; 363*0cc8ae86Sav else if (strncmp(model, "DC", 2) == 0) 364*0cc8ae86Sav plat_model = MODEL_DC; 365*0cc8ae86Sav } 36625cf1a30Sjl 36725cf1a30Sjl error = mod_install(&modlinkage); 36825cf1a30Sjl if (error != 0) { 36925cf1a30Sjl if (&opl_get_mem_unum) 37025cf1a30Sjl opl_get_mem_unum = NULL; 371*0cc8ae86Sav if (&opl_get_mem_sid) 372*0cc8ae86Sav opl_get_mem_sid = NULL; 373*0cc8ae86Sav if (&opl_get_mem_offset) 374*0cc8ae86Sav opl_get_mem_offset = NULL; 375*0cc8ae86Sav if (&opl_get_mem_addr) 376*0cc8ae86Sav opl_get_mem_addr = NULL; 37725cf1a30Sjl mutex_destroy(&mcmutex); 378*0cc8ae86Sav mc_poll_fini(); 37925cf1a30Sjl ddi_soft_state_fini(&mc_statep); 38025cf1a30Sjl } 38125cf1a30Sjl return (error); 38225cf1a30Sjl } 38325cf1a30Sjl 38425cf1a30Sjl int 38525cf1a30Sjl _fini(void) 38625cf1a30Sjl { 38725cf1a30Sjl int error; 38825cf1a30Sjl 38925cf1a30Sjl if ((error = mod_remove(&modlinkage)) != 0) 39025cf1a30Sjl return (error); 39125cf1a30Sjl 39225cf1a30Sjl if (&opl_get_mem_unum) 39325cf1a30Sjl opl_get_mem_unum = NULL; 394*0cc8ae86Sav if (&opl_get_mem_sid) 395*0cc8ae86Sav opl_get_mem_sid = NULL; 396*0cc8ae86Sav if (&opl_get_mem_offset) 397*0cc8ae86Sav opl_get_mem_offset = NULL; 398*0cc8ae86Sav if (&opl_get_mem_addr) 399*0cc8ae86Sav opl_get_mem_addr = NULL; 40025cf1a30Sjl 401*0cc8ae86Sav mutex_destroy(&mcmutex); 402*0cc8ae86Sav mc_poll_fini(); 40325cf1a30Sjl ddi_soft_state_fini(&mc_statep); 40425cf1a30Sjl 40525cf1a30Sjl return (0); 40625cf1a30Sjl } 40725cf1a30Sjl 40825cf1a30Sjl int 40925cf1a30Sjl _info(struct modinfo *modinfop) 41025cf1a30Sjl { 41125cf1a30Sjl return (mod_info(&modlinkage, modinfop)); 41225cf1a30Sjl } 41325cf1a30Sjl 414*0cc8ae86Sav static void 415*0cc8ae86Sav mc_polling_thread() 416*0cc8ae86Sav { 417*0cc8ae86Sav mutex_enter(&mc_polling_lock); 418*0cc8ae86Sav mc_pollthr_running = 1; 419*0cc8ae86Sav while (!(mc_poll_cmd & MC_POLL_EXIT)) { 420*0cc8ae86Sav mc_polling(); 421*0cc8ae86Sav cv_timedwait(&mc_polling_cv, &mc_polling_lock, 422*0cc8ae86Sav ddi_get_lbolt() + mc_timeout_period); 423*0cc8ae86Sav } 424*0cc8ae86Sav mc_pollthr_running = 0; 425*0cc8ae86Sav 426*0cc8ae86Sav /* 427*0cc8ae86Sav * signal if any one is waiting for this thread to exit. 428*0cc8ae86Sav */ 429*0cc8ae86Sav cv_signal(&mc_poll_exit_cv); 430*0cc8ae86Sav mutex_exit(&mc_polling_lock); 431*0cc8ae86Sav thread_exit(); 432*0cc8ae86Sav /* NOTREACHED */ 433*0cc8ae86Sav } 434*0cc8ae86Sav 435*0cc8ae86Sav static int 436*0cc8ae86Sav mc_poll_init() 437*0cc8ae86Sav { 438*0cc8ae86Sav mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL); 439*0cc8ae86Sav cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL); 440*0cc8ae86Sav cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL); 441*0cc8ae86Sav return (0); 442*0cc8ae86Sav } 443*0cc8ae86Sav 444*0cc8ae86Sav static void 445*0cc8ae86Sav mc_poll_fini() 446*0cc8ae86Sav { 447*0cc8ae86Sav mutex_enter(&mc_polling_lock); 448*0cc8ae86Sav if (mc_pollthr_running) { 449*0cc8ae86Sav mc_poll_cmd = MC_POLL_EXIT; 450*0cc8ae86Sav cv_signal(&mc_polling_cv); 451*0cc8ae86Sav while (mc_pollthr_running) { 452*0cc8ae86Sav cv_wait(&mc_poll_exit_cv, &mc_polling_lock); 453*0cc8ae86Sav } 454*0cc8ae86Sav } 455*0cc8ae86Sav mutex_exit(&mc_polling_lock); 456*0cc8ae86Sav mutex_destroy(&mc_polling_lock); 457*0cc8ae86Sav cv_destroy(&mc_polling_cv); 458*0cc8ae86Sav cv_destroy(&mc_poll_exit_cv); 459*0cc8ae86Sav } 460*0cc8ae86Sav 46125cf1a30Sjl static int 46225cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 46325cf1a30Sjl { 46425cf1a30Sjl mc_opl_t *mcp; 46525cf1a30Sjl int instance; 466*0cc8ae86Sav int rv; 46725cf1a30Sjl 46825cf1a30Sjl /* get the instance of this devi */ 46925cf1a30Sjl instance = ddi_get_instance(devi); 47025cf1a30Sjl 47125cf1a30Sjl switch (cmd) { 47225cf1a30Sjl case DDI_ATTACH: 47325cf1a30Sjl break; 47425cf1a30Sjl case DDI_RESUME: 47525cf1a30Sjl mcp = ddi_get_soft_state(mc_statep, instance); 476*0cc8ae86Sav rv = mc_resume(mcp, MC_DRIVER_SUSPENDED); 477*0cc8ae86Sav return (rv); 47825cf1a30Sjl default: 47925cf1a30Sjl return (DDI_FAILURE); 48025cf1a30Sjl } 48125cf1a30Sjl 48225cf1a30Sjl if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS) 48325cf1a30Sjl return (DDI_FAILURE); 48425cf1a30Sjl 48525cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 48625cf1a30Sjl goto bad; 48725cf1a30Sjl } 48825cf1a30Sjl 489*0cc8ae86Sav if (mc_timeout_period == 0) { 490*0cc8ae86Sav mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi, 491*0cc8ae86Sav DDI_PROP_DONTPASS, "mc-timeout-interval-sec", 492*0cc8ae86Sav mc_patrol_interval_sec); 493*0cc8ae86Sav mc_timeout_period = drv_usectohz( 494*0cc8ae86Sav 1000000 * mc_patrol_interval_sec / OPL_MAX_BOARDS); 495*0cc8ae86Sav } 496*0cc8ae86Sav 49725cf1a30Sjl /* set informations in mc state */ 49825cf1a30Sjl mcp->mc_dip = devi; 49925cf1a30Sjl 50025cf1a30Sjl if (mc_board_add(mcp)) 50125cf1a30Sjl goto bad; 50225cf1a30Sjl 50325cf1a30Sjl insert_mcp(mcp); 504*0cc8ae86Sav 505*0cc8ae86Sav /* 506*0cc8ae86Sav * Start the polling thread if it is not running already. 507*0cc8ae86Sav */ 508*0cc8ae86Sav mutex_enter(&mc_polling_lock); 509*0cc8ae86Sav if (!mc_pollthr_running) { 510*0cc8ae86Sav (void) thread_create(NULL, 0, (void (*)())mc_polling_thread, 511*0cc8ae86Sav NULL, 0, &p0, TS_RUN, mc_poll_priority); 512*0cc8ae86Sav } 513*0cc8ae86Sav mutex_exit(&mc_polling_lock); 51425cf1a30Sjl ddi_report_dev(devi); 51525cf1a30Sjl 51625cf1a30Sjl return (DDI_SUCCESS); 51725cf1a30Sjl 51825cf1a30Sjl bad: 51925cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 52025cf1a30Sjl return (DDI_FAILURE); 52125cf1a30Sjl } 52225cf1a30Sjl 52325cf1a30Sjl /* ARGSUSED */ 52425cf1a30Sjl static int 52525cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 52625cf1a30Sjl { 527*0cc8ae86Sav int rv; 52825cf1a30Sjl int instance; 52925cf1a30Sjl mc_opl_t *mcp; 53025cf1a30Sjl 53125cf1a30Sjl /* get the instance of this devi */ 53225cf1a30Sjl instance = ddi_get_instance(devi); 53325cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 53425cf1a30Sjl return (DDI_FAILURE); 53525cf1a30Sjl } 53625cf1a30Sjl 53725cf1a30Sjl switch (cmd) { 53825cf1a30Sjl case DDI_SUSPEND: 539*0cc8ae86Sav rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED); 540*0cc8ae86Sav return (rv); 54125cf1a30Sjl case DDI_DETACH: 54225cf1a30Sjl break; 54325cf1a30Sjl default: 54425cf1a30Sjl return (DDI_FAILURE); 54525cf1a30Sjl } 54625cf1a30Sjl 547*0cc8ae86Sav delete_mcp(mcp); 54825cf1a30Sjl if (mc_board_del(mcp) != DDI_SUCCESS) { 54925cf1a30Sjl return (DDI_FAILURE); 55025cf1a30Sjl } 55125cf1a30Sjl 55225cf1a30Sjl /* free up the soft state */ 55325cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 55425cf1a30Sjl 55525cf1a30Sjl return (DDI_SUCCESS); 55625cf1a30Sjl } 55725cf1a30Sjl 55825cf1a30Sjl /* ARGSUSED */ 55925cf1a30Sjl static int 56025cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 56125cf1a30Sjl { 56225cf1a30Sjl return (0); 56325cf1a30Sjl } 56425cf1a30Sjl 56525cf1a30Sjl /* ARGSUSED */ 56625cf1a30Sjl static int 56725cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp) 56825cf1a30Sjl { 56925cf1a30Sjl return (0); 57025cf1a30Sjl } 57125cf1a30Sjl 57225cf1a30Sjl /* ARGSUSED */ 57325cf1a30Sjl static int 57425cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 57525cf1a30Sjl int *rvalp) 57625cf1a30Sjl { 577*0cc8ae86Sav #ifdef DEBUG 578*0cc8ae86Sav return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp)); 579*0cc8ae86Sav #else 58025cf1a30Sjl return (ENXIO); 581*0cc8ae86Sav #endif 58225cf1a30Sjl } 58325cf1a30Sjl 58425cf1a30Sjl /* 58525cf1a30Sjl * PA validity check: 58625cf1a30Sjl * This function return 1 if the PA is valid, otherwise 58725cf1a30Sjl * return 0. 58825cf1a30Sjl */ 58925cf1a30Sjl 59025cf1a30Sjl /* ARGSUSED */ 59125cf1a30Sjl static int 59225cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr) 59325cf1a30Sjl { 59425cf1a30Sjl /* 59525cf1a30Sjl * Check if the addr is on the board. 59625cf1a30Sjl */ 59725cf1a30Sjl if ((addr < mcp->mc_start_address) || 59825cf1a30Sjl (mcp->mc_start_address + mcp->mc_size <= addr)) 59925cf1a30Sjl return (0); 60025cf1a30Sjl 60125cf1a30Sjl if (mcp->mlist == NULL) 60225cf1a30Sjl mc_get_mlist(mcp); 60325cf1a30Sjl 60425cf1a30Sjl if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) { 60525cf1a30Sjl return (1); 60625cf1a30Sjl } 60725cf1a30Sjl return (0); 60825cf1a30Sjl } 60925cf1a30Sjl 61025cf1a30Sjl /* 61125cf1a30Sjl * mac-pa translation routines. 61225cf1a30Sjl * 61325cf1a30Sjl * Input: mc driver state, (LSB#, Bank#, DIMM address) 61425cf1a30Sjl * Output: physical address 61525cf1a30Sjl * 61625cf1a30Sjl * Valid - return value: 0 61725cf1a30Sjl * Invalid - return value: -1 61825cf1a30Sjl */ 61925cf1a30Sjl static int 62025cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa) 62125cf1a30Sjl { 62225cf1a30Sjl int i; 62325cf1a30Sjl uint64_t pa_offset = 0; 62425cf1a30Sjl int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1; 62525cf1a30Sjl int bank = maddr->ma_bank; 62625cf1a30Sjl mc_addr_t maddr1; 62725cf1a30Sjl int bank0, bank1; 62825cf1a30Sjl 62925cf1a30Sjl MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank, 63025cf1a30Sjl maddr->ma_dimm_addr); 63125cf1a30Sjl 63225cf1a30Sjl /* loc validity check */ 63325cf1a30Sjl ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd); 63425cf1a30Sjl ASSERT(bank >= 0 && OPL_BANK_MAX > bank); 63525cf1a30Sjl 63625cf1a30Sjl /* Do translation */ 63725cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 63825cf1a30Sjl int pa_bit = 0; 63925cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 64025cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 64125cf1a30Sjl pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1; 64225cf1a30Sjl } else if (mc_bit == MP_NONE) { 64325cf1a30Sjl pa_bit = 0; 64425cf1a30Sjl } else if (mc_bit == MP_BANK_0) { 64525cf1a30Sjl pa_bit = bank & 1; 64625cf1a30Sjl } else if (mc_bit == MP_BANK_1) { 64725cf1a30Sjl pa_bit = (bank >> 1) & 1; 64825cf1a30Sjl } else if (mc_bit == MP_BANK_2) { 64925cf1a30Sjl pa_bit = (bank >> 2) & 1; 65025cf1a30Sjl } 65125cf1a30Sjl pa_offset |= ((uint64_t)pa_bit) << i; 65225cf1a30Sjl } 65325cf1a30Sjl *pa = mcp->mc_start_address + pa_offset; 65425cf1a30Sjl MC_LOG("pa = %lx\n", *pa); 65525cf1a30Sjl 65625cf1a30Sjl if (pa_to_maddr(mcp, *pa, &maddr1) == -1) { 657*0cc8ae86Sav cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to " 658*0cc8ae86Sav "convert PA %lx\n", maddr->ma_bd, bank, 659*0cc8ae86Sav maddr->ma_dimm_addr, *pa); 66025cf1a30Sjl return (-1); 66125cf1a30Sjl } 66225cf1a30Sjl 663*0cc8ae86Sav /* 664*0cc8ae86Sav * In mirror mode, PA is always translated to the even bank. 665*0cc8ae86Sav */ 66625cf1a30Sjl if (IS_MIRROR(mcp, maddr->ma_bank)) { 66725cf1a30Sjl bank0 = maddr->ma_bank & ~(1); 66825cf1a30Sjl bank1 = maddr1.ma_bank & ~(1); 66925cf1a30Sjl } else { 67025cf1a30Sjl bank0 = maddr->ma_bank; 67125cf1a30Sjl bank1 = maddr1.ma_bank; 67225cf1a30Sjl } 67325cf1a30Sjl /* 67425cf1a30Sjl * there is no need to check ma_bd because it is generated from 67525cf1a30Sjl * mcp. They are the same. 67625cf1a30Sjl */ 67725cf1a30Sjl if ((bank0 == bank1) && 67825cf1a30Sjl (maddr->ma_dimm_addr == maddr1.ma_dimm_addr)) { 67925cf1a30Sjl return (0); 68025cf1a30Sjl } else { 68125cf1a30Sjl cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, " 68225cf1a30Sjl "PA %lx, target /LSB%d/B%d/%x\n", 68325cf1a30Sjl maddr->ma_bd, bank, maddr->ma_dimm_addr, 68425cf1a30Sjl *pa, maddr1.ma_bd, maddr1.ma_bank, 68525cf1a30Sjl maddr1.ma_dimm_addr); 68625cf1a30Sjl return (-1); 68725cf1a30Sjl } 68825cf1a30Sjl } 68925cf1a30Sjl 69025cf1a30Sjl /* 69125cf1a30Sjl * PA to CS (used by pa_to_maddr). 69225cf1a30Sjl */ 69325cf1a30Sjl static int 69425cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset) 69525cf1a30Sjl { 69625cf1a30Sjl int i; 69725cf1a30Sjl int cs = 0; 69825cf1a30Sjl 69925cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 70025cf1a30Sjl /* MAC address bit<29> is arranged on the same PA bit */ 70125cf1a30Sjl /* on both table. So we may use any table. */ 70225cf1a30Sjl if (mcp->mc_trans_table[0][i] == CS_SHIFT) { 70325cf1a30Sjl cs = (pa_offset >> i) & 1; 70425cf1a30Sjl break; 70525cf1a30Sjl } 70625cf1a30Sjl } 70725cf1a30Sjl return (cs); 70825cf1a30Sjl } 70925cf1a30Sjl 71025cf1a30Sjl /* 71125cf1a30Sjl * PA to DIMM (used by pa_to_maddr). 71225cf1a30Sjl */ 71325cf1a30Sjl /* ARGSUSED */ 71425cf1a30Sjl static uint32_t 71525cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset) 71625cf1a30Sjl { 71725cf1a30Sjl int i; 71825cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 71925cf1a30Sjl uint32_t dimm_addr = 0; 72025cf1a30Sjl 72125cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 72225cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 72325cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 72425cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 72525cf1a30Sjl dimm_addr |= pa_bit_value << mc_bit; 72625cf1a30Sjl } 72725cf1a30Sjl } 72825cf1a30Sjl return (dimm_addr); 72925cf1a30Sjl } 73025cf1a30Sjl 73125cf1a30Sjl /* 73225cf1a30Sjl * PA to Bank (used by pa_to_maddr). 73325cf1a30Sjl */ 73425cf1a30Sjl static int 73525cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset) 73625cf1a30Sjl { 73725cf1a30Sjl int i; 73825cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 73925cf1a30Sjl int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT]; 74025cf1a30Sjl 74125cf1a30Sjl 74225cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 74325cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 74425cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 74525cf1a30Sjl switch (mc_bit) { 74625cf1a30Sjl case MP_BANK_0: 74725cf1a30Sjl bankno |= pa_bit_value; 74825cf1a30Sjl break; 74925cf1a30Sjl case MP_BANK_1: 75025cf1a30Sjl bankno |= pa_bit_value << 1; 75125cf1a30Sjl break; 75225cf1a30Sjl case MP_BANK_2: 75325cf1a30Sjl bankno |= pa_bit_value << 2; 75425cf1a30Sjl break; 75525cf1a30Sjl } 75625cf1a30Sjl } 75725cf1a30Sjl 75825cf1a30Sjl return (bankno); 75925cf1a30Sjl } 76025cf1a30Sjl 76125cf1a30Sjl /* 76225cf1a30Sjl * PA to MAC address translation 76325cf1a30Sjl * 76425cf1a30Sjl * Input: MAC driver state, physicall adress 76525cf1a30Sjl * Output: LSB#, Bank id, mac address 76625cf1a30Sjl * 76725cf1a30Sjl * Valid - return value: 0 76825cf1a30Sjl * Invalid - return value: -1 76925cf1a30Sjl */ 77025cf1a30Sjl 77125cf1a30Sjl int 77225cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr) 77325cf1a30Sjl { 77425cf1a30Sjl uint64_t pa_offset; 77525cf1a30Sjl 77625cf1a30Sjl /* PA validity check */ 77725cf1a30Sjl if (!pa_is_valid(mcp, pa)) 77825cf1a30Sjl return (-1); 77925cf1a30Sjl 78025cf1a30Sjl 78125cf1a30Sjl /* Do translation */ 78225cf1a30Sjl pa_offset = pa - mcp->mc_start_address; 78325cf1a30Sjl 78425cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 78525cf1a30Sjl maddr->ma_bank = pa_to_bank(mcp, pa_offset); 78625cf1a30Sjl maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset); 78725cf1a30Sjl MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", 78825cf1a30Sjl pa_offset, maddr->ma_bd, maddr->ma_bank, maddr->ma_dimm_addr); 78925cf1a30Sjl return (0); 79025cf1a30Sjl } 79125cf1a30Sjl 792*0cc8ae86Sav /* 793*0cc8ae86Sav * UNUM format for DC is "/CMUnn/MEMxyZ", where 794*0cc8ae86Sav * nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3. 795*0cc8ae86Sav * x = MAC 0..3 796*0cc8ae86Sav * y = 0..3 (slot info). 797*0cc8ae86Sav * Z = 'A' or 'B' 798*0cc8ae86Sav * 799*0cc8ae86Sav * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where 800*0cc8ae86Sav * x = 0..3 (MEMB number) 801*0cc8ae86Sav * y = 0..3 (slot info). 802*0cc8ae86Sav * Z = 'A' or 'B' 803*0cc8ae86Sav * 804*0cc8ae86Sav * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ" 805*0cc8ae86Sav * x = 0..7 (MEMB number) 806*0cc8ae86Sav * y = 0..3 (slot info). 807*0cc8ae86Sav * Z = 'A' or 'B' 808*0cc8ae86Sav */ 809*0cc8ae86Sav int 810*0cc8ae86Sav mc_set_mem_unum(char *buf, int buflen, int lsb, int bank, 811*0cc8ae86Sav uint32_t mf_type, uint32_t d_slot) 812*0cc8ae86Sav { 813*0cc8ae86Sav char *dimmnm; 814*0cc8ae86Sav char memb_num; 815*0cc8ae86Sav int sb; 816*0cc8ae86Sav int i; 817*0cc8ae86Sav 818*0cc8ae86Sav if ((sb = mc_opl_get_physical_board(lsb)) < 0) 819*0cc8ae86Sav return (ENODEV); 820*0cc8ae86Sav 821*0cc8ae86Sav if (plat_model == MODEL_DC) { 822*0cc8ae86Sav if (mf_type == FLT_TYPE_PERMANENT_CE) { 823*0cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 824*0cc8ae86Sav dimmnm = mc_dc_dimm_unum_table[i]; 825*0cc8ae86Sav snprintf(buf, buflen, "/%s%02d/MEM%s", 826*0cc8ae86Sav model_names[plat_model].unit_name, sb, dimmnm); 827*0cc8ae86Sav } else { 828*0cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 829*0cc8ae86Sav snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s MEM%s MEM%s", 830*0cc8ae86Sav model_names[plat_model].unit_name, sb, 831*0cc8ae86Sav mc_dc_dimm_unum_table[i], 832*0cc8ae86Sav mc_dc_dimm_unum_table[i + 1], 833*0cc8ae86Sav mc_dc_dimm_unum_table[i + 2], 834*0cc8ae86Sav mc_dc_dimm_unum_table[i + 3]); 835*0cc8ae86Sav } 836*0cc8ae86Sav } else { 837*0cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 838*0cc8ae86Sav if (mf_type == FLT_TYPE_PERMANENT_CE) { 839*0cc8ae86Sav dimmnm = mc_ff_dimm_unum_table[i]; 840*0cc8ae86Sav memb_num = dimmnm[0]; 841*0cc8ae86Sav snprintf(buf, buflen, "/%s/%s%c/MEM%s", 842*0cc8ae86Sav model_names[plat_model].unit_name, 843*0cc8ae86Sav model_names[plat_model].mem_name, 844*0cc8ae86Sav memb_num, &dimmnm[1]); 845*0cc8ae86Sav } else { 846*0cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 847*0cc8ae86Sav memb_num = mc_ff_dimm_unum_table[i][0], 848*0cc8ae86Sav snprintf(buf, buflen, 849*0cc8ae86Sav "/%s/%s%c/MEM%s MEM%s MEM%s MEM%s", 850*0cc8ae86Sav model_names[plat_model].unit_name, 851*0cc8ae86Sav model_names[plat_model].mem_name, memb_num, 852*0cc8ae86Sav &mc_ff_dimm_unum_table[i][1], 853*0cc8ae86Sav &mc_ff_dimm_unum_table[i + 1][1], 854*0cc8ae86Sav &mc_ff_dimm_unum_table[i + 2][1], 855*0cc8ae86Sav &mc_ff_dimm_unum_table[i + 3][1]); 856*0cc8ae86Sav } 857*0cc8ae86Sav } 858*0cc8ae86Sav return (0); 859*0cc8ae86Sav } 860*0cc8ae86Sav 86125cf1a30Sjl static void 86225cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt) 86325cf1a30Sjl { 86425cf1a30Sjl char buf[FM_MAX_CLASS]; 86525cf1a30Sjl char device_path[MAXPATHLEN]; 866*0cc8ae86Sav char sid[MAXPATHLEN]; 86725cf1a30Sjl nv_alloc_t *nva = NULL; 86825cf1a30Sjl nvlist_t *ereport, *detector, *resource; 86925cf1a30Sjl errorq_elem_t *eqep; 87025cf1a30Sjl int nflts; 87125cf1a30Sjl mc_flt_stat_t *flt_stat; 872*0cc8ae86Sav int i, n; 873*0cc8ae86Sav int blen = MAXPATHLEN; 874*0cc8ae86Sav char *p, *s = NULL; 87525cf1a30Sjl uint32_t values[2], synd[2], dslot[2]; 876*0cc8ae86Sav uint64_t offset = (uint64_t)-1; 877*0cc8ae86Sav int ret = -1; 87825cf1a30Sjl 87925cf1a30Sjl if (panicstr) { 88025cf1a30Sjl eqep = errorq_reserve(ereport_errorq); 88125cf1a30Sjl if (eqep == NULL) 88225cf1a30Sjl return; 88325cf1a30Sjl ereport = errorq_elem_nvl(ereport_errorq, eqep); 88425cf1a30Sjl nva = errorq_elem_nva(ereport_errorq, eqep); 88525cf1a30Sjl } else { 88625cf1a30Sjl ereport = fm_nvlist_create(nva); 88725cf1a30Sjl } 88825cf1a30Sjl 88925cf1a30Sjl /* 89025cf1a30Sjl * Create the scheme "dev" FMRI. 89125cf1a30Sjl */ 89225cf1a30Sjl detector = fm_nvlist_create(nva); 89325cf1a30Sjl resource = fm_nvlist_create(nva); 89425cf1a30Sjl 89525cf1a30Sjl nflts = mc_aflt->mflt_nflts; 89625cf1a30Sjl 89725cf1a30Sjl ASSERT(nflts >= 1 && nflts <= 2); 89825cf1a30Sjl 89925cf1a30Sjl flt_stat = mc_aflt->mflt_stat[0]; 90025cf1a30Sjl (void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path); 90125cf1a30Sjl (void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 90225cf1a30Sjl device_path, NULL); 90325cf1a30Sjl 90425cf1a30Sjl /* 90525cf1a30Sjl * Encode all the common data into the ereport. 90625cf1a30Sjl */ 90725cf1a30Sjl (void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", 90825cf1a30Sjl MC_OPL_ERROR_CLASS, 90925cf1a30Sjl mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : 91025cf1a30Sjl MC_OPL_MI_SUBCLASS, 91125cf1a30Sjl mc_aflt->mflt_erpt_class); 91225cf1a30Sjl 91325cf1a30Sjl MC_LOG("mc_ereport_post: ereport %s\n", buf); 91425cf1a30Sjl 91525cf1a30Sjl 91625cf1a30Sjl fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, 91725cf1a30Sjl fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), 91825cf1a30Sjl detector, NULL); 91925cf1a30Sjl 92025cf1a30Sjl /* 92125cf1a30Sjl * Set payload. 92225cf1a30Sjl */ 92325cf1a30Sjl fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32, 92425cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd, NULL); 92525cf1a30Sjl 92625cf1a30Sjl fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64, 92725cf1a30Sjl flt_stat->mf_flt_paddr, NULL); 92825cf1a30Sjl 92925cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 93025cf1a30Sjl fm_payload_set(ereport, MC_OPL_FLT_TYPE, 93125cf1a30Sjl DATA_TYPE_UINT8, ECC_STICKY, NULL); 93225cf1a30Sjl } 93325cf1a30Sjl 93425cf1a30Sjl for (i = 0; i < nflts; i++) 93525cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank; 93625cf1a30Sjl 93725cf1a30Sjl fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, 93825cf1a30Sjl nflts, values, NULL); 93925cf1a30Sjl 94025cf1a30Sjl for (i = 0; i < nflts; i++) 94125cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_cntl; 94225cf1a30Sjl 94325cf1a30Sjl fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, 94425cf1a30Sjl nflts, values, NULL); 94525cf1a30Sjl 94625cf1a30Sjl for (i = 0; i < nflts; i++) 94725cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_add; 94825cf1a30Sjl 949*0cc8ae86Sav /* offset is set only for PCE */ 950*0cc8ae86Sav if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) { 951*0cc8ae86Sav offset = values[0]; 952*0cc8ae86Sav 953*0cc8ae86Sav } 95425cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, 95525cf1a30Sjl nflts, values, NULL); 95625cf1a30Sjl 95725cf1a30Sjl for (i = 0; i < nflts; i++) 95825cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_log; 95925cf1a30Sjl 96025cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, 96125cf1a30Sjl nflts, values, NULL); 96225cf1a30Sjl 96325cf1a30Sjl for (i = 0; i < nflts; i++) { 96425cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 96525cf1a30Sjl if (flt_stat->mf_errlog_valid) { 96625cf1a30Sjl synd[i] = flt_stat->mf_synd; 96725cf1a30Sjl dslot[i] = flt_stat->mf_dimm_slot; 96825cf1a30Sjl values[i] = flt_stat->mf_dram_place; 96925cf1a30Sjl } else { 97025cf1a30Sjl synd[i] = 0; 97125cf1a30Sjl dslot[i] = 0; 97225cf1a30Sjl values[i] = 0; 97325cf1a30Sjl } 97425cf1a30Sjl } 97525cf1a30Sjl 97625cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_SYND, 97725cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, synd, NULL); 97825cf1a30Sjl 97925cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, 98025cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, dslot, NULL); 98125cf1a30Sjl 98225cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_DRAM, 98325cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, values, NULL); 98425cf1a30Sjl 98525cf1a30Sjl device_path[0] = 0; 98625cf1a30Sjl p = &device_path[0]; 987*0cc8ae86Sav sid[0] = 0; 988*0cc8ae86Sav s = &sid[0]; 989*0cc8ae86Sav ret = 0; 99025cf1a30Sjl 99125cf1a30Sjl for (i = 0; i < nflts; i++) { 992*0cc8ae86Sav int bank; 99325cf1a30Sjl 99425cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 995*0cc8ae86Sav bank = flt_stat->mf_flt_maddr.ma_bank; 996*0cc8ae86Sav ret = mc_set_mem_unum(p + strlen(p), blen, 997*0cc8ae86Sav flt_stat->mf_flt_maddr.ma_bd, bank, flt_stat->mf_type, 998*0cc8ae86Sav flt_stat->mf_dimm_slot); 999*0cc8ae86Sav 1000*0cc8ae86Sav if (ret != 0) { 1001*0cc8ae86Sav cmn_err(CE_WARN, 1002*0cc8ae86Sav "mc_ereport_post: Failed to determine the unum " 1003*0cc8ae86Sav "for board=%d bank=%d type=0x%x slot=0x%x", 1004*0cc8ae86Sav flt_stat->mf_flt_maddr.ma_bd, bank, 1005*0cc8ae86Sav flt_stat->mf_type, flt_stat->mf_dimm_slot); 1006*0cc8ae86Sav continue; 100725cf1a30Sjl } 1008*0cc8ae86Sav n = strlen(device_path); 100925cf1a30Sjl blen = MAXPATHLEN - n; 101025cf1a30Sjl p = &device_path[n]; 101125cf1a30Sjl if (i < (nflts - 1)) { 101225cf1a30Sjl snprintf(p, blen, " "); 1013*0cc8ae86Sav blen--; 1014*0cc8ae86Sav p++; 1015*0cc8ae86Sav } 1016*0cc8ae86Sav 1017*0cc8ae86Sav if (ret == 0) { 1018*0cc8ae86Sav ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s), 1019*0cc8ae86Sav blen, flt_stat->mf_flt_maddr.ma_bd, bank, 1020*0cc8ae86Sav flt_stat->mf_type, flt_stat->mf_dimm_slot); 1021*0cc8ae86Sav 102225cf1a30Sjl } 102325cf1a30Sjl } 102425cf1a30Sjl 102525cf1a30Sjl (void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, 1026*0cc8ae86Sav NULL, device_path, (ret == 0) ? sid : NULL, 1027*0cc8ae86Sav (ret == 0) ? offset : (uint64_t)-1); 102825cf1a30Sjl 102925cf1a30Sjl fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, 103025cf1a30Sjl resource, NULL); 103125cf1a30Sjl 103225cf1a30Sjl if (panicstr) { 103325cf1a30Sjl errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); 103425cf1a30Sjl } else { 103525cf1a30Sjl (void) fm_ereport_post(ereport, EVCH_TRYHARD); 103625cf1a30Sjl fm_nvlist_destroy(ereport, FM_NVA_FREE); 103725cf1a30Sjl fm_nvlist_destroy(detector, FM_NVA_FREE); 103825cf1a30Sjl fm_nvlist_destroy(resource, FM_NVA_FREE); 103925cf1a30Sjl } 104025cf1a30Sjl } 104125cf1a30Sjl 1042*0cc8ae86Sav 104325cf1a30Sjl static void 104425cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt) 104525cf1a30Sjl { 104625cf1a30Sjl int rv; 104725cf1a30Sjl page_t *pp; 104825cf1a30Sjl uint64_t errors; 104925cf1a30Sjl uint64_t pa = (uint64_t)(-1); 1050*0cc8ae86Sav int i; 105125cf1a30Sjl 105225cf1a30Sjl MC_LOG("mc_err_drain: %s\n", 105325cf1a30Sjl mc_aflt->mflt_erpt_class); 105425cf1a30Sjl /* 105525cf1a30Sjl * we come here only when we have: 105625cf1a30Sjl * In mirror mode: CMPE, MUE, SUE 105725cf1a30Sjl * In normal mode: UE, Permanent CE 105825cf1a30Sjl */ 1059*0cc8ae86Sav for (i = 0; i < mc_aflt->mflt_nflts; i++) { 1060*0cc8ae86Sav rv = mcaddr_to_pa(mc_aflt->mflt_mcp, 1061*0cc8ae86Sav &(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa); 1062*0cc8ae86Sav if (rv == 0) 1063*0cc8ae86Sav mc_aflt->mflt_stat[i]->mf_flt_paddr = pa; 1064*0cc8ae86Sav else 1065*0cc8ae86Sav mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1; 1066*0cc8ae86Sav } 1067*0cc8ae86Sav 1068*0cc8ae86Sav if (mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) { 106925cf1a30Sjl MC_LOG("mc_err_drain:pa = %lx\n", pa); 107025cf1a30Sjl pp = page_numtopp_nolock(pa >> PAGESHIFT); 107125cf1a30Sjl 107225cf1a30Sjl if (pp) { 107325cf1a30Sjl /* 107425cf1a30Sjl * Don't keep retiring and make ereports 107525cf1a30Sjl * on bad pages in PTRL case 107625cf1a30Sjl */ 107725cf1a30Sjl MC_LOG("mc_err_drain:pp = %p\n", pp); 107825cf1a30Sjl if (mc_aflt->mflt_is_ptrl) { 107925cf1a30Sjl errors = 0; 108025cf1a30Sjl if (page_retire_check(pa, &errors) == 0) { 108125cf1a30Sjl MC_LOG("Page retired\n"); 108225cf1a30Sjl return; 108325cf1a30Sjl } 108425cf1a30Sjl if (errors & mc_aflt->mflt_pr) { 108525cf1a30Sjl MC_LOG("errors %lx, mflt_pr %x\n", 108625cf1a30Sjl errors, mc_aflt->mflt_pr); 108725cf1a30Sjl return; 108825cf1a30Sjl } 108925cf1a30Sjl } 109025cf1a30Sjl MC_LOG("offline page %p error %x\n", pp, 109125cf1a30Sjl mc_aflt->mflt_pr); 109225cf1a30Sjl (void) page_retire(pa, mc_aflt->mflt_pr); 109325cf1a30Sjl } 109425cf1a30Sjl } 109525cf1a30Sjl 1096*0cc8ae86Sav for (i = 0; i < mc_aflt->mflt_nflts; i++) { 1097*0cc8ae86Sav mc_aflt_t mc_aflt0; 1098*0cc8ae86Sav if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) { 1099*0cc8ae86Sav mc_aflt0 = *mc_aflt; 1100*0cc8ae86Sav mc_aflt0.mflt_nflts = 1; 1101*0cc8ae86Sav mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i]; 1102*0cc8ae86Sav mc_ereport_post(&mc_aflt0); 1103*0cc8ae86Sav } 1104*0cc8ae86Sav } 1105*0cc8ae86Sav } 110625cf1a30Sjl 110725cf1a30Sjl /* 110825cf1a30Sjl * The restart address is actually defined in unit of PA[37:6] 110925cf1a30Sjl * the mac patrol will convert that to dimm offset. If the 111025cf1a30Sjl * address is not in the bank, it will continue to search for 111125cf1a30Sjl * the next PA that is within the bank. 111225cf1a30Sjl * 111325cf1a30Sjl * Also the mac patrol scans the dimms based on PA, not 111425cf1a30Sjl * dimm offset. 111525cf1a30Sjl */ 111625cf1a30Sjl static int 111725cf1a30Sjl restart_patrol(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr_info) 111825cf1a30Sjl { 111925cf1a30Sjl page_t *pp; 112025cf1a30Sjl uint64_t pa; 112125cf1a30Sjl int rv; 112225cf1a30Sjl int loop_count = 0; 112325cf1a30Sjl 112425cf1a30Sjl if (maddr_info == NULL || (maddr_info->mi_valid == 0)) { 112525cf1a30Sjl MAC_PTRL_START(mcp, bank); 112625cf1a30Sjl return (0); 112725cf1a30Sjl } 112825cf1a30Sjl 112925cf1a30Sjl rv = mcaddr_to_pa(mcp, &maddr_info->mi_maddr, &pa); 113025cf1a30Sjl if (rv != 0) { 113125cf1a30Sjl MC_LOG("cannot convert mcaddr to pa. use auto restart\n"); 113225cf1a30Sjl MAC_PTRL_START(mcp, bank); 113325cf1a30Sjl return (0); 113425cf1a30Sjl } 113525cf1a30Sjl 113625cf1a30Sjl /* 113725cf1a30Sjl * pa is the last address scanned by the mac patrol 113825cf1a30Sjl * we calculate the next restart address as follows: 113925cf1a30Sjl * first we always advance it by 64 byte. Then begin the loop. 114025cf1a30Sjl * loop { 114125cf1a30Sjl * if it is not in phys_install, we advance to next 64 MB boundary 114225cf1a30Sjl * if it is not backed by a page structure, done 114325cf1a30Sjl * if the page is bad, advance to the next page boundary. 114425cf1a30Sjl * else done 114525cf1a30Sjl * if the new address exceeds the board, wrap around. 114625cf1a30Sjl * } <stop if we come back to the same page> 114725cf1a30Sjl */ 114825cf1a30Sjl 114925cf1a30Sjl if (pa < mcp->mc_start_address || pa >= (mcp->mc_start_address 115025cf1a30Sjl + mcp->mc_size)) { 115125cf1a30Sjl /* pa is not on this board, just retry */ 115225cf1a30Sjl cmn_err(CE_WARN, "restart_patrol: invalid address %lx " 115325cf1a30Sjl "on board %d\n", pa, mcp->mc_board_num); 115425cf1a30Sjl MAC_PTRL_START(mcp, bank); 115525cf1a30Sjl return (0); 115625cf1a30Sjl } 115725cf1a30Sjl 115825cf1a30Sjl MC_LOG("restart_patrol: pa = %lx\n", pa); 115925cf1a30Sjl if (maddr_info->mi_advance) { 116025cf1a30Sjl uint64_t new_pa; 116125cf1a30Sjl 116225cf1a30Sjl if (IS_MIRROR(mcp, bank)) 116325cf1a30Sjl new_pa = pa + 64 * 2; 116425cf1a30Sjl else 116525cf1a30Sjl new_pa = pa + 64; 116625cf1a30Sjl 116725cf1a30Sjl if (!mc_valid_pa(mcp, new_pa)) { 116825cf1a30Sjl MC_LOG("Invalid PA\n"); 1169*0cc8ae86Sav pa = roundup(new_pa + 1, mc_isolation_bsize); 117025cf1a30Sjl } else { 117125cf1a30Sjl pp = page_numtopp_nolock(new_pa >> PAGESHIFT); 117225cf1a30Sjl if (pp != NULL) { 117325cf1a30Sjl uint64_t errors = 0; 117425cf1a30Sjl if (page_retire_check(new_pa, &errors) && 117525cf1a30Sjl (errors == 0)) { 117625cf1a30Sjl MC_LOG("Page has no error\n"); 1177*0cc8ae86Sav pa = new_pa; 1178*0cc8ae86Sav goto done; 117925cf1a30Sjl } 118025cf1a30Sjl /* 118125cf1a30Sjl * skip bad pages 118225cf1a30Sjl * and let the following loop to take care 118325cf1a30Sjl */ 118425cf1a30Sjl pa = roundup(new_pa + 1, PAGESIZE); 118525cf1a30Sjl MC_LOG("Skipping bad page to %lx\n", pa); 118625cf1a30Sjl } else { 118725cf1a30Sjl MC_LOG("Page has no page structure\n"); 1188*0cc8ae86Sav pa = new_pa; 1189*0cc8ae86Sav goto done; 119025cf1a30Sjl } 119125cf1a30Sjl } 119225cf1a30Sjl } 119325cf1a30Sjl 119425cf1a30Sjl /* 119525cf1a30Sjl * if we wrap around twice, we just give up and let 119625cf1a30Sjl * mac patrol decide. 119725cf1a30Sjl */ 119825cf1a30Sjl MC_LOG("pa is now %lx\n", pa); 119925cf1a30Sjl while (loop_count <= 1) { 120025cf1a30Sjl if (!mc_valid_pa(mcp, pa)) { 120125cf1a30Sjl MC_LOG("pa is not valid. round up to 64 MB\n"); 120225cf1a30Sjl pa = roundup(pa + 1, 64 * 1024 * 1024); 120325cf1a30Sjl } else { 120425cf1a30Sjl pp = page_numtopp_nolock(pa >> PAGESHIFT); 120525cf1a30Sjl if (pp != NULL) { 120625cf1a30Sjl uint64_t errors = 0; 120725cf1a30Sjl if (page_retire_check(pa, &errors) && 120825cf1a30Sjl (errors == 0)) { 120925cf1a30Sjl MC_LOG("Page has no error\n"); 121025cf1a30Sjl break; 121125cf1a30Sjl } 121225cf1a30Sjl /* skip bad pages */ 121325cf1a30Sjl pa = roundup(pa + 1, PAGESIZE); 121425cf1a30Sjl MC_LOG("Skipping bad page to %lx\n", pa); 121525cf1a30Sjl } else { 121625cf1a30Sjl MC_LOG("Page has no page structure\n"); 121725cf1a30Sjl break; 121825cf1a30Sjl } 121925cf1a30Sjl } 122025cf1a30Sjl if (pa >= (mcp->mc_start_address + mcp->mc_size)) { 122125cf1a30Sjl MC_LOG("Wrap around\n"); 122225cf1a30Sjl pa = mcp->mc_start_address; 122325cf1a30Sjl loop_count++; 122425cf1a30Sjl } 122525cf1a30Sjl } 122625cf1a30Sjl 1227*0cc8ae86Sav done: 122825cf1a30Sjl /* retstart MAC patrol: PA[37:6] */ 122925cf1a30Sjl MC_LOG("restart at pa = %lx\n", pa); 123025cf1a30Sjl ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa)); 123125cf1a30Sjl MAC_PTRL_START_ADD(mcp, bank); 123225cf1a30Sjl 123325cf1a30Sjl return (0); 123425cf1a30Sjl } 123525cf1a30Sjl 123625cf1a30Sjl /* 123725cf1a30Sjl * Rewriting is used for two purposes. 123825cf1a30Sjl * - to correct the error in memory. 123925cf1a30Sjl * - to determine whether the error is permanent or intermittent. 124025cf1a30Sjl * It's done by writing the address in MAC_BANKm_REWRITE_ADD 124125cf1a30Sjl * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that, 124225cf1a30Sjl * REW_END (and REW_CE/REW_UE if some error detected) is set when 124325cf1a30Sjl * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM. 124425cf1a30Sjl * 124525cf1a30Sjl * Note that rewrite operation doesn't change RAW_UE to Marked UE. 124625cf1a30Sjl * Therefore, we use it only CE case. 124725cf1a30Sjl */ 124825cf1a30Sjl static uint32_t 124925cf1a30Sjl do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr) 125025cf1a30Sjl { 125125cf1a30Sjl uint32_t cntl; 125225cf1a30Sjl int count = 0; 125325cf1a30Sjl 125425cf1a30Sjl /* first wait to make sure PTRL_STATUS is 0 */ 1255*0cc8ae86Sav while (count++ < mc_max_rewrite_loop) { 125625cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 125725cf1a30Sjl if (!(cntl & MAC_CNTL_PTRL_STATUS)) 125825cf1a30Sjl break; 1259*0cc8ae86Sav drv_usecwait(mc_rewrite_delay); 126025cf1a30Sjl } 1261*0cc8ae86Sav if (count >= mc_max_rewrite_loop) 126225cf1a30Sjl goto bad; 126325cf1a30Sjl 126425cf1a30Sjl count = 0; 126525cf1a30Sjl 126625cf1a30Sjl ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr); 126725cf1a30Sjl MAC_REW_REQ(mcp, bank); 126825cf1a30Sjl 126925cf1a30Sjl do { 127025cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 1271*0cc8ae86Sav if (count++ >= mc_max_rewrite_loop) { 127225cf1a30Sjl goto bad; 1273*0cc8ae86Sav } else { 1274*0cc8ae86Sav drv_usecwait(mc_rewrite_delay); 1275*0cc8ae86Sav } 127625cf1a30Sjl /* 127725cf1a30Sjl * If there are other MEMORY or PCI activities, this 127825cf1a30Sjl * will be BUSY, else it should be set immediately 127925cf1a30Sjl */ 128025cf1a30Sjl } while (!(cntl & MAC_CNTL_REW_END)); 128125cf1a30Sjl 128225cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 128325cf1a30Sjl return (cntl); 128425cf1a30Sjl bad: 128525cf1a30Sjl /* This is bad. Just reset the circuit */ 128625cf1a30Sjl cmn_err(CE_WARN, "mc-opl rewrite timeout on /LSB%d/B%d\n", 128725cf1a30Sjl mcp->mc_board_num, bank); 128825cf1a30Sjl cntl = MAC_CNTL_REW_END; 128925cf1a30Sjl MAC_CMD(mcp, bank, MAC_CNTL_PTRL_RESET); 129025cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 129125cf1a30Sjl return (cntl); 129225cf1a30Sjl } 129325cf1a30Sjl void 129425cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp) 129525cf1a30Sjl { 1296*0cc8ae86Sav int count; 1297*0cc8ae86Sav int n = 0; 129825cf1a30Sjl scf_log_t *p; 129925cf1a30Sjl int bank; 130025cf1a30Sjl 1301*0cc8ae86Sav for (bank = 0; bank < BANKNUM_PER_SB; bank++) { 1302*0cc8ae86Sav while ((p = mcp->mc_scf_log[bank]) != NULL && 1303*0cc8ae86Sav (n < mc_max_errlog_processed)) { 1304*0cc8ae86Sav ASSERT(bank == p->sl_bank); 1305*0cc8ae86Sav count = 0; 130625cf1a30Sjl while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank)) 130725cf1a30Sjl & MAC_STATIC_ERR_VLD)) { 1308*0cc8ae86Sav if (count++ >= (mc_max_scf_loop)) { 130925cf1a30Sjl break; 131025cf1a30Sjl } 1311*0cc8ae86Sav drv_usecwait(mc_scf_delay); 131225cf1a30Sjl } 131325cf1a30Sjl 1314*0cc8ae86Sav if (count < mc_max_scf_loop) { 131525cf1a30Sjl ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank), 131625cf1a30Sjl p->sl_err_log); 131725cf1a30Sjl 131825cf1a30Sjl ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank), 131925cf1a30Sjl p->sl_err_add|MAC_STATIC_ERR_VLD); 132025cf1a30Sjl mcp->mc_scf_retry[bank] = 0; 132125cf1a30Sjl } else { 132225cf1a30Sjl /* if we try too many times, just drop the req */ 1323*0cc8ae86Sav if (mcp->mc_scf_retry[bank]++ <= mc_max_scf_retry) { 132425cf1a30Sjl return; 132525cf1a30Sjl } else { 1326*0cc8ae86Sav if ((++mc_pce_dropped & 0xff) == 0) { 1327*0cc8ae86Sav cmn_err(CE_WARN, 1328*0cc8ae86Sav "Cannot report Permanent CE to SCF\n"); 1329*0cc8ae86Sav } 133025cf1a30Sjl } 133125cf1a30Sjl } 1332*0cc8ae86Sav n++; 1333*0cc8ae86Sav mcp->mc_scf_log[bank] = p->sl_next; 1334*0cc8ae86Sav mcp->mc_scf_total[bank]--; 1335*0cc8ae86Sav ASSERT(mcp->mc_scf_total[bank] >= 0); 133625cf1a30Sjl kmem_free(p, sizeof (scf_log_t)); 1337*0cc8ae86Sav } 133825cf1a30Sjl } 133925cf1a30Sjl } 134025cf1a30Sjl void 134125cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank) 134225cf1a30Sjl { 134325cf1a30Sjl scf_log_t *p; 134425cf1a30Sjl 1345*0cc8ae86Sav if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) { 1346*0cc8ae86Sav if ((++mc_pce_dropped & 0xff) == 0) { 1347*0cc8ae86Sav cmn_err(CE_WARN, "Too many Permanent CE requests.\n"); 1348*0cc8ae86Sav } 134925cf1a30Sjl return; 135025cf1a30Sjl } 135125cf1a30Sjl p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP); 135225cf1a30Sjl p->sl_next = 0; 135325cf1a30Sjl p->sl_err_add = flt_stat->mf_err_add; 135425cf1a30Sjl p->sl_err_log = flt_stat->mf_err_log; 135525cf1a30Sjl p->sl_bank = bank; 135625cf1a30Sjl 1357*0cc8ae86Sav if (mcp->mc_scf_log[bank] == NULL) { 135825cf1a30Sjl /* 135925cf1a30Sjl * we rely on mc_scf_log to detect NULL queue. 136025cf1a30Sjl * mc_scf_log_tail is irrelevant is such case. 136125cf1a30Sjl */ 1362*0cc8ae86Sav mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p; 136325cf1a30Sjl } else { 1364*0cc8ae86Sav mcp->mc_scf_log_tail[bank]->sl_next = p; 1365*0cc8ae86Sav mcp->mc_scf_log_tail[bank] = p; 136625cf1a30Sjl } 1367*0cc8ae86Sav mcp->mc_scf_total[bank]++; 136825cf1a30Sjl } 136925cf1a30Sjl /* 137025cf1a30Sjl * This routine determines what kind of CE happens, intermittent 137125cf1a30Sjl * or permanent as follows. (See 4.7.3 in Columbus2 PRM.) 137225cf1a30Sjl * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register. 137325cf1a30Sjl * - If CE is still detected on the same address even after doing 137425cf1a30Sjl * rewrite operation twice, it is determined as permanent error. 137525cf1a30Sjl * - If error is not detected anymore, it is determined as intermittent 137625cf1a30Sjl * error. 137725cf1a30Sjl * - If UE is detected due to rewrite operation, it should be treated 137825cf1a30Sjl * as UE. 137925cf1a30Sjl */ 138025cf1a30Sjl 138125cf1a30Sjl /* ARGSUSED */ 138225cf1a30Sjl static void 138325cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error) 138425cf1a30Sjl { 138525cf1a30Sjl uint32_t cntl; 138625cf1a30Sjl int i; 138725cf1a30Sjl 138825cf1a30Sjl flt_stat->mf_type = FLT_TYPE_PERMANENT_CE; 138925cf1a30Sjl /* 139025cf1a30Sjl * rewrite request 1st time reads and correct error data 139125cf1a30Sjl * and write to DIMM. 2nd rewrite request must be issued 139225cf1a30Sjl * after REW_CE/UE/END is 0. When the 2nd request is completed, 139325cf1a30Sjl * if REW_CE = 1, then it is permanent CE. 139425cf1a30Sjl */ 139525cf1a30Sjl for (i = 0; i < 2; i++) { 139625cf1a30Sjl cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add); 139725cf1a30Sjl /* 139825cf1a30Sjl * If the error becomes UE or CMPE 139925cf1a30Sjl * we return to the caller immediately. 140025cf1a30Sjl */ 140125cf1a30Sjl if (cntl & MAC_CNTL_REW_UE) { 140225cf1a30Sjl if (ptrl_error) 140325cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE; 140425cf1a30Sjl else 140525cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_UE; 140625cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 140725cf1a30Sjl return; 140825cf1a30Sjl } 140925cf1a30Sjl if (cntl & MAC_CNTL_REW_CMPE) { 141025cf1a30Sjl if (ptrl_error) 141125cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE; 141225cf1a30Sjl else 141325cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE; 141425cf1a30Sjl flt_stat->mf_type = FLT_TYPE_CMPE; 141525cf1a30Sjl return; 141625cf1a30Sjl } 141725cf1a30Sjl } 141825cf1a30Sjl if (!(cntl & MAC_CNTL_REW_CE)) { 141925cf1a30Sjl flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE; 142025cf1a30Sjl } 142125cf1a30Sjl 142225cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 142325cf1a30Sjl /* report PERMANENT_CE to SP via SCF */ 142425cf1a30Sjl if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) { 142525cf1a30Sjl mc_queue_scf_log(mcp, flt_stat, bank); 142625cf1a30Sjl } 142725cf1a30Sjl } 142825cf1a30Sjl } 142925cf1a30Sjl 143025cf1a30Sjl #define IS_CMPE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\ 143125cf1a30Sjl MAC_CNTL_MI_CMPE)) 143225cf1a30Sjl #define IS_UE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE)) 143325cf1a30Sjl #define IS_CE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE)) 143425cf1a30Sjl #define IS_OK(cntl, f) (!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \ 143525cf1a30Sjl MAC_CNTL_MI_ERRS))) 143625cf1a30Sjl 143725cf1a30Sjl 143825cf1a30Sjl static int 143925cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error) 144025cf1a30Sjl { 144125cf1a30Sjl if (ptrl_error) { 144225cf1a30Sjl return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE); 144325cf1a30Sjl } else { 144425cf1a30Sjl return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE); 144525cf1a30Sjl } 144625cf1a30Sjl } 144725cf1a30Sjl 144825cf1a30Sjl void 144925cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value) 145025cf1a30Sjl { 1451*0cc8ae86Sav if (mcp->mc_speedup_period[bank] > 0) 1452*0cc8ae86Sav value |= mc_max_speed; 1453*0cc8ae86Sav else 1454*0cc8ae86Sav value |= mcp->mc_speed; 145525cf1a30Sjl ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value); 145625cf1a30Sjl } 145725cf1a30Sjl 145825cf1a30Sjl static void 145925cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 146025cf1a30Sjl { 146125cf1a30Sjl flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 146225cf1a30Sjl MAC_CNTL_PTRL_ERRS; 146325cf1a30Sjl flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank)); 146425cf1a30Sjl flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank)); 146525cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 146625cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 146725cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 146825cf1a30Sjl } 146925cf1a30Sjl 147025cf1a30Sjl static void 147125cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 147225cf1a30Sjl { 147325cf1a30Sjl uint32_t status, old_status; 147425cf1a30Sjl 147525cf1a30Sjl status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 147625cf1a30Sjl MAC_CNTL_MI_ERRS; 147725cf1a30Sjl old_status = 0; 147825cf1a30Sjl 147925cf1a30Sjl /* we keep reading until the status is stable */ 148025cf1a30Sjl while (old_status != status) { 148125cf1a30Sjl old_status = status; 148225cf1a30Sjl flt_stat->mf_err_add = 148325cf1a30Sjl LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank)); 148425cf1a30Sjl flt_stat->mf_err_log = 148525cf1a30Sjl LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank)); 148625cf1a30Sjl status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 148725cf1a30Sjl MAC_CNTL_MI_ERRS; 148825cf1a30Sjl if (status == old_status) { 148925cf1a30Sjl break; 149025cf1a30Sjl } 149125cf1a30Sjl } 149225cf1a30Sjl 149325cf1a30Sjl flt_stat->mf_cntl = status; 149425cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 149525cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 149625cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 149725cf1a30Sjl } 149825cf1a30Sjl 149925cf1a30Sjl 150025cf1a30Sjl /* 150125cf1a30Sjl * Error philosophy for mirror mode: 150225cf1a30Sjl * 150325cf1a30Sjl * PTRL (The error address for both banks are same, since ptrl stops if it 150425cf1a30Sjl * detects error.) 150525cf1a30Sjl * - Compaire error Report CMPE. 150625cf1a30Sjl * 150725cf1a30Sjl * - UE-UE Report MUE. No rewrite. 150825cf1a30Sjl * 150925cf1a30Sjl * - UE-* UE-(CE/OK). Rewrite to scrub UE. Report SUE. 151025cf1a30Sjl * 151125cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 151225cf1a30Sjl * If CE is permanent, inform SCF. Once for each 151325cf1a30Sjl * Dimm. If CE becomes UE or CMPE, go back to above. 151425cf1a30Sjl * 151525cf1a30Sjl * 151625cf1a30Sjl * MI (The error addresses for each bank are the same or different.) 151725cf1a30Sjl * - Compair error If addresses are the same. Just CMPE. 151825cf1a30Sjl * If addresses are different (this could happen 151925cf1a30Sjl * as a result of scrubbing. Report each seperately. 152025cf1a30Sjl * Only report error info on each side. 152125cf1a30Sjl * 152225cf1a30Sjl * - UE-UE Addresses are the same. Report MUE. 152325cf1a30Sjl * Addresses are different. Report SUE on each bank. 152425cf1a30Sjl * Rewrite to clear UE. 152525cf1a30Sjl * 152625cf1a30Sjl * - UE-* UE-(CE/OK) 152725cf1a30Sjl * Rewrite to clear UE. Report SUE for the bank. 152825cf1a30Sjl * 152925cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 153025cf1a30Sjl * If CE becomes UE or CMPE, go back to above. 153125cf1a30Sjl * 153225cf1a30Sjl */ 153325cf1a30Sjl 153425cf1a30Sjl static int 153525cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat) 153625cf1a30Sjl { 153725cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 153825cf1a30Sjl int i; 153925cf1a30Sjl int rv = 0; 154025cf1a30Sjl 154125cf1a30Sjl MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n", 154225cf1a30Sjl flt_stat[0].mf_cntl, flt_stat[1].mf_cntl); 154325cf1a30Sjl 154425cf1a30Sjl if (ptrl_error) { 154525cf1a30Sjl if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) 154625cf1a30Sjl & MAC_CNTL_PTRL_ERRS) == 0) 154725cf1a30Sjl return (0); 154825cf1a30Sjl } else { 154925cf1a30Sjl if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) 155025cf1a30Sjl & MAC_CNTL_MI_ERRS) == 0) 155125cf1a30Sjl return (0); 155225cf1a30Sjl } 155325cf1a30Sjl 155425cf1a30Sjl /* 155525cf1a30Sjl * First we take care of the case of CE 155625cf1a30Sjl * because they can become UE or CMPE 155725cf1a30Sjl */ 155825cf1a30Sjl for (i = 0; i < 2; i++) { 155925cf1a30Sjl if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) { 156025cf1a30Sjl MC_LOG("CE detected on bank %d\n", 156125cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank); 156225cf1a30Sjl mc_scrub_ce(mcp, flt_stat[i].mf_flt_maddr.ma_bank, 156325cf1a30Sjl &flt_stat[i], ptrl_error); 156425cf1a30Sjl rv = 1; 156525cf1a30Sjl } 156625cf1a30Sjl } 156725cf1a30Sjl 156825cf1a30Sjl /* The above scrubbing can turn CE into UE or CMPE */ 156925cf1a30Sjl 157025cf1a30Sjl /* 157125cf1a30Sjl * Now we distinguish two cases: same address or not 157225cf1a30Sjl * the same address. It might seem more intuitive to 157325cf1a30Sjl * distinguish PTRL v.s. MI error but it is more 157425cf1a30Sjl * complicated that way. 157525cf1a30Sjl */ 157625cf1a30Sjl 157725cf1a30Sjl if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) { 157825cf1a30Sjl 157925cf1a30Sjl if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) || 158025cf1a30Sjl IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) { 158125cf1a30Sjl flt_stat[0].mf_type = FLT_TYPE_CMPE; 158225cf1a30Sjl flt_stat[1].mf_type = FLT_TYPE_CMPE; 158325cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 158425cf1a30Sjl MC_LOG("cmpe error detected\n"); 158525cf1a30Sjl mc_aflt->mflt_nflts = 2; 158625cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 158725cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 158825cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 158925cf1a30Sjl mc_err_drain(mc_aflt); 159025cf1a30Sjl return (1); 159125cf1a30Sjl } 159225cf1a30Sjl 159325cf1a30Sjl if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) && 159425cf1a30Sjl IS_UE(flt_stat[1].mf_cntl, ptrl_error)) { 159525cf1a30Sjl /* Both side are UE's */ 159625cf1a30Sjl 159725cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[0]); 159825cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[1]); 159925cf1a30Sjl MC_LOG("MUE detected\n"); 1600*0cc8ae86Sav flt_stat[0].mf_type = FLT_TYPE_MUE; 1601*0cc8ae86Sav flt_stat[1].mf_type = FLT_TYPE_MUE; 160225cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_MUE; 160325cf1a30Sjl mc_aflt->mflt_nflts = 2; 160425cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 160525cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 160625cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 160725cf1a30Sjl mc_err_drain(mc_aflt); 160825cf1a30Sjl return (1); 160925cf1a30Sjl } 161025cf1a30Sjl 161125cf1a30Sjl /* Now the only case is UE/CE, UE/OK, or don't care */ 161225cf1a30Sjl for (i = 0; i < 2; i++) { 161325cf1a30Sjl if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 1614*0cc8ae86Sav 1615*0cc8ae86Sav /* rewrite can clear the one side UE error */ 1616*0cc8ae86Sav 161725cf1a30Sjl if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) { 161825cf1a30Sjl (void) do_rewrite(mcp, 161925cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank, 162025cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_dimm_addr); 162125cf1a30Sjl } 162225cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_UE; 162325cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[i]); 162425cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_SUE; 162525cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 162625cf1a30Sjl mc_aflt->mflt_nflts = 1; 162725cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 162825cf1a30Sjl mc_err_drain(mc_aflt); 162925cf1a30Sjl /* Once we hit a UE/CE or UE/OK case, done */ 163025cf1a30Sjl return (1); 163125cf1a30Sjl } 163225cf1a30Sjl } 163325cf1a30Sjl 163425cf1a30Sjl } else { 163525cf1a30Sjl /* 163625cf1a30Sjl * addresses are different. That means errors 163725cf1a30Sjl * on the 2 banks are not related at all. 163825cf1a30Sjl */ 163925cf1a30Sjl for (i = 0; i < 2; i++) { 164025cf1a30Sjl if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) { 164125cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_CMPE; 164225cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 164325cf1a30Sjl MC_LOG("cmpe error detected\n"); 164425cf1a30Sjl mc_aflt->mflt_nflts = 1; 164525cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 164625cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 164725cf1a30Sjl mc_err_drain(mc_aflt); 164825cf1a30Sjl /* no more report on this bank */ 164925cf1a30Sjl flt_stat[i].mf_cntl = 0; 165025cf1a30Sjl rv = 1; 165125cf1a30Sjl } 165225cf1a30Sjl } 165325cf1a30Sjl 1654*0cc8ae86Sav /* rewrite can clear the one side UE error */ 1655*0cc8ae86Sav 165625cf1a30Sjl for (i = 0; i < 2; i++) { 165725cf1a30Sjl if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 165825cf1a30Sjl (void) do_rewrite(mcp, 165925cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank, 166025cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_dimm_addr); 166125cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_UE; 166225cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[i]); 166325cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_SUE; 166425cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 166525cf1a30Sjl mc_aflt->mflt_nflts = 1; 166625cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 166725cf1a30Sjl mc_err_drain(mc_aflt); 166825cf1a30Sjl rv = 1; 166925cf1a30Sjl } 167025cf1a30Sjl } 167125cf1a30Sjl } 167225cf1a30Sjl return (rv); 167325cf1a30Sjl } 167425cf1a30Sjl static void 167525cf1a30Sjl mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr) 167625cf1a30Sjl { 167725cf1a30Sjl mc_aflt_t mc_aflt; 167825cf1a30Sjl mc_flt_stat_t flt_stat[2], mi_flt_stat[2]; 1679*0cc8ae86Sav int i; 1680*0cc8ae86Sav int mi_valid; 168125cf1a30Sjl 168225cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 168325cf1a30Sjl bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t)); 168425cf1a30Sjl bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t)); 168525cf1a30Sjl 168625cf1a30Sjl mc_aflt.mflt_mcp = mcp; 168725cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 168825cf1a30Sjl 168925cf1a30Sjl /* Now read all the registers into flt_stat */ 169025cf1a30Sjl 1691*0cc8ae86Sav for (i = 0; i < 2; i++) { 1692*0cc8ae86Sav MC_LOG("Reading registers of bank %d\n", bank); 1693*0cc8ae86Sav /* patrol registers */ 1694*0cc8ae86Sav mc_read_ptrl_reg(mcp, bank, &flt_stat[i]); 169525cf1a30Sjl 1696*0cc8ae86Sav ASSERT(maddr); 1697*0cc8ae86Sav maddr->mi_maddr = flt_stat[i].mf_flt_maddr; 169825cf1a30Sjl 1699*0cc8ae86Sav MC_LOG("ptrl registers cntl %x add %x log %x\n", 1700*0cc8ae86Sav flt_stat[i].mf_cntl, 1701*0cc8ae86Sav flt_stat[i].mf_err_add, 1702*0cc8ae86Sav flt_stat[i].mf_err_log); 170325cf1a30Sjl 1704*0cc8ae86Sav /* MI registers */ 1705*0cc8ae86Sav mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]); 170625cf1a30Sjl 1707*0cc8ae86Sav MC_LOG("MI registers cntl %x add %x log %x\n", 1708*0cc8ae86Sav mi_flt_stat[i].mf_cntl, 1709*0cc8ae86Sav mi_flt_stat[i].mf_err_add, 1710*0cc8ae86Sav mi_flt_stat[i].mf_err_log); 171125cf1a30Sjl 1712*0cc8ae86Sav bank = bank^1; 1713*0cc8ae86Sav } 171425cf1a30Sjl 171525cf1a30Sjl /* clear errors once we read all the registers */ 1716*0cc8ae86Sav MAC_CLEAR_ERRS(mcp, bank, 171725cf1a30Sjl (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 171825cf1a30Sjl 1719*0cc8ae86Sav MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 1720*0cc8ae86Sav 1721*0cc8ae86Sav /* Process MI errors first */ 172225cf1a30Sjl 1723*0cc8ae86Sav /* if not error mode, cntl1 is 0 */ 1724*0cc8ae86Sav if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 1725*0cc8ae86Sav (mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 1726*0cc8ae86Sav mi_flt_stat[0].mf_cntl = 0; 1727*0cc8ae86Sav 1728*0cc8ae86Sav if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 1729*0cc8ae86Sav (mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 1730*0cc8ae86Sav mi_flt_stat[1].mf_cntl = 0; 173125cf1a30Sjl 1732*0cc8ae86Sav mc_aflt.mflt_is_ptrl = 0; 1733*0cc8ae86Sav mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]); 1734*0cc8ae86Sav 1735*0cc8ae86Sav if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 1736*0cc8ae86Sav MAC_CNTL_PTRL_ERR_SHIFT) == 1737*0cc8ae86Sav ((mi_flt_stat[0].mf_cntl & MAC_CNTL_MI_ERRS) >> 1738*0cc8ae86Sav MAC_CNTL_MI_ERR_SHIFT)) && 1739*0cc8ae86Sav (flt_stat[0].mf_err_add == mi_flt_stat[0].mf_err_add) && 1740*0cc8ae86Sav (((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 1741*0cc8ae86Sav MAC_CNTL_PTRL_ERR_SHIFT) == 1742*0cc8ae86Sav ((mi_flt_stat[1].mf_cntl & MAC_CNTL_MI_ERRS) >> 1743*0cc8ae86Sav MAC_CNTL_MI_ERR_SHIFT)) && 1744*0cc8ae86Sav (flt_stat[1].mf_err_add == mi_flt_stat[1].mf_err_add)) { 1745*0cc8ae86Sav #ifdef DEBUG 1746*0cc8ae86Sav MC_LOG("discarding PTRL error because " 1747*0cc8ae86Sav "it is the same as MI\n"); 1748*0cc8ae86Sav #endif 1749*0cc8ae86Sav maddr->mi_valid = mi_valid; 1750*0cc8ae86Sav return; 1751*0cc8ae86Sav } 175225cf1a30Sjl /* if not error mode, cntl1 is 0 */ 175325cf1a30Sjl if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 175425cf1a30Sjl (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 175525cf1a30Sjl flt_stat[0].mf_cntl = 0; 175625cf1a30Sjl 175725cf1a30Sjl if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 175825cf1a30Sjl (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 175925cf1a30Sjl flt_stat[1].mf_cntl = 0; 176025cf1a30Sjl 176125cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 176225cf1a30Sjl maddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]); 176325cf1a30Sjl } 176425cf1a30Sjl static int 176525cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt, 176625cf1a30Sjl mc_flt_stat_t *flt_stat) 176725cf1a30Sjl { 176825cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 176925cf1a30Sjl int rv = 0; 177025cf1a30Sjl 177125cf1a30Sjl mc_aflt->mflt_erpt_class = NULL; 177225cf1a30Sjl if (IS_UE(flt_stat->mf_cntl, ptrl_error)) { 177325cf1a30Sjl MC_LOG("UE deteceted\n"); 177425cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 177525cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 177625cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 177725cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 177825cf1a30Sjl rv = 1; 177925cf1a30Sjl } else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) { 178025cf1a30Sjl MC_LOG("CE deteceted\n"); 178125cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 178225cf1a30Sjl 178325cf1a30Sjl /* Error type can change after scrubing */ 178425cf1a30Sjl mc_scrub_ce(mcp, bank, flt_stat, ptrl_error); 178525cf1a30Sjl 178625cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 178725cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CE; 178825cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 178925cf1a30Sjl } else if (flt_stat->mf_type == FLT_TYPE_UE) { 179025cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 179125cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 179225cf1a30Sjl } 179325cf1a30Sjl rv = 1; 179425cf1a30Sjl } 179525cf1a30Sjl MC_LOG("mc_process_error: fault type %x erpt %s\n", 179625cf1a30Sjl flt_stat->mf_type, 179725cf1a30Sjl mc_aflt->mflt_erpt_class); 179825cf1a30Sjl if (mc_aflt->mflt_erpt_class) { 179925cf1a30Sjl mc_aflt->mflt_stat[0] = flt_stat; 180025cf1a30Sjl mc_aflt->mflt_nflts = 1; 180125cf1a30Sjl mc_err_drain(mc_aflt); 180225cf1a30Sjl } 180325cf1a30Sjl return (rv); 180425cf1a30Sjl } 180525cf1a30Sjl 180625cf1a30Sjl static void 180725cf1a30Sjl mc_error_handler(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr) 180825cf1a30Sjl { 180925cf1a30Sjl mc_aflt_t mc_aflt; 181025cf1a30Sjl mc_flt_stat_t flt_stat, mi_flt_stat; 1811*0cc8ae86Sav int mi_valid; 181225cf1a30Sjl 181325cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 181425cf1a30Sjl bzero(&flt_stat, sizeof (mc_flt_stat_t)); 181525cf1a30Sjl bzero(&mi_flt_stat, sizeof (mc_flt_stat_t)); 181625cf1a30Sjl 181725cf1a30Sjl mc_aflt.mflt_mcp = mcp; 181825cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 181925cf1a30Sjl 182025cf1a30Sjl /* patrol registers */ 182125cf1a30Sjl mc_read_ptrl_reg(mcp, bank, &flt_stat); 182225cf1a30Sjl 182325cf1a30Sjl ASSERT(maddr); 182425cf1a30Sjl maddr->mi_maddr = flt_stat.mf_flt_maddr; 182525cf1a30Sjl 182625cf1a30Sjl MC_LOG("ptrl registers cntl %x add %x log %x\n", 182725cf1a30Sjl flt_stat.mf_cntl, 182825cf1a30Sjl flt_stat.mf_err_add, 182925cf1a30Sjl flt_stat.mf_err_log); 183025cf1a30Sjl 183125cf1a30Sjl /* MI registers */ 183225cf1a30Sjl mc_read_mi_reg(mcp, bank, &mi_flt_stat); 183325cf1a30Sjl 1834*0cc8ae86Sav 183525cf1a30Sjl MC_LOG("MI registers cntl %x add %x log %x\n", 183625cf1a30Sjl mi_flt_stat.mf_cntl, 183725cf1a30Sjl mi_flt_stat.mf_err_add, 183825cf1a30Sjl mi_flt_stat.mf_err_log); 183925cf1a30Sjl 184025cf1a30Sjl /* clear errors once we read all the registers */ 184125cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 184225cf1a30Sjl 1843*0cc8ae86Sav mc_aflt.mflt_is_ptrl = 0; 1844*0cc8ae86Sav if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) && 1845*0cc8ae86Sav ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 1846*0cc8ae86Sav ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 1847*0cc8ae86Sav mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat); 1848*0cc8ae86Sav } 1849*0cc8ae86Sav 1850*0cc8ae86Sav if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >> 1851*0cc8ae86Sav MAC_CNTL_PTRL_ERR_SHIFT) == 1852*0cc8ae86Sav ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) >> 1853*0cc8ae86Sav MAC_CNTL_MI_ERR_SHIFT)) && 1854*0cc8ae86Sav (flt_stat.mf_err_add == mi_flt_stat.mf_err_add)) { 1855*0cc8ae86Sav #ifdef DEBUG 1856*0cc8ae86Sav MC_LOG("discarding PTRL error because " 1857*0cc8ae86Sav "it is the same as MI\n"); 1858*0cc8ae86Sav #endif 1859*0cc8ae86Sav maddr->mi_valid = mi_valid; 1860*0cc8ae86Sav return; 1861*0cc8ae86Sav } 1862*0cc8ae86Sav 186325cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 186425cf1a30Sjl if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) && 186525cf1a30Sjl ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 186625cf1a30Sjl ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 186725cf1a30Sjl maddr->mi_valid = mc_process_error(mcp, bank, 186825cf1a30Sjl &mc_aflt, &flt_stat); 186925cf1a30Sjl } 187025cf1a30Sjl } 187125cf1a30Sjl /* 187225cf1a30Sjl * memory patrol error handling algorithm: 187325cf1a30Sjl * timeout() is used to do periodic polling 187425cf1a30Sjl * This is the flow chart. 187525cf1a30Sjl * timeout -> 187625cf1a30Sjl * mc_check_errors() 187725cf1a30Sjl * if memory bank is installed, read the status register 187825cf1a30Sjl * if any error bit is set, 187925cf1a30Sjl * -> mc_error_handler() 188025cf1a30Sjl * -> read all error regsiters 188125cf1a30Sjl * -> mc_process_error() 188225cf1a30Sjl * determine error type 188325cf1a30Sjl * rewrite to clear error or scrub to determine CE type 188425cf1a30Sjl * inform SCF on permanent CE 188525cf1a30Sjl * -> mc_err_drain 188625cf1a30Sjl * page offline processing 188725cf1a30Sjl * -> mc_ereport_post() 188825cf1a30Sjl */ 188925cf1a30Sjl 189025cf1a30Sjl static void 189125cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp) 189225cf1a30Sjl { 189325cf1a30Sjl mc_addr_info_t maddr_info; 189425cf1a30Sjl int i, error_count = 0; 189525cf1a30Sjl uint32_t stat, cntl; 1896*0cc8ae86Sav int running; 189725cf1a30Sjl 189825cf1a30Sjl /* 189925cf1a30Sjl * scan errors. 190025cf1a30Sjl */ 1901*0cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) 1902*0cc8ae86Sav return; 1903*0cc8ae86Sav 190425cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 190525cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 190625cf1a30Sjl stat = ldphysio(MAC_PTRL_STAT(mcp, i)); 190725cf1a30Sjl cntl = ldphysio(MAC_PTRL_CNTL(mcp, i)); 1908*0cc8ae86Sav running = cntl & MAC_CNTL_PTRL_START; 1909*0cc8ae86Sav 191025cf1a30Sjl if (cntl & MAC_CNTL_PTRL_ADD_MAX) { 1911*0cc8ae86Sav mcp->mc_period[i]++; 191225cf1a30Sjl MC_LOG("mc period %ld on " 1913*0cc8ae86Sav "/LSB%d/B%d\n", mcp->mc_period[i], 191425cf1a30Sjl mcp->mc_board_num, i); 191525cf1a30Sjl MAC_CLEAR_MAX(mcp, i); 1916*0cc8ae86Sav if (mcp->mc_speedup_period[i] > 0) { 1917*0cc8ae86Sav /* If patrol is stoppped, we fall through */ 1918*0cc8ae86Sav if (--mcp->mc_speedup_period[i] == 0 && 1919*0cc8ae86Sav running) { 1920*0cc8ae86Sav MAC_CMD(mcp, i, 0); 1921*0cc8ae86Sav } 1922*0cc8ae86Sav } 192325cf1a30Sjl } 192425cf1a30Sjl if (mc_debug_show_all) { 192525cf1a30Sjl MC_LOG("/LSB%d/B%d stat %x cntl %x\n", 192625cf1a30Sjl mcp->mc_board_num, i, 192725cf1a30Sjl stat, cntl); 192825cf1a30Sjl } 192925cf1a30Sjl if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) { 1930*0cc8ae86Sav if (running) { 1931*0cc8ae86Sav MC_LOG("patrol running /LSB%d/B%d\n", 1932*0cc8ae86Sav mcp->mc_board_num, i); 1933*0cc8ae86Sav } 1934*0cc8ae86Sav if (running) { 1935*0cc8ae86Sav /* speed up the scanning */ 1936*0cc8ae86Sav mcp->mc_speedup_period[i] = 2; 1937*0cc8ae86Sav MAC_CMD(mcp, i, 0); 1938*0cc8ae86Sav } else { 1939*0cc8ae86Sav mcp->mc_speedup_period[i] = 0; 1940*0cc8ae86Sav maddr_info.mi_valid = 0; 1941*0cc8ae86Sav maddr_info.mi_advance = 1; 1942*0cc8ae86Sav if (IS_MIRROR(mcp, i)) 194325cf1a30Sjl mc_error_handler_mir(mcp, i, 194425cf1a30Sjl &maddr_info); 1945*0cc8ae86Sav else 194625cf1a30Sjl mc_error_handler(mcp, i, &maddr_info); 194725cf1a30Sjl 1948*0cc8ae86Sav error_count++; 1949*0cc8ae86Sav restart_patrol(mcp, i, &maddr_info); 1950*0cc8ae86Sav } 195125cf1a30Sjl } else { 195225cf1a30Sjl restart_patrol(mcp, i, NULL); 195325cf1a30Sjl } 195425cf1a30Sjl } 195525cf1a30Sjl } 195625cf1a30Sjl if (error_count > 0) 195725cf1a30Sjl mcp->mc_last_error += error_count; 195825cf1a30Sjl else 195925cf1a30Sjl mcp->mc_last_error = 0; 196025cf1a30Sjl } 196125cf1a30Sjl 1962*0cc8ae86Sav /* 1963*0cc8ae86Sav * mc_polling -- Check errors for only one instance, 1964*0cc8ae86Sav * but process errors for all instances to make sure we drain the errors 1965*0cc8ae86Sav * faster than they can be accumulated. 1966*0cc8ae86Sav * 1967*0cc8ae86Sav * Polling on each board should be done only once per each 1968*0cc8ae86Sav * mc_patrol_interval_sec. This is equivalent to setting mc_tick_left 1969*0cc8ae86Sav * to OPL_MAX_BOARDS and decrement by 1 on each timeout. 1970*0cc8ae86Sav * Once mc_tick_left becomes negative, the board becomes a candidate 1971*0cc8ae86Sav * for polling because it has waited for at least 1972*0cc8ae86Sav * mc_patrol_interval_sec's long. If mc_timeout_period is calculated 1973*0cc8ae86Sav * differently, this has to beupdated accordingly. 1974*0cc8ae86Sav */ 197525cf1a30Sjl 197625cf1a30Sjl static void 1977*0cc8ae86Sav mc_polling(void) 197825cf1a30Sjl { 1979*0cc8ae86Sav int i, scan_error; 1980*0cc8ae86Sav mc_opl_t *mcp; 198125cf1a30Sjl 198225cf1a30Sjl 1983*0cc8ae86Sav scan_error = 1; 1984*0cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 1985*0cc8ae86Sav mutex_enter(&mcmutex); 1986*0cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) { 1987*0cc8ae86Sav mutex_exit(&mcmutex); 1988*0cc8ae86Sav continue; 1989*0cc8ae86Sav } 1990*0cc8ae86Sav mutex_enter(&mcp->mc_lock); 1991*0cc8ae86Sav mutex_exit(&mcmutex); 1992*0cc8ae86Sav if (scan_error && mcp->mc_tick_left <= 0) { 1993*0cc8ae86Sav mc_check_errors_func((void *)mcp); 1994*0cc8ae86Sav mcp->mc_tick_left = OPL_MAX_BOARDS; 1995*0cc8ae86Sav scan_error = 0; 1996*0cc8ae86Sav } else { 1997*0cc8ae86Sav mcp->mc_tick_left--; 1998*0cc8ae86Sav } 1999*0cc8ae86Sav mc_process_scf_log(mcp); 2000*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 200125cf1a30Sjl } 200225cf1a30Sjl } 200325cf1a30Sjl 200425cf1a30Sjl static void 200525cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr) 200625cf1a30Sjl { 200725cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 200825cf1a30Sjl maddr->ma_bank = bank; 200925cf1a30Sjl maddr->ma_dimm_addr = 0; 201025cf1a30Sjl } 201125cf1a30Sjl 201225cf1a30Sjl typedef struct mc_mem_range { 201325cf1a30Sjl uint64_t addr; 201425cf1a30Sjl uint64_t size; 201525cf1a30Sjl } mc_mem_range_t; 201625cf1a30Sjl 201725cf1a30Sjl static int 201825cf1a30Sjl get_base_address(mc_opl_t *mcp) 201925cf1a30Sjl { 202025cf1a30Sjl mc_mem_range_t *mem_range; 202125cf1a30Sjl int len; 202225cf1a30Sjl 202325cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 202425cf1a30Sjl "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) { 202525cf1a30Sjl return (DDI_FAILURE); 202625cf1a30Sjl } 202725cf1a30Sjl 202825cf1a30Sjl mcp->mc_start_address = mem_range->addr; 202925cf1a30Sjl mcp->mc_size = mem_range->size; 203025cf1a30Sjl 203125cf1a30Sjl kmem_free(mem_range, len); 203225cf1a30Sjl return (DDI_SUCCESS); 203325cf1a30Sjl } 203425cf1a30Sjl 203525cf1a30Sjl struct mc_addr_spec { 203625cf1a30Sjl uint32_t bank; 203725cf1a30Sjl uint32_t phys_hi; 203825cf1a30Sjl uint32_t phys_lo; 203925cf1a30Sjl }; 204025cf1a30Sjl 204125cf1a30Sjl #define REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo) 204225cf1a30Sjl 204325cf1a30Sjl static char *mc_tbl_name[] = { 204425cf1a30Sjl "cs0-mc-pa-trans-table", 204525cf1a30Sjl "cs1-mc-pa-trans-table" 204625cf1a30Sjl }; 204725cf1a30Sjl 204825cf1a30Sjl static int 204925cf1a30Sjl mc_valid_pa(mc_opl_t *mcp, uint64_t pa) 205025cf1a30Sjl { 205125cf1a30Sjl struct memlist *ml; 205225cf1a30Sjl 205325cf1a30Sjl if (mcp->mlist == NULL) 205425cf1a30Sjl mc_get_mlist(mcp); 205525cf1a30Sjl 205625cf1a30Sjl for (ml = mcp->mlist; ml; ml = ml->next) { 205725cf1a30Sjl if (ml->address <= pa && pa < (ml->address + ml->size)) 205825cf1a30Sjl return (1); 205925cf1a30Sjl } 206025cf1a30Sjl return (0); 206125cf1a30Sjl } 206225cf1a30Sjl 206325cf1a30Sjl static void 206425cf1a30Sjl mc_memlist_delete(struct memlist *mlist) 206525cf1a30Sjl { 206625cf1a30Sjl struct memlist *ml; 206725cf1a30Sjl 206825cf1a30Sjl for (ml = mlist; ml; ml = mlist) { 206925cf1a30Sjl mlist = ml->next; 207025cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 207125cf1a30Sjl } 207225cf1a30Sjl } 207325cf1a30Sjl 207425cf1a30Sjl static struct memlist * 207525cf1a30Sjl mc_memlist_dup(struct memlist *mlist) 207625cf1a30Sjl { 207725cf1a30Sjl struct memlist *hl = NULL, *tl, **mlp; 207825cf1a30Sjl 207925cf1a30Sjl if (mlist == NULL) 208025cf1a30Sjl return (NULL); 208125cf1a30Sjl 208225cf1a30Sjl mlp = &hl; 208325cf1a30Sjl tl = *mlp; 208425cf1a30Sjl for (; mlist; mlist = mlist->next) { 208525cf1a30Sjl *mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP); 208625cf1a30Sjl (*mlp)->address = mlist->address; 208725cf1a30Sjl (*mlp)->size = mlist->size; 208825cf1a30Sjl (*mlp)->prev = tl; 208925cf1a30Sjl tl = *mlp; 209025cf1a30Sjl mlp = &((*mlp)->next); 209125cf1a30Sjl } 209225cf1a30Sjl *mlp = NULL; 209325cf1a30Sjl 209425cf1a30Sjl return (hl); 209525cf1a30Sjl } 209625cf1a30Sjl 209725cf1a30Sjl 209825cf1a30Sjl static struct memlist * 209925cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len) 210025cf1a30Sjl { 210125cf1a30Sjl uint64_t end; 210225cf1a30Sjl struct memlist *ml, *tl, *nlp; 210325cf1a30Sjl 210425cf1a30Sjl if (mlist == NULL) 210525cf1a30Sjl return (NULL); 210625cf1a30Sjl 210725cf1a30Sjl end = base + len; 210825cf1a30Sjl if ((end <= mlist->address) || (base == end)) 210925cf1a30Sjl return (mlist); 211025cf1a30Sjl 211125cf1a30Sjl for (tl = ml = mlist; ml; tl = ml, ml = nlp) { 211225cf1a30Sjl uint64_t mend; 211325cf1a30Sjl 211425cf1a30Sjl nlp = ml->next; 211525cf1a30Sjl 211625cf1a30Sjl if (end <= ml->address) 211725cf1a30Sjl break; 211825cf1a30Sjl 211925cf1a30Sjl mend = ml->address + ml->size; 212025cf1a30Sjl if (base < mend) { 212125cf1a30Sjl if (base <= ml->address) { 212225cf1a30Sjl ml->address = end; 212325cf1a30Sjl if (end >= mend) 212425cf1a30Sjl ml->size = 0ull; 212525cf1a30Sjl else 212625cf1a30Sjl ml->size = mend - ml->address; 212725cf1a30Sjl } else { 212825cf1a30Sjl ml->size = base - ml->address; 212925cf1a30Sjl if (end < mend) { 213025cf1a30Sjl struct memlist *nl; 213125cf1a30Sjl /* 213225cf1a30Sjl * splitting an memlist entry. 213325cf1a30Sjl */ 213425cf1a30Sjl nl = kmem_alloc(sizeof (struct memlist), 213525cf1a30Sjl KM_SLEEP); 213625cf1a30Sjl nl->address = end; 213725cf1a30Sjl nl->size = mend - nl->address; 213825cf1a30Sjl if ((nl->next = nlp) != NULL) 213925cf1a30Sjl nlp->prev = nl; 214025cf1a30Sjl nl->prev = ml; 214125cf1a30Sjl ml->next = nl; 214225cf1a30Sjl nlp = nl; 214325cf1a30Sjl } 214425cf1a30Sjl } 214525cf1a30Sjl if (ml->size == 0ull) { 214625cf1a30Sjl if (ml == mlist) { 214725cf1a30Sjl if ((mlist = nlp) != NULL) 214825cf1a30Sjl nlp->prev = NULL; 214925cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 215025cf1a30Sjl if (mlist == NULL) 215125cf1a30Sjl break; 215225cf1a30Sjl ml = nlp; 215325cf1a30Sjl } else { 215425cf1a30Sjl if ((tl->next = nlp) != NULL) 215525cf1a30Sjl nlp->prev = tl; 215625cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 215725cf1a30Sjl ml = tl; 215825cf1a30Sjl } 215925cf1a30Sjl } 216025cf1a30Sjl } 216125cf1a30Sjl } 216225cf1a30Sjl 216325cf1a30Sjl return (mlist); 216425cf1a30Sjl } 216525cf1a30Sjl 216625cf1a30Sjl static void 216725cf1a30Sjl mc_get_mlist(mc_opl_t *mcp) 216825cf1a30Sjl { 216925cf1a30Sjl struct memlist *mlist; 217025cf1a30Sjl 217125cf1a30Sjl memlist_read_lock(); 217225cf1a30Sjl mlist = mc_memlist_dup(phys_install); 217325cf1a30Sjl memlist_read_unlock(); 217425cf1a30Sjl 217525cf1a30Sjl if (mlist) { 217625cf1a30Sjl mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address); 217725cf1a30Sjl } 217825cf1a30Sjl 217925cf1a30Sjl if (mlist) { 218025cf1a30Sjl uint64_t startpa, endpa; 218125cf1a30Sjl 218225cf1a30Sjl startpa = mcp->mc_start_address + mcp->mc_size; 218325cf1a30Sjl endpa = ptob(physmax + 1); 218425cf1a30Sjl if (endpa > startpa) { 218525cf1a30Sjl mlist = mc_memlist_del_span(mlist, 218625cf1a30Sjl startpa, endpa - startpa); 218725cf1a30Sjl } 218825cf1a30Sjl } 218925cf1a30Sjl 219025cf1a30Sjl if (mlist) { 219125cf1a30Sjl mcp->mlist = mlist; 219225cf1a30Sjl } 219325cf1a30Sjl } 219425cf1a30Sjl 219525cf1a30Sjl int 219625cf1a30Sjl mc_board_add(mc_opl_t *mcp) 219725cf1a30Sjl { 219825cf1a30Sjl struct mc_addr_spec *macaddr; 2199*0cc8ae86Sav cs_status_t *cs_status; 2200*0cc8ae86Sav int len, len1, i, bk, cc; 220125cf1a30Sjl mc_addr_info_t maddr; 220225cf1a30Sjl uint32_t mirr; 2203*0cc8ae86Sav int nbanks = 0; 2204*0cc8ae86Sav uint64_t nbytes = 0; 220525cf1a30Sjl 220625cf1a30Sjl /* 220725cf1a30Sjl * Get configurations from "pseudo-mc" node which includes: 220825cf1a30Sjl * board# : LSB number 220925cf1a30Sjl * mac-addr : physical base address of MAC registers 221025cf1a30Sjl * csX-mac-pa-trans-table: translation table from DIMM address 221125cf1a30Sjl * to physical address or vice versa. 221225cf1a30Sjl */ 221325cf1a30Sjl mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip, 221425cf1a30Sjl DDI_PROP_DONTPASS, "board#", -1); 221525cf1a30Sjl 2216*0cc8ae86Sav if (mcp->mc_board_num == -1) { 2217*0cc8ae86Sav return (DDI_FAILURE); 2218*0cc8ae86Sav } 2219*0cc8ae86Sav 222025cf1a30Sjl /* 222125cf1a30Sjl * Get start address in this CAB. It can be gotten from 222225cf1a30Sjl * "sb-mem-ranges" property. 222325cf1a30Sjl */ 222425cf1a30Sjl 222525cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 222625cf1a30Sjl return (DDI_FAILURE); 222725cf1a30Sjl } 222825cf1a30Sjl /* get mac-pa trans tables */ 222925cf1a30Sjl for (i = 0; i < MC_TT_CS; i++) { 223025cf1a30Sjl len = MC_TT_ENTRIES; 223125cf1a30Sjl cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip, 223225cf1a30Sjl DDI_PROP_DONTPASS, mc_tbl_name[i], 223325cf1a30Sjl (caddr_t)mcp->mc_trans_table[i], &len); 223425cf1a30Sjl 223525cf1a30Sjl if (cc != DDI_SUCCESS) { 223625cf1a30Sjl bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES); 223725cf1a30Sjl } 223825cf1a30Sjl } 223925cf1a30Sjl mcp->mlist = NULL; 224025cf1a30Sjl 224125cf1a30Sjl mc_get_mlist(mcp); 224225cf1a30Sjl 224325cf1a30Sjl /* initialize bank informations */ 224425cf1a30Sjl cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 224525cf1a30Sjl "mc-addr", (caddr_t)&macaddr, &len); 224625cf1a30Sjl if (cc != DDI_SUCCESS) { 224725cf1a30Sjl cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc); 224825cf1a30Sjl return (DDI_FAILURE); 224925cf1a30Sjl } 225025cf1a30Sjl 2251*0cc8ae86Sav cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 2252*0cc8ae86Sav "cs-status", (caddr_t)&cs_status, &len1); 225325cf1a30Sjl 2254*0cc8ae86Sav if (cc != DDI_SUCCESS) { 2255*0cc8ae86Sav if (len > 0) 2256*0cc8ae86Sav kmem_free(macaddr, len); 2257*0cc8ae86Sav cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc); 2258*0cc8ae86Sav return (DDI_FAILURE); 2259*0cc8ae86Sav } 226025cf1a30Sjl 2261*0cc8ae86Sav mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL); 2262*0cc8ae86Sav 2263*0cc8ae86Sav for (i = 0; i < len1 / sizeof (cs_status_t); i++) { 2264*0cc8ae86Sav nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) | 2265*0cc8ae86Sav ((uint64_t)cs_status[i].cs_avail_low); 2266*0cc8ae86Sav } 2267*0cc8ae86Sav if (len1 > 0) 2268*0cc8ae86Sav kmem_free(cs_status, len1); 2269*0cc8ae86Sav nbanks = len / sizeof (struct mc_addr_spec); 2270*0cc8ae86Sav 2271*0cc8ae86Sav if (nbanks > 0) 2272*0cc8ae86Sav nbytes /= nbanks; 2273*0cc8ae86Sav else { 2274*0cc8ae86Sav /* No need to free macaddr because len must be 0 */ 2275*0cc8ae86Sav mcp->mc_status |= MC_MEMORYLESS; 2276*0cc8ae86Sav return (DDI_SUCCESS); 2277*0cc8ae86Sav } 2278*0cc8ae86Sav 2279*0cc8ae86Sav for (i = 0; i < BANKNUM_PER_SB; i++) { 2280*0cc8ae86Sav mcp->mc_scf_retry[i] = 0; 2281*0cc8ae86Sav mcp->mc_period[i] = 0; 2282*0cc8ae86Sav mcp->mc_speedup_period[i] = 0; 2283*0cc8ae86Sav } 2284*0cc8ae86Sav 2285*0cc8ae86Sav /* 2286*0cc8ae86Sav * Get the memory size here. Let it be B (bytes). 2287*0cc8ae86Sav * Let T be the time in u.s. to scan 64 bytes. 2288*0cc8ae86Sav * If we want to complete 1 round of scanning in P seconds. 2289*0cc8ae86Sav * 2290*0cc8ae86Sav * B * T * 10^(-6) = P 2291*0cc8ae86Sav * --------------- 2292*0cc8ae86Sav * 64 2293*0cc8ae86Sav * 2294*0cc8ae86Sav * T = P * 64 * 10^6 2295*0cc8ae86Sav * ------------- 2296*0cc8ae86Sav * B 2297*0cc8ae86Sav * 2298*0cc8ae86Sav * = P * 64 * 10^6 2299*0cc8ae86Sav * ------------- 2300*0cc8ae86Sav * B 2301*0cc8ae86Sav * 2302*0cc8ae86Sav * The timing bits are set in PTRL_CNTL[28:26] where 2303*0cc8ae86Sav * 2304*0cc8ae86Sav * 0 - 1 m.s 2305*0cc8ae86Sav * 1 - 512 u.s. 2306*0cc8ae86Sav * 10 - 256 u.s. 2307*0cc8ae86Sav * 11 - 128 u.s. 2308*0cc8ae86Sav * 100 - 64 u.s. 2309*0cc8ae86Sav * 101 - 32 u.s. 2310*0cc8ae86Sav * 110 - 0 u.s. 2311*0cc8ae86Sav * 111 - reserved. 2312*0cc8ae86Sav * 2313*0cc8ae86Sav * 2314*0cc8ae86Sav * a[0] = 110, a[1] = 101, ... a[6] = 0 2315*0cc8ae86Sav * 2316*0cc8ae86Sav * cs-status property is int x 7 2317*0cc8ae86Sav * 0 - cs# 2318*0cc8ae86Sav * 1 - cs-status 2319*0cc8ae86Sav * 2 - cs-avail.hi 2320*0cc8ae86Sav * 3 - cs-avail.lo 2321*0cc8ae86Sav * 4 - dimm-capa.hi 2322*0cc8ae86Sav * 5 - dimm-capa.lo 2323*0cc8ae86Sav * 6 - #of dimms 2324*0cc8ae86Sav */ 2325*0cc8ae86Sav 2326*0cc8ae86Sav if (nbytes > 0) { 2327*0cc8ae86Sav int i; 2328*0cc8ae86Sav uint64_t ms; 2329*0cc8ae86Sav ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes; 2330*0cc8ae86Sav mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds; 2331*0cc8ae86Sav for (i = 0; i < MC_MAX_SPEEDS - 1; i++) { 2332*0cc8ae86Sav if (ms < mc_scan_speeds[i + 1].mc_period) { 2333*0cc8ae86Sav mcp->mc_speed = mc_scan_speeds[i].mc_speeds; 2334*0cc8ae86Sav break; 2335*0cc8ae86Sav } 2336*0cc8ae86Sav } 2337*0cc8ae86Sav } else 2338*0cc8ae86Sav mcp->mc_speed = 0; 2339*0cc8ae86Sav 2340*0cc8ae86Sav 2341*0cc8ae86Sav for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) { 2342*0cc8ae86Sav struct mc_bank *bankp; 2343*0cc8ae86Sav uint32_t reg; 2344*0cc8ae86Sav 2345*0cc8ae86Sav /* 2346*0cc8ae86Sav * setup bank 2347*0cc8ae86Sav */ 2348*0cc8ae86Sav bk = macaddr[i].bank; 2349*0cc8ae86Sav bankp = &(mcp->mc_bank[bk]); 2350*0cc8ae86Sav bankp->mcb_status = BANK_INSTALLED; 2351*0cc8ae86Sav bankp->mcb_reg_base = REGS_PA(macaddr, i); 2352*0cc8ae86Sav 2353*0cc8ae86Sav reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk)); 2354*0cc8ae86Sav bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS); 235525cf1a30Sjl 235625cf1a30Sjl /* 235725cf1a30Sjl * check if mirror mode 235825cf1a30Sjl */ 235925cf1a30Sjl mirr = LD_MAC_REG(MAC_MIRR(mcp, bk)); 236025cf1a30Sjl 236125cf1a30Sjl if (mirr & MAC_MIRR_MIRROR_MODE) { 236225cf1a30Sjl MC_LOG("Mirror -> /LSB%d/B%d\n", 236325cf1a30Sjl mcp->mc_board_num, bk); 236425cf1a30Sjl bankp->mcb_status |= BANK_MIRROR_MODE; 236525cf1a30Sjl /* 236625cf1a30Sjl * The following bit is only used for 236725cf1a30Sjl * error injection. We should clear it 236825cf1a30Sjl */ 236925cf1a30Sjl if (mirr & MAC_MIRR_BANK_EXCLUSIVE) 237025cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bk), 237125cf1a30Sjl 0); 237225cf1a30Sjl } 237325cf1a30Sjl 237425cf1a30Sjl /* 237525cf1a30Sjl * restart if not mirror mode or the other bank 237625cf1a30Sjl * of the mirror is not running 237725cf1a30Sjl */ 237825cf1a30Sjl if (!(mirr & MAC_MIRR_MIRROR_MODE) || 237925cf1a30Sjl !(mcp->mc_bank[bk^1].mcb_status & 238025cf1a30Sjl BANK_PTRL_RUNNING)) { 238125cf1a30Sjl MC_LOG("Starting up /LSB%d/B%d\n", 238225cf1a30Sjl mcp->mc_board_num, bk); 238325cf1a30Sjl get_ptrl_start_address(mcp, bk, &maddr.mi_maddr); 2384*0cc8ae86Sav maddr.mi_maddr.ma_bd = mcp->mc_board_num; 2385*0cc8ae86Sav maddr.mi_maddr.ma_bank = bk; 2386*0cc8ae86Sav maddr.mi_maddr.ma_dimm_addr = 0; 2387*0cc8ae86Sav maddr.mi_valid = 0; 238825cf1a30Sjl maddr.mi_advance = 0; 238925cf1a30Sjl restart_patrol(mcp, bk, &maddr); 239025cf1a30Sjl } else { 239125cf1a30Sjl MC_LOG("Not starting up /LSB%d/B%d\n", 239225cf1a30Sjl mcp->mc_board_num, bk); 239325cf1a30Sjl } 239425cf1a30Sjl bankp->mcb_status |= BANK_PTRL_RUNNING; 239525cf1a30Sjl } 2396*0cc8ae86Sav if (len > 0) 2397*0cc8ae86Sav kmem_free(macaddr, len); 2398*0cc8ae86Sav 2399*0cc8ae86Sav mcp->mc_dimm_list = mc_get_dimm_list(mcp); 240025cf1a30Sjl 240125cf1a30Sjl /* 240225cf1a30Sjl * set interval in HZ. 240325cf1a30Sjl */ 240425cf1a30Sjl mcp->mc_last_error = 0; 240525cf1a30Sjl 240625cf1a30Sjl /* restart memory patrol checking */ 240725cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 240825cf1a30Sjl 240925cf1a30Sjl return (DDI_SUCCESS); 241025cf1a30Sjl } 241125cf1a30Sjl 241225cf1a30Sjl int 241325cf1a30Sjl mc_board_del(mc_opl_t *mcp) 241425cf1a30Sjl { 241525cf1a30Sjl int i; 241625cf1a30Sjl scf_log_t *p; 241725cf1a30Sjl 241825cf1a30Sjl /* 241925cf1a30Sjl * cleanup mac state 242025cf1a30Sjl */ 242125cf1a30Sjl mutex_enter(&mcp->mc_lock); 2422*0cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 2423*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 2424*0cc8ae86Sav mutex_destroy(&mcp->mc_lock); 2425*0cc8ae86Sav return (DDI_SUCCESS); 2426*0cc8ae86Sav } 242725cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 242825cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 242925cf1a30Sjl mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED; 243025cf1a30Sjl } 243125cf1a30Sjl } 243225cf1a30Sjl 243325cf1a30Sjl /* stop memory patrol checking */ 243425cf1a30Sjl if (mcp->mc_status & MC_POLL_RUNNING) { 243525cf1a30Sjl mcp->mc_status &= ~MC_POLL_RUNNING; 243625cf1a30Sjl } 243725cf1a30Sjl 243825cf1a30Sjl /* just throw away all the scf logs */ 2439*0cc8ae86Sav for (i = 0; i < BANKNUM_PER_SB; i++) { 2440*0cc8ae86Sav while ((p = mcp->mc_scf_log[i]) != NULL) { 2441*0cc8ae86Sav mcp->mc_scf_log[i] = p->sl_next; 2442*0cc8ae86Sav mcp->mc_scf_total[i]--; 244325cf1a30Sjl kmem_free(p, sizeof (scf_log_t)); 2444*0cc8ae86Sav } 244525cf1a30Sjl } 244625cf1a30Sjl 244725cf1a30Sjl if (mcp->mlist) 244825cf1a30Sjl mc_memlist_delete(mcp->mlist); 244925cf1a30Sjl 2450*0cc8ae86Sav if (mcp->mc_dimm_list) 2451*0cc8ae86Sav mc_free_dimm_list(mcp->mc_dimm_list); 2452*0cc8ae86Sav 245325cf1a30Sjl mutex_exit(&mcp->mc_lock); 245425cf1a30Sjl 245525cf1a30Sjl mutex_destroy(&mcp->mc_lock); 245625cf1a30Sjl return (DDI_SUCCESS); 245725cf1a30Sjl } 245825cf1a30Sjl 245925cf1a30Sjl int 246025cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag) 246125cf1a30Sjl { 246225cf1a30Sjl /* stop memory patrol checking */ 246325cf1a30Sjl mutex_enter(&mcp->mc_lock); 2464*0cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 2465*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 2466*0cc8ae86Sav return (DDI_SUCCESS); 2467*0cc8ae86Sav } 2468*0cc8ae86Sav 246925cf1a30Sjl if (mcp->mc_status & MC_POLL_RUNNING) { 247025cf1a30Sjl mcp->mc_status &= ~MC_POLL_RUNNING; 247125cf1a30Sjl } 247225cf1a30Sjl mcp->mc_status |= flag; 247325cf1a30Sjl mutex_exit(&mcp->mc_lock); 247425cf1a30Sjl 247525cf1a30Sjl return (DDI_SUCCESS); 247625cf1a30Sjl } 247725cf1a30Sjl 247825cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */ 247925cf1a30Sjl 248025cf1a30Sjl int 248125cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag) 248225cf1a30Sjl { 248325cf1a30Sjl int i; 248425cf1a30Sjl uint64_t basepa; 248525cf1a30Sjl 248625cf1a30Sjl mutex_enter(&mcp->mc_lock); 2487*0cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 2488*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 2489*0cc8ae86Sav return (DDI_SUCCESS); 2490*0cc8ae86Sav } 249125cf1a30Sjl basepa = mcp->mc_start_address; 249225cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 249325cf1a30Sjl mutex_exit(&mcp->mc_lock); 249425cf1a30Sjl return (DDI_FAILURE); 249525cf1a30Sjl } 249625cf1a30Sjl 249725cf1a30Sjl if (basepa != mcp->mc_start_address) { 249825cf1a30Sjl if (mcp->mlist) 249925cf1a30Sjl mc_memlist_delete(mcp->mlist); 250025cf1a30Sjl mcp->mlist = NULL; 250125cf1a30Sjl mc_get_mlist(mcp); 250225cf1a30Sjl } 250325cf1a30Sjl 250425cf1a30Sjl mcp->mc_status &= ~flag; 250525cf1a30Sjl 250625cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 250725cf1a30Sjl mutex_exit(&mcp->mc_lock); 250825cf1a30Sjl return (DDI_SUCCESS); 250925cf1a30Sjl } 251025cf1a30Sjl 251125cf1a30Sjl if (!(mcp->mc_status & MC_POLL_RUNNING)) { 251225cf1a30Sjl /* restart memory patrol checking */ 251325cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 251425cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 251525cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 251625cf1a30Sjl restart_patrol(mcp, i, NULL); 251725cf1a30Sjl } 251825cf1a30Sjl } 251925cf1a30Sjl } 252025cf1a30Sjl mutex_exit(&mcp->mc_lock); 252125cf1a30Sjl 252225cf1a30Sjl return (DDI_SUCCESS); 252325cf1a30Sjl } 252425cf1a30Sjl 252525cf1a30Sjl static mc_opl_t * 252625cf1a30Sjl mc_pa_to_mcp(uint64_t pa) 252725cf1a30Sjl { 2528*0cc8ae86Sav mc_opl_t *mcp; 2529*0cc8ae86Sav int i; 2530*0cc8ae86Sav 253125cf1a30Sjl ASSERT(MUTEX_HELD(&mcmutex)); 2532*0cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 2533*0cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 2534*0cc8ae86Sav continue; 253525cf1a30Sjl /* if mac patrol is suspended, we cannot rely on it */ 2536*0cc8ae86Sav if (!(mcp->mc_status & MC_POLL_RUNNING) || 2537*0cc8ae86Sav (mcp->mc_status & MC_SOFT_SUSPENDED)) 253825cf1a30Sjl continue; 2539*0cc8ae86Sav if ((mcp->mc_start_address <= pa) && 2540*0cc8ae86Sav (pa < (mcp->mc_start_address + mcp->mc_size))) { 2541*0cc8ae86Sav return (mcp); 254225cf1a30Sjl } 254325cf1a30Sjl } 254425cf1a30Sjl return (NULL); 254525cf1a30Sjl } 254625cf1a30Sjl 254725cf1a30Sjl /* 254825cf1a30Sjl * Get Physical Board number from Logical one. 254925cf1a30Sjl */ 255025cf1a30Sjl static int 255125cf1a30Sjl mc_opl_get_physical_board(int sb) 255225cf1a30Sjl { 255325cf1a30Sjl if (&opl_get_physical_board) { 255425cf1a30Sjl return (opl_get_physical_board(sb)); 255525cf1a30Sjl } 255625cf1a30Sjl 255725cf1a30Sjl cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n"); 255825cf1a30Sjl return (-1); 255925cf1a30Sjl } 256025cf1a30Sjl 256125cf1a30Sjl /* ARGSUSED */ 256225cf1a30Sjl int 256325cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen, 256425cf1a30Sjl int *lenp) 256525cf1a30Sjl { 2566*0cc8ae86Sav int i; 256725cf1a30Sjl int sb; 2568*0cc8ae86Sav int bank; 2569*0cc8ae86Sav mc_opl_t *mcp; 2570*0cc8ae86Sav char memb_num; 257125cf1a30Sjl 257225cf1a30Sjl mutex_enter(&mcmutex); 257325cf1a30Sjl 257425cf1a30Sjl if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) || 257525cf1a30Sjl (!pa_is_valid(mcp, flt_addr))) { 257625cf1a30Sjl mutex_exit(&mcmutex); 257725cf1a30Sjl if (snprintf(buf, buflen, "UNKNOWN") >= buflen) { 257825cf1a30Sjl return (ENOSPC); 257925cf1a30Sjl } else { 258025cf1a30Sjl if (lenp) 258125cf1a30Sjl *lenp = strlen(buf); 258225cf1a30Sjl } 258325cf1a30Sjl return (0); 258425cf1a30Sjl } 258525cf1a30Sjl 258625cf1a30Sjl bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address); 258725cf1a30Sjl sb = mc_opl_get_physical_board(mcp->mc_board_num); 258825cf1a30Sjl 258925cf1a30Sjl if (sb == -1) { 259025cf1a30Sjl mutex_exit(&mcmutex); 259125cf1a30Sjl return (ENXIO); 259225cf1a30Sjl } 259325cf1a30Sjl 2594*0cc8ae86Sav if (plat_model == MODEL_DC) { 2595*0cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 2596*0cc8ae86Sav snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s MEM%s MEM%s", 2597*0cc8ae86Sav model_names[plat_model].unit_name, sb, 2598*0cc8ae86Sav mc_dc_dimm_unum_table[i], mc_dc_dimm_unum_table[i + 1], 2599*0cc8ae86Sav mc_dc_dimm_unum_table[i + 2], mc_dc_dimm_unum_table[i + 3]); 260025cf1a30Sjl } else { 2601*0cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 2602*0cc8ae86Sav memb_num = mc_ff_dimm_unum_table[i][0]; 2603*0cc8ae86Sav snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s MEM%s MEM%s", 2604*0cc8ae86Sav model_names[plat_model].unit_name, 2605*0cc8ae86Sav model_names[plat_model].mem_name, memb_num, 2606*0cc8ae86Sav &mc_ff_dimm_unum_table[i][1], 2607*0cc8ae86Sav 2608*0cc8ae86Sav &mc_ff_dimm_unum_table[i + 1][1], 2609*0cc8ae86Sav &mc_ff_dimm_unum_table[i + 2][1], 2610*0cc8ae86Sav &mc_ff_dimm_unum_table[i + 3][1]); 2611*0cc8ae86Sav } 2612*0cc8ae86Sav if (lenp) { 2613*0cc8ae86Sav *lenp = strlen(buf); 261425cf1a30Sjl } 261525cf1a30Sjl mutex_exit(&mcmutex); 261625cf1a30Sjl return (0); 261725cf1a30Sjl } 261825cf1a30Sjl 261925cf1a30Sjl int 2620*0cc8ae86Sav opl_mc_suspend(void) 262125cf1a30Sjl { 262225cf1a30Sjl mc_opl_t *mcp; 2623*0cc8ae86Sav int i; 262425cf1a30Sjl 262525cf1a30Sjl mutex_enter(&mcmutex); 2626*0cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 2627*0cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 2628*0cc8ae86Sav continue; 2629*0cc8ae86Sav mc_suspend(mcp, MC_SOFT_SUSPENDED); 263025cf1a30Sjl } 263125cf1a30Sjl mutex_exit(&mcmutex); 2632*0cc8ae86Sav 263325cf1a30Sjl return (0); 263425cf1a30Sjl } 263525cf1a30Sjl 263625cf1a30Sjl int 2637*0cc8ae86Sav opl_mc_resume(void) 263825cf1a30Sjl { 263925cf1a30Sjl mc_opl_t *mcp; 2640*0cc8ae86Sav int i; 264125cf1a30Sjl 264225cf1a30Sjl mutex_enter(&mcmutex); 2643*0cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 2644*0cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 2645*0cc8ae86Sav continue; 2646*0cc8ae86Sav mc_resume(mcp, MC_SOFT_SUSPENDED); 264725cf1a30Sjl } 264825cf1a30Sjl mutex_exit(&mcmutex); 2649*0cc8ae86Sav 265025cf1a30Sjl return (0); 265125cf1a30Sjl } 265225cf1a30Sjl static void 265325cf1a30Sjl insert_mcp(mc_opl_t *mcp) 265425cf1a30Sjl { 265525cf1a30Sjl mutex_enter(&mcmutex); 2656*0cc8ae86Sav if (mc_instances[mcp->mc_board_num] != NULL) { 2657*0cc8ae86Sav MC_LOG("mc-opl instance for board# %d already exists\n", 2658*0cc8ae86Sav mcp->mc_board_num); 2659*0cc8ae86Sav } 2660*0cc8ae86Sav mc_instances[mcp->mc_board_num] = mcp; 266125cf1a30Sjl mutex_exit(&mcmutex); 266225cf1a30Sjl } 266325cf1a30Sjl 266425cf1a30Sjl static void 266525cf1a30Sjl delete_mcp(mc_opl_t *mcp) 266625cf1a30Sjl { 2667*0cc8ae86Sav mutex_enter(&mcmutex); 2668*0cc8ae86Sav mc_instances[mcp->mc_board_num] = 0; 2669*0cc8ae86Sav mutex_exit(&mcmutex); 267025cf1a30Sjl } 267125cf1a30Sjl 267225cf1a30Sjl /* Error injection interface */ 267325cf1a30Sjl 267425cf1a30Sjl /* ARGSUSED */ 267525cf1a30Sjl int 267625cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags) 267725cf1a30Sjl { 267825cf1a30Sjl mc_opl_t *mcp; 267925cf1a30Sjl int bank; 268025cf1a30Sjl uint32_t dimm_addr; 268125cf1a30Sjl uint32_t cntl; 268225cf1a30Sjl mc_addr_info_t maddr; 268325cf1a30Sjl uint32_t data, stat; 268425cf1a30Sjl int both_sides = 0; 268525cf1a30Sjl uint64_t pa0; 268625cf1a30Sjl on_trap_data_t otd; 268725cf1a30Sjl extern void cpu_flush_ecache(void); 268825cf1a30Sjl 268925cf1a30Sjl MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags); 269025cf1a30Sjl 269125cf1a30Sjl mutex_enter(&mcmutex); 269225cf1a30Sjl if ((mcp = mc_pa_to_mcp(pa)) == NULL) { 269325cf1a30Sjl mutex_exit(&mcmutex); 269425cf1a30Sjl MC_LOG("mc_inject_error: invalid pa\n"); 269525cf1a30Sjl return (ENOTSUP); 269625cf1a30Sjl } 269725cf1a30Sjl 269825cf1a30Sjl mutex_enter(&mcp->mc_lock); 269925cf1a30Sjl mutex_exit(&mcmutex); 270025cf1a30Sjl 270125cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 270225cf1a30Sjl mutex_exit(&mcp->mc_lock); 270325cf1a30Sjl MC_LOG("mc-opl has been suspended. No error injection.\n"); 270425cf1a30Sjl return (EBUSY); 270525cf1a30Sjl } 270625cf1a30Sjl 270725cf1a30Sjl /* convert pa to offset within the board */ 270825cf1a30Sjl MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address); 270925cf1a30Sjl 271025cf1a30Sjl if (!pa_is_valid(mcp, pa)) { 271125cf1a30Sjl mutex_exit(&mcp->mc_lock); 271225cf1a30Sjl return (EINVAL); 271325cf1a30Sjl } 271425cf1a30Sjl 271525cf1a30Sjl pa0 = pa - mcp->mc_start_address; 271625cf1a30Sjl 271725cf1a30Sjl bank = pa_to_bank(mcp, pa0); 271825cf1a30Sjl 271925cf1a30Sjl if (flags & MC_INJECT_FLAG_OTHER) 272025cf1a30Sjl bank = bank ^ 1; 272125cf1a30Sjl 272225cf1a30Sjl if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) { 272325cf1a30Sjl mutex_exit(&mcp->mc_lock); 272425cf1a30Sjl MC_LOG("Not mirror mode\n"); 272525cf1a30Sjl return (EINVAL); 272625cf1a30Sjl } 272725cf1a30Sjl 272825cf1a30Sjl dimm_addr = pa_to_dimm(mcp, pa0); 272925cf1a30Sjl 273025cf1a30Sjl MC_LOG("injecting error to /LSB%d/B%d/D%x\n", 273125cf1a30Sjl mcp->mc_board_num, bank, dimm_addr); 273225cf1a30Sjl 273325cf1a30Sjl 273425cf1a30Sjl switch (error_type) { 273525cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 273625cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 273725cf1a30Sjl case MC_INJECT_MUE: 273825cf1a30Sjl both_sides = 1; 273925cf1a30Sjl } 274025cf1a30Sjl 274125cf1a30Sjl if (flags & MC_INJECT_FLAG_RESET) 274225cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0); 274325cf1a30Sjl 274425cf1a30Sjl ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK); 274525cf1a30Sjl 274625cf1a30Sjl if (both_sides) { 274725cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0); 274825cf1a30Sjl ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), 274925cf1a30Sjl dimm_addr & MAC_EG_ADD_MASK); 275025cf1a30Sjl } 275125cf1a30Sjl 275225cf1a30Sjl switch (error_type) { 275325cf1a30Sjl case MC_INJECT_UE: 275425cf1a30Sjl case MC_INJECT_SUE: 275525cf1a30Sjl case MC_INJECT_MUE: 275625cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 275725cf1a30Sjl cntl = MAC_EG_ADD_FIX 275825cf1a30Sjl |MAC_EG_FORCE_READ00|MAC_EG_FORCE_READ16 2759*0cc8ae86Sav |MAC_EG_RDERR_ONCE; 276025cf1a30Sjl } else { 276125cf1a30Sjl cntl = MAC_EG_ADD_FIX|MAC_EG_FORCE_DERR00 276225cf1a30Sjl |MAC_EG_FORCE_DERR16|MAC_EG_DERR_ONCE; 276325cf1a30Sjl } 276425cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 276525cf1a30Sjl break; 276625cf1a30Sjl case MC_INJECT_INTERMITTENT_CE: 276725cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 276825cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 276925cf1a30Sjl cntl = MAC_EG_ADD_FIX 277025cf1a30Sjl |MAC_EG_FORCE_READ00 2771*0cc8ae86Sav |MAC_EG_RDERR_ONCE; 277225cf1a30Sjl } else { 277325cf1a30Sjl cntl = MAC_EG_ADD_FIX 277425cf1a30Sjl |MAC_EG_FORCE_DERR16 277525cf1a30Sjl |MAC_EG_DERR_ONCE; 277625cf1a30Sjl } 277725cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 277825cf1a30Sjl break; 277925cf1a30Sjl case MC_INJECT_PERMANENT_CE: 278025cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 278125cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 278225cf1a30Sjl cntl = MAC_EG_ADD_FIX 278325cf1a30Sjl |MAC_EG_FORCE_READ00 2784*0cc8ae86Sav |MAC_EG_RDERR_ALWAYS; 278525cf1a30Sjl } else { 278625cf1a30Sjl cntl = MAC_EG_ADD_FIX 278725cf1a30Sjl |MAC_EG_FORCE_DERR16 278825cf1a30Sjl |MAC_EG_DERR_ALWAYS; 278925cf1a30Sjl } 279025cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 279125cf1a30Sjl break; 279225cf1a30Sjl case MC_INJECT_CMPE: 279325cf1a30Sjl data = 0xabcdefab; 279425cf1a30Sjl stphys(pa, data); 279525cf1a30Sjl cpu_flush_ecache(); 279625cf1a30Sjl MC_LOG("CMPE: writing data %x to %lx\n", data, pa); 279725cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE); 279825cf1a30Sjl stphys(pa, data ^ 0xffffffff); 279925cf1a30Sjl cpu_flush_ecache(); 280025cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), 0); 280125cf1a30Sjl MC_LOG("CMPE: write new data %xto %lx\n", data, pa); 280225cf1a30Sjl cntl = 0; 280325cf1a30Sjl break; 280425cf1a30Sjl case MC_INJECT_NOP: 280525cf1a30Sjl cntl = 0; 280625cf1a30Sjl break; 280725cf1a30Sjl default: 280825cf1a30Sjl MC_LOG("mc_inject_error: invalid option\n"); 280925cf1a30Sjl cntl = 0; 281025cf1a30Sjl } 281125cf1a30Sjl 281225cf1a30Sjl if (cntl) { 281325cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK); 281425cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 281525cf1a30Sjl 281625cf1a30Sjl if (both_sides) { 281725cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 281825cf1a30Sjl MAC_EG_SETUP_MASK); 281925cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 282025cf1a30Sjl } 282125cf1a30Sjl } 282225cf1a30Sjl 282325cf1a30Sjl /* 282425cf1a30Sjl * For all injection cases except compare error, we 282525cf1a30Sjl * must write to the PA to trigger the error. 282625cf1a30Sjl */ 282725cf1a30Sjl 282825cf1a30Sjl if (flags & MC_INJECT_FLAG_ST) { 282925cf1a30Sjl data = 0xf0e0d0c0; 283025cf1a30Sjl MC_LOG("Writing %x to %lx\n", data, pa); 283125cf1a30Sjl stphys(pa, data); 283225cf1a30Sjl cpu_flush_ecache(); 283325cf1a30Sjl } 283425cf1a30Sjl 283525cf1a30Sjl 283625cf1a30Sjl if (flags & MC_INJECT_FLAG_LD) { 283725cf1a30Sjl if (flags & MC_INJECT_FLAG_NO_TRAP) { 283825cf1a30Sjl if (on_trap(&otd, OT_DATA_EC)) { 283925cf1a30Sjl no_trap(); 284025cf1a30Sjl MC_LOG("Trap occurred\n"); 284125cf1a30Sjl } else { 284225cf1a30Sjl MC_LOG("On-trap Reading from %lx\n", pa); 284325cf1a30Sjl data = ldphys(pa); 284425cf1a30Sjl no_trap(); 284525cf1a30Sjl MC_LOG("data = %x\n", data); 284625cf1a30Sjl } 284725cf1a30Sjl } else { 284825cf1a30Sjl MC_LOG("Reading from %lx\n", pa); 284925cf1a30Sjl data = ldphys(pa); 285025cf1a30Sjl MC_LOG("data = %x\n", data); 285125cf1a30Sjl } 285225cf1a30Sjl } 285325cf1a30Sjl 285425cf1a30Sjl if (flags & MC_INJECT_FLAG_RESTART) { 285525cf1a30Sjl MC_LOG("Restart patrol\n"); 285625cf1a30Sjl maddr.mi_maddr.ma_bd = mcp->mc_board_num; 285725cf1a30Sjl maddr.mi_maddr.ma_bank = bank; 285825cf1a30Sjl maddr.mi_maddr.ma_dimm_addr = dimm_addr; 285925cf1a30Sjl maddr.mi_valid = 1; 286025cf1a30Sjl maddr.mi_advance = 0; 286125cf1a30Sjl restart_patrol(mcp, bank, &maddr); 286225cf1a30Sjl } 286325cf1a30Sjl 286425cf1a30Sjl if (flags & MC_INJECT_FLAG_POLL) { 2865*0cc8ae86Sav int running; 286625cf1a30Sjl 286725cf1a30Sjl MC_LOG("Poll patrol error\n"); 286825cf1a30Sjl stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank)); 286925cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 2870*0cc8ae86Sav running = cntl & MAC_CNTL_PTRL_START; 287125cf1a30Sjl if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) { 2872*0cc8ae86Sav if (running) { 2873*0cc8ae86Sav /* speed up the scanning */ 2874*0cc8ae86Sav mcp->mc_speedup_period[bank] = 2; 2875*0cc8ae86Sav MAC_CMD(mcp, bank, 0); 2876*0cc8ae86Sav } else { 2877*0cc8ae86Sav mcp->mc_speedup_period[bank] = 0; 2878*0cc8ae86Sav maddr.mi_valid = 0; 2879*0cc8ae86Sav maddr.mi_advance = 1; 2880*0cc8ae86Sav if (IS_MIRROR(mcp, bank)) 2881*0cc8ae86Sav mc_error_handler_mir(mcp, bank, 2882*0cc8ae86Sav &maddr); 2883*0cc8ae86Sav else 2884*0cc8ae86Sav mc_error_handler(mcp, bank, &maddr); 288525cf1a30Sjl 2886*0cc8ae86Sav restart_patrol(mcp, bank, &maddr); 2887*0cc8ae86Sav } 288825cf1a30Sjl } else 288925cf1a30Sjl restart_patrol(mcp, bank, NULL); 289025cf1a30Sjl } 289125cf1a30Sjl 289225cf1a30Sjl mutex_exit(&mcp->mc_lock); 289325cf1a30Sjl return (0); 289425cf1a30Sjl } 289525cf1a30Sjl void 289625cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data) 289725cf1a30Sjl { 2898*0cc8ae86Sav #ifndef lint 2899*0cc8ae86Sav uint32_t dummy; 2900*0cc8ae86Sav #endif 2901*0cc8ae86Sav 290225cf1a30Sjl MC_LOG("0x%x -> pa(%lx)\n", data, pa); 290325cf1a30Sjl stphysio(pa, data); 2904*0cc8ae86Sav 2905*0cc8ae86Sav /* force the above write to be processed by mac patrol */ 2906*0cc8ae86Sav #ifndef lint 2907*0cc8ae86Sav dummy = ldphysio(pa); 2908*0cc8ae86Sav #endif 290925cf1a30Sjl } 291025cf1a30Sjl 291125cf1a30Sjl uint32_t 291225cf1a30Sjl mc_ldphysio(uint64_t pa) 291325cf1a30Sjl { 291425cf1a30Sjl uint32_t rv; 291525cf1a30Sjl 291625cf1a30Sjl rv = ldphysio(pa); 291725cf1a30Sjl MC_LOG("pa(%lx) = 0x%x\n", pa, rv); 291825cf1a30Sjl return (rv); 291925cf1a30Sjl } 2920*0cc8ae86Sav 2921*0cc8ae86Sav #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 2922*0cc8ae86Sav 2923*0cc8ae86Sav /* 2924*0cc8ae86Sav * parse_unum_memory -- extract the board number and the DIMM name from 2925*0cc8ae86Sav * the unum. 2926*0cc8ae86Sav * 2927*0cc8ae86Sav * Return 0 for success and non-zero for a failure. 2928*0cc8ae86Sav */ 2929*0cc8ae86Sav int 2930*0cc8ae86Sav parse_unum_memory(char *unum, int *board, char *dname) 2931*0cc8ae86Sav { 2932*0cc8ae86Sav char *c; 2933*0cc8ae86Sav char x, y, z; 2934*0cc8ae86Sav 2935*0cc8ae86Sav if ((c = strstr(unum, "CMU")) != NULL) { 2936*0cc8ae86Sav /* DC Model */ 2937*0cc8ae86Sav c += 3; 2938*0cc8ae86Sav *board = (uint8_t)stoi(&c); 2939*0cc8ae86Sav if ((c = strstr(c, "MEM")) == NULL) { 2940*0cc8ae86Sav return (1); 2941*0cc8ae86Sav } 2942*0cc8ae86Sav c += 3; 2943*0cc8ae86Sav if (strlen(c) < 3) { 2944*0cc8ae86Sav return (2); 2945*0cc8ae86Sav } 2946*0cc8ae86Sav if ((!isdigit(c[0])) || (!(isdigit(c[1]))) || 2947*0cc8ae86Sav ((c[2] != 'A') && (c[2] != 'B'))) { 2948*0cc8ae86Sav return (3); 2949*0cc8ae86Sav } 2950*0cc8ae86Sav x = c[0]; 2951*0cc8ae86Sav y = c[1]; 2952*0cc8ae86Sav z = c[2]; 2953*0cc8ae86Sav } else if ((c = strstr(unum, "MBU_")) != NULL) { 2954*0cc8ae86Sav /* FF1/FF2 Model */ 2955*0cc8ae86Sav c += 4; 2956*0cc8ae86Sav if ((c[0] != 'A') && (c[0] != 'B')) { 2957*0cc8ae86Sav return (4); 2958*0cc8ae86Sav } 2959*0cc8ae86Sav if ((c = strstr(c, "MEMB")) == NULL) { 2960*0cc8ae86Sav return (5); 2961*0cc8ae86Sav } 2962*0cc8ae86Sav c += 4; 2963*0cc8ae86Sav 2964*0cc8ae86Sav x = c[0]; 2965*0cc8ae86Sav *board = ((uint8_t)stoi(&c)) / 4; 2966*0cc8ae86Sav if ((c = strstr(c, "MEM")) == NULL) { 2967*0cc8ae86Sav return (6); 2968*0cc8ae86Sav } 2969*0cc8ae86Sav c += 3; 2970*0cc8ae86Sav if (strlen(c) < 2) { 2971*0cc8ae86Sav return (7); 2972*0cc8ae86Sav } 2973*0cc8ae86Sav if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) { 2974*0cc8ae86Sav return (8); 2975*0cc8ae86Sav } 2976*0cc8ae86Sav y = c[0]; 2977*0cc8ae86Sav z = c[1]; 2978*0cc8ae86Sav } else { 2979*0cc8ae86Sav return (9); 2980*0cc8ae86Sav } 2981*0cc8ae86Sav if (*board < 0) { 2982*0cc8ae86Sav return (10); 2983*0cc8ae86Sav } 2984*0cc8ae86Sav dname[0] = x; 2985*0cc8ae86Sav dname[1] = y; 2986*0cc8ae86Sav dname[2] = z; 2987*0cc8ae86Sav dname[3] = '\0'; 2988*0cc8ae86Sav return (0); 2989*0cc8ae86Sav } 2990*0cc8ae86Sav 2991*0cc8ae86Sav /* 2992*0cc8ae86Sav * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and 2993*0cc8ae86Sav * the DIMM name. 2994*0cc8ae86Sav */ 2995*0cc8ae86Sav int 2996*0cc8ae86Sav mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 2997*0cc8ae86Sav int buflen, int *lenp) 2998*0cc8ae86Sav { 2999*0cc8ae86Sav int ret = ENODEV; 3000*0cc8ae86Sav mc_dimm_info_t *d = NULL; 3001*0cc8ae86Sav 3002*0cc8ae86Sav if ((d = mcp->mc_dimm_list) == NULL) 3003*0cc8ae86Sav return (ENOTSUP); 3004*0cc8ae86Sav 3005*0cc8ae86Sav for (; d != NULL; d = d->md_next) { 3006*0cc8ae86Sav if (strcmp(d->md_dimmname, dname) == 0) { 3007*0cc8ae86Sav break; 3008*0cc8ae86Sav } 3009*0cc8ae86Sav } 3010*0cc8ae86Sav if (d != NULL) { 3011*0cc8ae86Sav *lenp = strlen(d->md_serial) + strlen(d->md_partnum); 3012*0cc8ae86Sav if (buflen <= *lenp) { 3013*0cc8ae86Sav cmn_err(CE_WARN, "mc_get_mem_sid_dimm: " 3014*0cc8ae86Sav "buflen is smaller than %d\n", *lenp); 3015*0cc8ae86Sav ret = ENOSPC; 3016*0cc8ae86Sav } else { 3017*0cc8ae86Sav snprintf(buf, buflen, "%s:%s", 3018*0cc8ae86Sav d->md_serial, d->md_partnum); 3019*0cc8ae86Sav ret = 0; 3020*0cc8ae86Sav } 3021*0cc8ae86Sav } 3022*0cc8ae86Sav MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n", 3023*0cc8ae86Sav ret, dname, (ret == 0) ? buf : ""); 3024*0cc8ae86Sav return (ret); 3025*0cc8ae86Sav } 3026*0cc8ae86Sav 3027*0cc8ae86Sav int 3028*0cc8ae86Sav mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, 3029*0cc8ae86Sav int bank, uint32_t mf_type, uint32_t d_slot) 3030*0cc8ae86Sav { 3031*0cc8ae86Sav int sb; 3032*0cc8ae86Sav int lenp = buflen; 3033*0cc8ae86Sav int id; 3034*0cc8ae86Sav int ret; 3035*0cc8ae86Sav char *dimmnm; 3036*0cc8ae86Sav 3037*0cc8ae86Sav if ((sb = mc_opl_get_physical_board(lsb)) < 0) { 3038*0cc8ae86Sav return (ENODEV); 3039*0cc8ae86Sav } 3040*0cc8ae86Sav 3041*0cc8ae86Sav if (mf_type == FLT_TYPE_PERMANENT_CE) { 3042*0cc8ae86Sav if (plat_model == MODEL_DC) { 3043*0cc8ae86Sav id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 3044*0cc8ae86Sav } else { 3045*0cc8ae86Sav id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 3046*0cc8ae86Sav } 3047*0cc8ae86Sav dimmnm = mc_dc_dimm_unum_table[id]; 3048*0cc8ae86Sav if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen, 3049*0cc8ae86Sav &lenp)) != 0) { 3050*0cc8ae86Sav return (ret); 3051*0cc8ae86Sav } 3052*0cc8ae86Sav } else { 3053*0cc8ae86Sav return (1); 3054*0cc8ae86Sav } 3055*0cc8ae86Sav 3056*0cc8ae86Sav return (0); 3057*0cc8ae86Sav } 3058*0cc8ae86Sav 3059*0cc8ae86Sav /* 3060*0cc8ae86Sav * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum. 3061*0cc8ae86Sav */ 3062*0cc8ae86Sav int 3063*0cc8ae86Sav mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp) 3064*0cc8ae86Sav { 3065*0cc8ae86Sav int i; 3066*0cc8ae86Sav int ret = ENODEV; 3067*0cc8ae86Sav int board; 3068*0cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 3069*0cc8ae86Sav mc_opl_t *mcp; 3070*0cc8ae86Sav 3071*0cc8ae86Sav MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen); 3072*0cc8ae86Sav if ((ret = parse_unum_memory(unum, &board, dname)) != 0) { 3073*0cc8ae86Sav MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 3074*0cc8ae86Sav unum, ret); 3075*0cc8ae86Sav return (EINVAL); 3076*0cc8ae86Sav } 3077*0cc8ae86Sav 3078*0cc8ae86Sav if (board < 0) { 3079*0cc8ae86Sav MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n", 3080*0cc8ae86Sav board, dname); 3081*0cc8ae86Sav return (EINVAL); 3082*0cc8ae86Sav } 3083*0cc8ae86Sav 3084*0cc8ae86Sav mutex_enter(&mcmutex); 3085*0cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 3086*0cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 3087*0cc8ae86Sav continue; 3088*0cc8ae86Sav mutex_enter(&mcp->mc_lock); 3089*0cc8ae86Sav if (mcp->mc_board_num == board) { 3090*0cc8ae86Sav ret = mc_get_mem_sid_dimm(mcp, dname, buf, 3091*0cc8ae86Sav buflen, lenp); 3092*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 3093*0cc8ae86Sav break; 3094*0cc8ae86Sav } 3095*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 3096*0cc8ae86Sav } 3097*0cc8ae86Sav mutex_exit(&mcmutex); 3098*0cc8ae86Sav return (ret); 3099*0cc8ae86Sav } 3100*0cc8ae86Sav 3101*0cc8ae86Sav /* 3102*0cc8ae86Sav * mc_get_mem_offset -- get the offset in a DIMM for a given physical address. 3103*0cc8ae86Sav */ 3104*0cc8ae86Sav int 3105*0cc8ae86Sav mc_get_mem_offset(uint64_t paddr, uint64_t *offp) 3106*0cc8ae86Sav { 3107*0cc8ae86Sav int i; 3108*0cc8ae86Sav int ret = ENODEV; 3109*0cc8ae86Sav mc_addr_t maddr; 3110*0cc8ae86Sav mc_opl_t *mcp; 3111*0cc8ae86Sav 3112*0cc8ae86Sav mutex_enter(&mcmutex); 3113*0cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 3114*0cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 3115*0cc8ae86Sav continue; 3116*0cc8ae86Sav mutex_enter(&mcp->mc_lock); 3117*0cc8ae86Sav if (!pa_is_valid(mcp, paddr)) { 3118*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 3119*0cc8ae86Sav continue; 3120*0cc8ae86Sav } 3121*0cc8ae86Sav if (pa_to_maddr(mcp, paddr, &maddr) == 0) { 3122*0cc8ae86Sav *offp = maddr.ma_dimm_addr; 3123*0cc8ae86Sav ret = 0; 3124*0cc8ae86Sav } 3125*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 3126*0cc8ae86Sav } 3127*0cc8ae86Sav mutex_exit(&mcmutex); 3128*0cc8ae86Sav MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n", 3129*0cc8ae86Sav ret, paddr, *offp); 3130*0cc8ae86Sav return (ret); 3131*0cc8ae86Sav } 3132*0cc8ae86Sav 3133*0cc8ae86Sav /* 3134*0cc8ae86Sav * dname_to_bankslot - Get the bank and slot number from the DIMM name. 3135*0cc8ae86Sav */ 3136*0cc8ae86Sav int 3137*0cc8ae86Sav dname_to_bankslot(char *dname, int *bank, int *slot) 3138*0cc8ae86Sav { 3139*0cc8ae86Sav int i; 3140*0cc8ae86Sav int tsz; 3141*0cc8ae86Sav char **tbl; 3142*0cc8ae86Sav 3143*0cc8ae86Sav if (plat_model == MODEL_DC) { /* DC */ 3144*0cc8ae86Sav tbl = mc_dc_dimm_unum_table; 3145*0cc8ae86Sav tsz = OPL_MAX_DIMMS; 3146*0cc8ae86Sav } else { 3147*0cc8ae86Sav tbl = mc_ff_dimm_unum_table; 3148*0cc8ae86Sav tsz = 2 * OPL_MAX_DIMMS; 3149*0cc8ae86Sav } 3150*0cc8ae86Sav 3151*0cc8ae86Sav for (i = 0; i < tsz; i++) { 3152*0cc8ae86Sav if (strcmp(dname, tbl[i]) == 0) { 3153*0cc8ae86Sav break; 3154*0cc8ae86Sav } 3155*0cc8ae86Sav } 3156*0cc8ae86Sav if (i == tsz) { 3157*0cc8ae86Sav return (1); 3158*0cc8ae86Sav } 3159*0cc8ae86Sav *bank = INDEX_TO_BANK(i); 3160*0cc8ae86Sav *slot = INDEX_TO_SLOT(i); 3161*0cc8ae86Sav return (0); 3162*0cc8ae86Sav } 3163*0cc8ae86Sav 3164*0cc8ae86Sav /* 3165*0cc8ae86Sav * mc_get_mem_addr -- get the physical address of a DIMM corresponding 3166*0cc8ae86Sav * to the unum and sid. 3167*0cc8ae86Sav */ 3168*0cc8ae86Sav int 3169*0cc8ae86Sav mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr) 3170*0cc8ae86Sav { 3171*0cc8ae86Sav int board; 3172*0cc8ae86Sav int bank; 3173*0cc8ae86Sav int slot; 3174*0cc8ae86Sav int i; 3175*0cc8ae86Sav int ret = ENODEV; 3176*0cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 3177*0cc8ae86Sav mc_addr_t maddr; 3178*0cc8ae86Sav mc_opl_t *mcp; 3179*0cc8ae86Sav 3180*0cc8ae86Sav MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n", 3181*0cc8ae86Sav unum, sid, offset); 3182*0cc8ae86Sav if (parse_unum_memory(unum, &board, dname) != 0) { 3183*0cc8ae86Sav MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 3184*0cc8ae86Sav unum, ret); 3185*0cc8ae86Sav return (EINVAL); 3186*0cc8ae86Sav } 3187*0cc8ae86Sav 3188*0cc8ae86Sav if (board < 0) { 3189*0cc8ae86Sav MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n", 3190*0cc8ae86Sav board, dname); 3191*0cc8ae86Sav return (EINVAL); 3192*0cc8ae86Sav } 3193*0cc8ae86Sav 3194*0cc8ae86Sav mutex_enter(&mcmutex); 3195*0cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 3196*0cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 3197*0cc8ae86Sav continue; 3198*0cc8ae86Sav mutex_enter(&mcp->mc_lock); 3199*0cc8ae86Sav if (mcp->mc_board_num != board) { 3200*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 3201*0cc8ae86Sav continue; 3202*0cc8ae86Sav } 3203*0cc8ae86Sav 3204*0cc8ae86Sav ret = dname_to_bankslot(dname, &bank, &slot); 3205*0cc8ae86Sav MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot); 3206*0cc8ae86Sav if (ret != 0) { 3207*0cc8ae86Sav MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n"); 3208*0cc8ae86Sav ret = ENODEV; 3209*0cc8ae86Sav } else { 3210*0cc8ae86Sav maddr.ma_bd = board; 3211*0cc8ae86Sav maddr.ma_bank = bank; 3212*0cc8ae86Sav maddr.ma_dimm_addr = offset; 3213*0cc8ae86Sav ret = mcaddr_to_pa(mcp, &maddr, paddr); 3214*0cc8ae86Sav if (ret != 0) { 3215*0cc8ae86Sav MC_LOG("mc_get_mem_addr: " 3216*0cc8ae86Sav "mcaddr_to_pa failed\n"); 3217*0cc8ae86Sav ret = ENODEV; 3218*0cc8ae86Sav } 3219*0cc8ae86Sav } 3220*0cc8ae86Sav mutex_exit(&mcp->mc_lock); 3221*0cc8ae86Sav } 3222*0cc8ae86Sav mutex_exit(&mcmutex); 3223*0cc8ae86Sav MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr); 3224*0cc8ae86Sav return (ret); 3225*0cc8ae86Sav } 3226*0cc8ae86Sav 3227*0cc8ae86Sav static void 3228*0cc8ae86Sav mc_free_dimm_list(mc_dimm_info_t *d) 3229*0cc8ae86Sav { 3230*0cc8ae86Sav mc_dimm_info_t *next; 3231*0cc8ae86Sav 3232*0cc8ae86Sav while (d != NULL) { 3233*0cc8ae86Sav next = d->md_next; 3234*0cc8ae86Sav kmem_free(d, sizeof (mc_dimm_info_t)); 3235*0cc8ae86Sav d = next; 3236*0cc8ae86Sav } 3237*0cc8ae86Sav } 3238*0cc8ae86Sav 3239*0cc8ae86Sav /* 3240*0cc8ae86Sav * mc_get_dimm_list -- get the list of dimms with serial-id info 3241*0cc8ae86Sav * from the SP. 3242*0cc8ae86Sav */ 3243*0cc8ae86Sav mc_dimm_info_t * 3244*0cc8ae86Sav mc_get_dimm_list(mc_opl_t *mcp) 3245*0cc8ae86Sav { 3246*0cc8ae86Sav uint32_t bufsz; 3247*0cc8ae86Sav uint32_t maxbufsz; 3248*0cc8ae86Sav int ret; 3249*0cc8ae86Sav int sexp; 3250*0cc8ae86Sav board_dimm_info_t *bd_dimmp; 3251*0cc8ae86Sav mc_dimm_info_t *dimm_list = NULL; 3252*0cc8ae86Sav 3253*0cc8ae86Sav maxbufsz = bufsz = sizeof (board_dimm_info_t) + 3254*0cc8ae86Sav ((MCOPL_MAX_DIMMNAME + MCOPL_MAX_SERIAL + 3255*0cc8ae86Sav MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS); 3256*0cc8ae86Sav 3257*0cc8ae86Sav bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP); 3258*0cc8ae86Sav ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz); 3259*0cc8ae86Sav 3260*0cc8ae86Sav MC_LOG("mc_get_dimm_list: scf_service_getinfo returned=%d\n", ret); 3261*0cc8ae86Sav if (ret == 0) { 3262*0cc8ae86Sav sexp = sizeof (board_dimm_info_t) + 3263*0cc8ae86Sav ((bd_dimmp->bd_dnamesz + bd_dimmp->bd_serialsz + 3264*0cc8ae86Sav bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms); 3265*0cc8ae86Sav 3266*0cc8ae86Sav if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) && 3267*0cc8ae86Sav (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) && 3268*0cc8ae86Sav (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) && 3269*0cc8ae86Sav (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) && 3270*0cc8ae86Sav (sexp <= bufsz)) { 3271*0cc8ae86Sav 3272*0cc8ae86Sav #ifdef DEBUG 3273*0cc8ae86Sav if (oplmc_debug) 3274*0cc8ae86Sav mc_dump_dimm_info(bd_dimmp); 3275*0cc8ae86Sav #endif 3276*0cc8ae86Sav dimm_list = mc_prepare_dimmlist(bd_dimmp); 3277*0cc8ae86Sav 3278*0cc8ae86Sav } else { 3279*0cc8ae86Sav cmn_err(CE_WARN, "DIMM info version mismatch\n"); 3280*0cc8ae86Sav } 3281*0cc8ae86Sav } 3282*0cc8ae86Sav kmem_free(bd_dimmp, maxbufsz); 3283*0cc8ae86Sav MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list); 3284*0cc8ae86Sav return (dimm_list); 3285*0cc8ae86Sav } 3286*0cc8ae86Sav 3287*0cc8ae86Sav /* 3288*0cc8ae86Sav * mc_prepare_dimmlist - Prepare the dimm list from the infomation 3289*0cc8ae86Sav * recieved from the SP. 3290*0cc8ae86Sav */ 3291*0cc8ae86Sav mc_dimm_info_t * 3292*0cc8ae86Sav mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp) 3293*0cc8ae86Sav { 3294*0cc8ae86Sav char *dimm_name; 3295*0cc8ae86Sav char *serial; 3296*0cc8ae86Sav char *part; 3297*0cc8ae86Sav int dimm; 3298*0cc8ae86Sav int dnamesz = bd_dimmp->bd_dnamesz; 3299*0cc8ae86Sav int sersz = bd_dimmp->bd_serialsz; 3300*0cc8ae86Sav int partsz = bd_dimmp->bd_partnumsz; 3301*0cc8ae86Sav mc_dimm_info_t *dimm_list = NULL; 3302*0cc8ae86Sav mc_dimm_info_t *d; 3303*0cc8ae86Sav 3304*0cc8ae86Sav dimm_name = (char *)(bd_dimmp + 1); 3305*0cc8ae86Sav for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 3306*0cc8ae86Sav 3307*0cc8ae86Sav d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t), 3308*0cc8ae86Sav KM_SLEEP); 3309*0cc8ae86Sav snprintf(d->md_dimmname, dnamesz + 1, "%s", dimm_name); 3310*0cc8ae86Sav serial = dimm_name + dnamesz; 3311*0cc8ae86Sav snprintf(d->md_serial, sersz + 1, "%s", serial); 3312*0cc8ae86Sav part = serial + sersz; 3313*0cc8ae86Sav snprintf(d->md_partnum, partsz + 1, "%s", part); 3314*0cc8ae86Sav 3315*0cc8ae86Sav d->md_next = dimm_list; 3316*0cc8ae86Sav dimm_list = d; 3317*0cc8ae86Sav dimm_name = part + partsz; 3318*0cc8ae86Sav } 3319*0cc8ae86Sav return (dimm_list); 3320*0cc8ae86Sav } 3321*0cc8ae86Sav 3322*0cc8ae86Sav #ifdef DEBUG 3323*0cc8ae86Sav void 3324*0cc8ae86Sav mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz) 3325*0cc8ae86Sav { 3326*0cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 3327*0cc8ae86Sav char serial[MCOPL_MAX_SERIAL + 1]; 3328*0cc8ae86Sav char part[ MCOPL_MAX_PARTNUM + 1]; 3329*0cc8ae86Sav char *b; 3330*0cc8ae86Sav 3331*0cc8ae86Sav b = buf; 3332*0cc8ae86Sav snprintf(dname, dnamesz + 1, "%s", b); 3333*0cc8ae86Sav b += dnamesz; 3334*0cc8ae86Sav snprintf(serial, serialsz + 1, "%s", b); 3335*0cc8ae86Sav b += serialsz; 3336*0cc8ae86Sav snprintf(part, partnumsz + 1, "%s", b); 3337*0cc8ae86Sav printf("DIMM=%s Serial=%s PartNum=%s\n", dname, serial, part); 3338*0cc8ae86Sav } 3339*0cc8ae86Sav 3340*0cc8ae86Sav void 3341*0cc8ae86Sav mc_dump_dimm_info(board_dimm_info_t *bd_dimmp) 3342*0cc8ae86Sav { 3343*0cc8ae86Sav int dimm; 3344*0cc8ae86Sav int dnamesz = bd_dimmp->bd_dnamesz; 3345*0cc8ae86Sav int sersz = bd_dimmp->bd_serialsz; 3346*0cc8ae86Sav int partsz = bd_dimmp->bd_partnumsz; 3347*0cc8ae86Sav char *buf; 3348*0cc8ae86Sav 3349*0cc8ae86Sav printf("Version=%d Board=%02d DIMMs=%d NameSize=%d " 3350*0cc8ae86Sav "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version, 3351*0cc8ae86Sav bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz, 3352*0cc8ae86Sav bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz); 3353*0cc8ae86Sav printf("======================================================\n"); 3354*0cc8ae86Sav 3355*0cc8ae86Sav buf = (char *)(bd_dimmp + 1); 3356*0cc8ae86Sav for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 3357*0cc8ae86Sav mc_dump_dimm(buf, dnamesz, sersz, partsz); 3358*0cc8ae86Sav buf += dnamesz + sersz + partsz; 3359*0cc8ae86Sav } 3360*0cc8ae86Sav printf("======================================================\n"); 3361*0cc8ae86Sav } 3362*0cc8ae86Sav 3363*0cc8ae86Sav 3364*0cc8ae86Sav /* ARGSUSED */ 3365*0cc8ae86Sav static int 3366*0cc8ae86Sav mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 3367*0cc8ae86Sav int *rvalp) 3368*0cc8ae86Sav { 3369*0cc8ae86Sav caddr_t buf; 3370*0cc8ae86Sav uint64_t pa; 3371*0cc8ae86Sav int rv = 0; 3372*0cc8ae86Sav int i; 3373*0cc8ae86Sav uint32_t flags; 3374*0cc8ae86Sav static uint32_t offset = 0; 3375*0cc8ae86Sav 3376*0cc8ae86Sav 3377*0cc8ae86Sav flags = (cmd >> 4) & 0xfffffff; 3378*0cc8ae86Sav 3379*0cc8ae86Sav cmd &= 0xf; 3380*0cc8ae86Sav 3381*0cc8ae86Sav MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags); 3382*0cc8ae86Sav 3383*0cc8ae86Sav if (arg != NULL) { 3384*0cc8ae86Sav if (ddi_copyin((const void *)arg, (void *)&pa, 3385*0cc8ae86Sav sizeof (uint64_t), 0) < 0) { 3386*0cc8ae86Sav rv = EFAULT; 3387*0cc8ae86Sav return (rv); 3388*0cc8ae86Sav } 3389*0cc8ae86Sav buf = NULL; 3390*0cc8ae86Sav } else { 3391*0cc8ae86Sav buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP); 3392*0cc8ae86Sav 3393*0cc8ae86Sav pa = va_to_pa(buf); 3394*0cc8ae86Sav pa += offset; 3395*0cc8ae86Sav 3396*0cc8ae86Sav offset += 64; 3397*0cc8ae86Sav if (offset >= PAGESIZE) 3398*0cc8ae86Sav offset = 0; 3399*0cc8ae86Sav } 3400*0cc8ae86Sav 3401*0cc8ae86Sav switch (cmd) { 3402*0cc8ae86Sav case MCI_CE: 3403*0cc8ae86Sav mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa, 3404*0cc8ae86Sav flags); 3405*0cc8ae86Sav break; 3406*0cc8ae86Sav case MCI_PERM_CE: 3407*0cc8ae86Sav mc_inject_error(MC_INJECT_PERMANENT_CE, pa, 3408*0cc8ae86Sav flags); 3409*0cc8ae86Sav break; 3410*0cc8ae86Sav case MCI_UE: 3411*0cc8ae86Sav mc_inject_error(MC_INJECT_UE, pa, 3412*0cc8ae86Sav flags); 3413*0cc8ae86Sav break; 3414*0cc8ae86Sav case MCI_M_CE: 3415*0cc8ae86Sav mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa, 3416*0cc8ae86Sav flags); 3417*0cc8ae86Sav break; 3418*0cc8ae86Sav case MCI_M_PCE: 3419*0cc8ae86Sav mc_inject_error(MC_INJECT_PERMANENT_MCE, pa, 3420*0cc8ae86Sav flags); 3421*0cc8ae86Sav break; 3422*0cc8ae86Sav case MCI_M_UE: 3423*0cc8ae86Sav mc_inject_error(MC_INJECT_MUE, pa, 3424*0cc8ae86Sav flags); 3425*0cc8ae86Sav break; 3426*0cc8ae86Sav case MCI_CMP: 3427*0cc8ae86Sav mc_inject_error(MC_INJECT_CMPE, pa, 3428*0cc8ae86Sav flags); 3429*0cc8ae86Sav break; 3430*0cc8ae86Sav case MCI_NOP: 3431*0cc8ae86Sav mc_inject_error(MC_INJECT_NOP, pa, flags); 3432*0cc8ae86Sav break; 3433*0cc8ae86Sav case MCI_SHOW_ALL: 3434*0cc8ae86Sav mc_debug_show_all = 1; 3435*0cc8ae86Sav break; 3436*0cc8ae86Sav case MCI_SHOW_NONE: 3437*0cc8ae86Sav mc_debug_show_all = 0; 3438*0cc8ae86Sav break; 3439*0cc8ae86Sav case MCI_ALLOC: 3440*0cc8ae86Sav /* 3441*0cc8ae86Sav * just allocate some kernel memory and never free it 3442*0cc8ae86Sav * 512 MB seems to be the maximum size supported. 3443*0cc8ae86Sav */ 3444*0cc8ae86Sav cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512); 3445*0cc8ae86Sav for (i = 0; i < flags; i++) { 3446*0cc8ae86Sav buf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP); 3447*0cc8ae86Sav cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n", 3448*0cc8ae86Sav (u_longlong_t)buf, (u_longlong_t)va_to_pa(buf)); 3449*0cc8ae86Sav } 3450*0cc8ae86Sav break; 3451*0cc8ae86Sav case MCI_SUSPEND: 3452*0cc8ae86Sav (void) opl_mc_suspend(); 3453*0cc8ae86Sav break; 3454*0cc8ae86Sav case MCI_RESUME: 3455*0cc8ae86Sav (void) opl_mc_resume(); 3456*0cc8ae86Sav break; 3457*0cc8ae86Sav default: 3458*0cc8ae86Sav rv = ENXIO; 3459*0cc8ae86Sav } 3460*0cc8ae86Sav return (rv); 3461*0cc8ae86Sav } 3462*0cc8ae86Sav 3463*0cc8ae86Sav #endif /* DEBUG */ 3464