11772Sjl139090 /* 21772Sjl139090 * CDDL HEADER START 31772Sjl139090 * 41772Sjl139090 * The contents of this file are subject to the terms of the 51772Sjl139090 * Common Development and Distribution License (the "License"). 61772Sjl139090 * You may not use this file except in compliance with the License. 71772Sjl139090 * 81772Sjl139090 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91772Sjl139090 * or http://www.opensolaris.org/os/licensing. 101772Sjl139090 * See the License for the specific language governing permissions 111772Sjl139090 * and limitations under the License. 121772Sjl139090 * 131772Sjl139090 * When distributing Covered Code, include this CDDL HEADER in each 141772Sjl139090 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151772Sjl139090 * If applicable, add the following below this CDDL HEADER, with the 161772Sjl139090 * fields enclosed by brackets "[]" replaced with your own identifying 171772Sjl139090 * information: Portions Copyright [yyyy] [name of copyright owner] 181772Sjl139090 * 191772Sjl139090 * CDDL HEADER END 201772Sjl139090 */ 211772Sjl139090 /* 225979Sjimand * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 232241Shuah * Use is subject to license terms. 242241Shuah */ 252241Shuah /* 266297Sjl139090 * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2008 271772Sjl139090 */ 281772Sjl139090 291772Sjl139090 #pragma ident "%Z%%M% %I% %E% SMI" 301772Sjl139090 311772Sjl139090 #include <sys/types.h> 321772Sjl139090 #include <sys/sysmacros.h> 331772Sjl139090 #include <sys/conf.h> 341772Sjl139090 #include <sys/modctl.h> 351772Sjl139090 #include <sys/stat.h> 361772Sjl139090 #include <sys/async.h> 372241Shuah #include <sys/machcpuvar.h> 381772Sjl139090 #include <sys/machsystm.h> 392214Sav145390 #include <sys/promif.h> 401772Sjl139090 #include <sys/ksynch.h> 411772Sjl139090 #include <sys/ddi.h> 421772Sjl139090 #include <sys/sunddi.h> 435080Swh31274 #include <sys/sunndi.h> 441772Sjl139090 #include <sys/ddifm.h> 451772Sjl139090 #include <sys/fm/protocol.h> 461772Sjl139090 #include <sys/fm/util.h> 471772Sjl139090 #include <sys/kmem.h> 481772Sjl139090 #include <sys/fm/io/opl_mc_fm.h> 491772Sjl139090 #include <sys/memlist.h> 501772Sjl139090 #include <sys/param.h> 512214Sav145390 #include <sys/disp.h> 521772Sjl139090 #include <vm/page.h> 531772Sjl139090 #include <sys/mc-opl.h> 542214Sav145390 #include <sys/opl.h> 552214Sav145390 #include <sys/opl_dimm.h> 562214Sav145390 #include <sys/scfd/scfostoescf.h> 572494Shyw #include <sys/cpu_module.h> 582494Shyw #include <vm/seg_kmem.h> 592494Shyw #include <sys/vmem.h> 602494Shyw #include <vm/hat_sfmmu.h> 612494Shyw #include <sys/vmsystm.h> 622662Shyw #include <sys/membar.h> 63*6693Swh31274 #include <sys/mem.h> 641772Sjl139090 651772Sjl139090 /* 661772Sjl139090 * Function prototypes 671772Sjl139090 */ 681772Sjl139090 static int mc_open(dev_t *, int, int, cred_t *); 691772Sjl139090 static int mc_close(dev_t, int, int, cred_t *); 701772Sjl139090 static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 711772Sjl139090 static int mc_attach(dev_info_t *, ddi_attach_cmd_t); 721772Sjl139090 static int mc_detach(dev_info_t *, ddi_detach_cmd_t); 731772Sjl139090 742214Sav145390 static int mc_poll_init(void); 752214Sav145390 static void mc_poll_fini(void); 761772Sjl139090 static int mc_board_add(mc_opl_t *mcp); 771772Sjl139090 static int mc_board_del(mc_opl_t *mcp); 781772Sjl139090 static int mc_suspend(mc_opl_t *mcp, uint32_t flag); 791772Sjl139090 static int mc_resume(mc_opl_t *mcp, uint32_t flag); 802214Sav145390 int opl_mc_suspend(void); 812214Sav145390 int opl_mc_resume(void); 821772Sjl139090 831772Sjl139090 static void insert_mcp(mc_opl_t *mcp); 841772Sjl139090 static void delete_mcp(mc_opl_t *mcp); 851772Sjl139090 861772Sjl139090 static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr); 871772Sjl139090 882662Shyw static int mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa); 891772Sjl139090 901772Sjl139090 int mc_get_mem_unum(int, uint64_t, char *, int, int *); 912214Sav145390 int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr); 922214Sav145390 int mc_get_mem_offset(uint64_t paddr, uint64_t *offp); 932214Sav145390 int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp); 942214Sav145390 int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 952214Sav145390 int buflen, int *lenp); 962214Sav145390 mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp); 972214Sav145390 mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp); 982214Sav145390 int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank, 992214Sav145390 uint32_t mf_type, uint32_t d_slot); 1002214Sav145390 static void mc_free_dimm_list(mc_dimm_info_t *d); 1011772Sjl139090 static void mc_get_mlist(mc_opl_t *); 1022214Sav145390 static void mc_polling(void); 1032214Sav145390 static int mc_opl_get_physical_board(int); 1042214Sav145390 1055310Sdhain static void mc_clear_rewrite(mc_opl_t *mcp, int i); 1065310Sdhain static void mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state); 107*6693Swh31274 static int mc_scf_log_event(mc_flt_page_t *flt_pag); 1085310Sdhain 1092214Sav145390 #ifdef DEBUG 1102214Sav145390 static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *); 1112214Sav145390 void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz); 1122214Sav145390 void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp); 1132214Sav145390 #endif 1141772Sjl139090 1151772Sjl139090 #pragma weak opl_get_physical_board 1161772Sjl139090 extern int opl_get_physical_board(int); 1172214Sav145390 extern int plat_max_boards(void); 1181772Sjl139090 1191772Sjl139090 /* 1201772Sjl139090 * Configuration data structures 1211772Sjl139090 */ 1221772Sjl139090 static struct cb_ops mc_cb_ops = { 1231772Sjl139090 mc_open, /* open */ 1241772Sjl139090 mc_close, /* close */ 1251772Sjl139090 nulldev, /* strategy */ 1261772Sjl139090 nulldev, /* print */ 1271772Sjl139090 nodev, /* dump */ 1281772Sjl139090 nulldev, /* read */ 1291772Sjl139090 nulldev, /* write */ 1301772Sjl139090 mc_ioctl, /* ioctl */ 1311772Sjl139090 nodev, /* devmap */ 1321772Sjl139090 nodev, /* mmap */ 1331772Sjl139090 nodev, /* segmap */ 1341772Sjl139090 nochpoll, /* poll */ 1351772Sjl139090 ddi_prop_op, /* cb_prop_op */ 1361772Sjl139090 0, /* streamtab */ 1371772Sjl139090 D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 1381772Sjl139090 CB_REV, /* rev */ 1391772Sjl139090 nodev, /* cb_aread */ 1401772Sjl139090 nodev /* cb_awrite */ 1411772Sjl139090 }; 1421772Sjl139090 1431772Sjl139090 static struct dev_ops mc_ops = { 1441772Sjl139090 DEVO_REV, /* rev */ 1451772Sjl139090 0, /* refcnt */ 1461772Sjl139090 ddi_getinfo_1to1, /* getinfo */ 1471772Sjl139090 nulldev, /* identify */ 1481772Sjl139090 nulldev, /* probe */ 1491772Sjl139090 mc_attach, /* attach */ 1501772Sjl139090 mc_detach, /* detach */ 1511772Sjl139090 nulldev, /* reset */ 1521772Sjl139090 &mc_cb_ops, /* cb_ops */ 1531772Sjl139090 (struct bus_ops *)0, /* bus_ops */ 1541772Sjl139090 nulldev /* power */ 1551772Sjl139090 }; 1561772Sjl139090 1571772Sjl139090 /* 1581772Sjl139090 * Driver globals 1591772Sjl139090 */ 1602214Sav145390 1612214Sav145390 static enum { 1626297Sjl139090 MODEL_FF1, 1636297Sjl139090 MODEL_FF2, 1646297Sjl139090 MODEL_DC, 1656297Sjl139090 MODEL_IKKAKU 1662214Sav145390 } plat_model = MODEL_DC; /* The default behaviour is DC */ 1672214Sav145390 1682214Sav145390 static struct plat_model_names { 1692214Sav145390 const char *unit_name; 1702214Sav145390 const char *mem_name; 1712214Sav145390 } model_names[] = { 1722214Sav145390 { "MBU_A", "MEMB" }, 1732214Sav145390 { "MBU_B", "MEMB" }, 1746297Sjl139090 { "CMU", "" }, 1756297Sjl139090 { "MBU_A", "" } 1762214Sav145390 }; 1772214Sav145390 1782214Sav145390 /* 1792214Sav145390 * The DIMM Names for DC platform. 1802214Sav145390 * The index into this table is made up of (bank, dslot), 1812214Sav145390 * Where dslot occupies bits 0-1 and bank occupies 2-4. 1822214Sav145390 */ 1832214Sav145390 static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = { 1842214Sav145390 /* --------CMUnn----------- */ 1852214Sav145390 /* --CS0-----|--CS1------ */ 1862214Sav145390 /* -H-|--L-- | -H- | -L-- */ 1872501Sraghuram "03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */ 1882501Sraghuram "13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */ 1892501Sraghuram "23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */ 1902501Sraghuram "33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */ 1912501Sraghuram "01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */ 1922501Sraghuram "11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */ 1932501Sraghuram "21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */ 1942501Sraghuram "31A", "30A", "31B", "30B" /* Bank 7 (MAC 3 bank 1) */ 1952214Sav145390 }; 1962214Sav145390 1972214Sav145390 /* 1986297Sjl139090 * The DIMM Names for FF1/FF2/IKKAKU platforms. 1992214Sav145390 * The index into this table is made up of (board, bank, dslot), 2002214Sav145390 * Where dslot occupies bits 0-1, bank occupies 2-4 and 2012214Sav145390 * board occupies the bit 5. 2022214Sav145390 */ 2032214Sav145390 static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = { 2042214Sav145390 /* --------CMU0---------- */ 2052214Sav145390 /* --CS0-----|--CS1------ */ 2062214Sav145390 /* -H-|--L-- | -H- | -L-- */ 2072501Sraghuram "03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */ 2082501Sraghuram "01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */ 2092501Sraghuram "13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */ 2102501Sraghuram "11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */ 2112501Sraghuram "23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */ 2122501Sraghuram "21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */ 2132501Sraghuram "33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */ 2142501Sraghuram "31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */ 2152214Sav145390 /* --------CMU1---------- */ 2162214Sav145390 /* --CS0-----|--CS1------ */ 2172214Sav145390 /* -H-|--L-- | -H- | -L-- */ 2182501Sraghuram "43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */ 2192501Sraghuram "41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */ 2202501Sraghuram "53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */ 2212501Sraghuram "51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */ 2222501Sraghuram "63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */ 2232501Sraghuram "61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */ 2242501Sraghuram "73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */ 2252501Sraghuram "71A", "70A", "71B", "70B" /* Bank 7 (MAC 3 bank 1) */ 2262214Sav145390 }; 2272214Sav145390 2282214Sav145390 #define BD_BK_SLOT_TO_INDEX(bd, bk, s) \ 2292214Sav145390 (((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03)) 2302214Sav145390 2312214Sav145390 #define INDEX_TO_BANK(i) (((i) & 0x1C) >> 2) 2322214Sav145390 #define INDEX_TO_SLOT(i) ((i) & 0x03) 2332214Sav145390 2343045Sav145390 #define SLOT_TO_CS(slot) ((slot & 0x3) >> 1) 2353045Sav145390 2362214Sav145390 /* Isolation unit size is 64 MB */ 2372214Sav145390 #define MC_ISOLATION_BSIZE (64 * 1024 * 1024) 2382214Sav145390 2392214Sav145390 #define MC_MAX_SPEEDS 7 2402214Sav145390 2412214Sav145390 typedef struct { 2422214Sav145390 uint32_t mc_speeds; 2432214Sav145390 uint32_t mc_period; 2442214Sav145390 } mc_scan_speed_t; 2452214Sav145390 2462214Sav145390 #define MC_CNTL_SPEED_SHIFT 26 2472214Sav145390 2482867Shyw /* 2492867Shyw * In mirror mode, we normalized the bank idx to "even" since 2502867Shyw * the HW treats them as one unit w.r.t programming. 2512867Shyw * This bank index will be the "effective" bank index. 2522867Shyw * All mirrored bank state info on mc_period, mc_speedup_period 2532867Shyw * will be stored in the even bank structure to avoid code duplication. 2542867Shyw */ 2552867Shyw #define MIRROR_IDX(bankidx) (bankidx & ~1) 2562867Shyw 2572214Sav145390 static mc_scan_speed_t mc_scan_speeds[MC_MAX_SPEEDS] = { 2582214Sav145390 {0x6 << MC_CNTL_SPEED_SHIFT, 0}, 2592214Sav145390 {0x5 << MC_CNTL_SPEED_SHIFT, 32}, 2602214Sav145390 {0x4 << MC_CNTL_SPEED_SHIFT, 64}, 2612214Sav145390 {0x3 << MC_CNTL_SPEED_SHIFT, 128}, 2622214Sav145390 {0x2 << MC_CNTL_SPEED_SHIFT, 256}, 2632214Sav145390 {0x1 << MC_CNTL_SPEED_SHIFT, 512}, 2642214Sav145390 {0x0 << MC_CNTL_SPEED_SHIFT, 1024} 2652214Sav145390 }; 2662214Sav145390 2672214Sav145390 static uint32_t mc_max_speed = (0x6 << 26); 2682214Sav145390 2692214Sav145390 int mc_isolation_bsize = MC_ISOLATION_BSIZE; 2702214Sav145390 int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC; 2712214Sav145390 int mc_max_scf_retry = 16; 2722214Sav145390 int mc_max_scf_logs = 64; 2732214Sav145390 int mc_max_errlog_processed = BANKNUM_PER_SB*2; 2742214Sav145390 int mc_scan_period = 12 * 60 * 60; /* 12 hours period */ 2752214Sav145390 int mc_max_rewrite_loop = 100; 2762214Sav145390 int mc_rewrite_delay = 10; 2772214Sav145390 /* 2782214Sav145390 * it takes SCF about 300 m.s. to process a requst. We can bail out 2792214Sav145390 * if it is busy. It does not pay to wait for it too long. 2802214Sav145390 */ 2812214Sav145390 int mc_max_scf_loop = 2; 2822214Sav145390 int mc_scf_delay = 100; 2832214Sav145390 int mc_pce_dropped = 0; 2842214Sav145390 int mc_poll_priority = MINCLSYSPRI; 2855310Sdhain int mc_max_rewrite_retry = 6 * 60; 2862214Sav145390 2872214Sav145390 2882214Sav145390 /* 2893152Sav145390 * Mutex hierarchy in mc-opl 2902214Sav145390 * If both mcmutex and mc_lock must be held, 2912214Sav145390 * mcmutex must be acquired first, and then mc_lock. 2922214Sav145390 */ 2932214Sav145390 2941772Sjl139090 static kmutex_t mcmutex; 2952214Sav145390 mc_opl_t *mc_instances[OPL_MAX_BOARDS]; 2962214Sav145390 2972214Sav145390 static kmutex_t mc_polling_lock; 2982214Sav145390 static kcondvar_t mc_polling_cv; 2992214Sav145390 static kcondvar_t mc_poll_exit_cv; 3002214Sav145390 static int mc_poll_cmd = 0; 3012214Sav145390 static int mc_pollthr_running = 0; 3022214Sav145390 int mc_timeout_period = 0; /* this is in m.s. */ 3031772Sjl139090 void *mc_statep; 3041772Sjl139090 3051772Sjl139090 #ifdef DEBUG 3061809Shyw int oplmc_debug = 0; 3071772Sjl139090 #endif 3081772Sjl139090 3092214Sav145390 static int mc_debug_show_all = 0; 3101772Sjl139090 3111772Sjl139090 extern struct mod_ops mod_driverops; 3121772Sjl139090 3131772Sjl139090 static struct modldrv modldrv = { 3141772Sjl139090 &mod_driverops, /* module type, this one is a driver */ 3152214Sav145390 "OPL Memory-controller %I%", /* module name */ 3161772Sjl139090 &mc_ops, /* driver ops */ 3171772Sjl139090 }; 3181772Sjl139090 3191772Sjl139090 static struct modlinkage modlinkage = { 3201772Sjl139090 MODREV_1, /* rev */ 3211772Sjl139090 (void *)&modldrv, 3221772Sjl139090 NULL 3231772Sjl139090 }; 3241772Sjl139090 3251772Sjl139090 #pragma weak opl_get_mem_unum 3262214Sav145390 #pragma weak opl_get_mem_sid 3272214Sav145390 #pragma weak opl_get_mem_offset 3282214Sav145390 #pragma weak opl_get_mem_addr 3292214Sav145390 3301772Sjl139090 extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *); 3312214Sav145390 extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp); 3322214Sav145390 extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp); 3332214Sav145390 extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset, 3342214Sav145390 uint64_t *paddr); 3352214Sav145390 3361772Sjl139090 3371772Sjl139090 /* 3381772Sjl139090 * pseudo-mc node portid format 3391772Sjl139090 * 3401772Sjl139090 * [10] = 0 3411772Sjl139090 * [9] = 1 3421772Sjl139090 * [8] = LSB_ID[4] = 0 3431772Sjl139090 * [7:4] = LSB_ID[3:0] 3441772Sjl139090 * [3:0] = 0 3451772Sjl139090 * 3461772Sjl139090 */ 3471772Sjl139090 3481772Sjl139090 /* 3491772Sjl139090 * These are the module initialization routines. 3501772Sjl139090 */ 3511772Sjl139090 int 3521772Sjl139090 _init(void) 3531772Sjl139090 { 3542214Sav145390 int error; 3552214Sav145390 int plen; 3562214Sav145390 char model[20]; 3572214Sav145390 pnode_t node; 3581772Sjl139090 3591772Sjl139090 3601772Sjl139090 if ((error = ddi_soft_state_init(&mc_statep, 3611772Sjl139090 sizeof (mc_opl_t), 1)) != 0) 3621772Sjl139090 return (error); 3631772Sjl139090 3642214Sav145390 if ((error = mc_poll_init()) != 0) { 3652214Sav145390 ddi_soft_state_fini(&mc_statep); 3662214Sav145390 return (error); 3672214Sav145390 } 3682214Sav145390 3691772Sjl139090 mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL); 3701772Sjl139090 if (&opl_get_mem_unum) 3711772Sjl139090 opl_get_mem_unum = mc_get_mem_unum; 3722214Sav145390 if (&opl_get_mem_sid) 3732214Sav145390 opl_get_mem_sid = mc_get_mem_sid; 3742214Sav145390 if (&opl_get_mem_offset) 3752214Sav145390 opl_get_mem_offset = mc_get_mem_offset; 3762214Sav145390 if (&opl_get_mem_addr) 3772214Sav145390 opl_get_mem_addr = mc_get_mem_addr; 3782214Sav145390 3792214Sav145390 node = prom_rootnode(); 3802214Sav145390 plen = prom_getproplen(node, "model"); 3812214Sav145390 3822214Sav145390 if (plen > 0 && plen < sizeof (model)) { 3832214Sav145390 (void) prom_getprop(node, "model", model); 3842214Sav145390 model[plen] = '\0'; 3852214Sav145390 if (strcmp(model, "FF1") == 0) 3862214Sav145390 plat_model = MODEL_FF1; 3872214Sav145390 else if (strcmp(model, "FF2") == 0) 3882214Sav145390 plat_model = MODEL_FF2; 3892214Sav145390 else if (strncmp(model, "DC", 2) == 0) 3902214Sav145390 plat_model = MODEL_DC; 3916297Sjl139090 else if (strcmp(model, "IKKAKU") == 0) 3926297Sjl139090 plat_model = MODEL_IKKAKU; 3932214Sav145390 } 3941772Sjl139090 3951772Sjl139090 error = mod_install(&modlinkage); 3961772Sjl139090 if (error != 0) { 3971772Sjl139090 if (&opl_get_mem_unum) 3981772Sjl139090 opl_get_mem_unum = NULL; 3992214Sav145390 if (&opl_get_mem_sid) 4002214Sav145390 opl_get_mem_sid = NULL; 4012214Sav145390 if (&opl_get_mem_offset) 4022214Sav145390 opl_get_mem_offset = NULL; 4032214Sav145390 if (&opl_get_mem_addr) 4042214Sav145390 opl_get_mem_addr = NULL; 4051772Sjl139090 mutex_destroy(&mcmutex); 4062214Sav145390 mc_poll_fini(); 4071772Sjl139090 ddi_soft_state_fini(&mc_statep); 4081772Sjl139090 } 4091772Sjl139090 return (error); 4101772Sjl139090 } 4111772Sjl139090 4121772Sjl139090 int 4131772Sjl139090 _fini(void) 4141772Sjl139090 { 4151772Sjl139090 int error; 4161772Sjl139090 4171772Sjl139090 if ((error = mod_remove(&modlinkage)) != 0) 4181772Sjl139090 return (error); 4191772Sjl139090 4201772Sjl139090 if (&opl_get_mem_unum) 4211772Sjl139090 opl_get_mem_unum = NULL; 4222214Sav145390 if (&opl_get_mem_sid) 4232214Sav145390 opl_get_mem_sid = NULL; 4242214Sav145390 if (&opl_get_mem_offset) 4252214Sav145390 opl_get_mem_offset = NULL; 4262214Sav145390 if (&opl_get_mem_addr) 4272214Sav145390 opl_get_mem_addr = NULL; 4282214Sav145390 4292214Sav145390 mutex_destroy(&mcmutex); 4302214Sav145390 mc_poll_fini(); 4311772Sjl139090 ddi_soft_state_fini(&mc_statep); 4321772Sjl139090 4331772Sjl139090 return (0); 4341772Sjl139090 } 4351772Sjl139090 4361772Sjl139090 int 4371772Sjl139090 _info(struct modinfo *modinfop) 4381772Sjl139090 { 4391772Sjl139090 return (mod_info(&modlinkage, modinfop)); 4401772Sjl139090 } 4411772Sjl139090 4422214Sav145390 static void 4432214Sav145390 mc_polling_thread() 4442214Sav145390 { 4452214Sav145390 mutex_enter(&mc_polling_lock); 4462214Sav145390 mc_pollthr_running = 1; 4472214Sav145390 while (!(mc_poll_cmd & MC_POLL_EXIT)) { 4482214Sav145390 mc_polling(); 4492214Sav145390 cv_timedwait(&mc_polling_cv, &mc_polling_lock, 4502214Sav145390 ddi_get_lbolt() + mc_timeout_period); 4512214Sav145390 } 4522214Sav145390 mc_pollthr_running = 0; 4532214Sav145390 4542214Sav145390 /* 4552214Sav145390 * signal if any one is waiting for this thread to exit. 4562214Sav145390 */ 4572214Sav145390 cv_signal(&mc_poll_exit_cv); 4582214Sav145390 mutex_exit(&mc_polling_lock); 4592214Sav145390 thread_exit(); 4602214Sav145390 /* NOTREACHED */ 4612214Sav145390 } 4622214Sav145390 4632214Sav145390 static int 4642214Sav145390 mc_poll_init() 4652214Sav145390 { 4662214Sav145390 mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL); 4672214Sav145390 cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL); 4682214Sav145390 cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL); 4692214Sav145390 return (0); 4702214Sav145390 } 4712214Sav145390 4722214Sav145390 static void 4732214Sav145390 mc_poll_fini() 4742214Sav145390 { 4752214Sav145390 mutex_enter(&mc_polling_lock); 4762214Sav145390 if (mc_pollthr_running) { 4772214Sav145390 mc_poll_cmd = MC_POLL_EXIT; 4782214Sav145390 cv_signal(&mc_polling_cv); 4792214Sav145390 while (mc_pollthr_running) { 4802214Sav145390 cv_wait(&mc_poll_exit_cv, &mc_polling_lock); 4812214Sav145390 } 4822214Sav145390 } 4832214Sav145390 mutex_exit(&mc_polling_lock); 4842214Sav145390 mutex_destroy(&mc_polling_lock); 4852214Sav145390 cv_destroy(&mc_polling_cv); 4862214Sav145390 cv_destroy(&mc_poll_exit_cv); 4872214Sav145390 } 4882214Sav145390 4891772Sjl139090 static int 4901772Sjl139090 mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 4911772Sjl139090 { 4921772Sjl139090 mc_opl_t *mcp; 4931772Sjl139090 int instance; 4942214Sav145390 int rv; 4951772Sjl139090 4961772Sjl139090 /* get the instance of this devi */ 4971772Sjl139090 instance = ddi_get_instance(devi); 4981772Sjl139090 4991772Sjl139090 switch (cmd) { 5001772Sjl139090 case DDI_ATTACH: 5011772Sjl139090 break; 5021772Sjl139090 case DDI_RESUME: 5031772Sjl139090 mcp = ddi_get_soft_state(mc_statep, instance); 5042214Sav145390 rv = mc_resume(mcp, MC_DRIVER_SUSPENDED); 5052214Sav145390 return (rv); 5061772Sjl139090 default: 5071772Sjl139090 return (DDI_FAILURE); 5081772Sjl139090 } 5091772Sjl139090 5101772Sjl139090 if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS) 5111772Sjl139090 return (DDI_FAILURE); 5121772Sjl139090 513*6693Swh31274 if (ddi_create_minor_node(devi, "mc-opl", S_IFCHR, instance, 514*6693Swh31274 "ddi_mem_ctrl", 0) != DDI_SUCCESS) { 515*6693Swh31274 MC_LOG("mc_attach: create_minor_node failed\n"); 516*6693Swh31274 return (DDI_FAILURE); 517*6693Swh31274 } 518*6693Swh31274 5191772Sjl139090 if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 5201772Sjl139090 goto bad; 5211772Sjl139090 } 5221772Sjl139090 5232214Sav145390 if (mc_timeout_period == 0) { 5242214Sav145390 mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi, 5255080Swh31274 DDI_PROP_DONTPASS, "mc-timeout-interval-sec", 5265080Swh31274 mc_patrol_interval_sec); 5275080Swh31274 mc_timeout_period = drv_usectohz(1000000 * 5285080Swh31274 mc_patrol_interval_sec / OPL_MAX_BOARDS); 5292214Sav145390 } 5302214Sav145390 5311772Sjl139090 /* set informations in mc state */ 5321772Sjl139090 mcp->mc_dip = devi; 5331772Sjl139090 5341772Sjl139090 if (mc_board_add(mcp)) 5351772Sjl139090 goto bad; 5361772Sjl139090 5371772Sjl139090 insert_mcp(mcp); 5382214Sav145390 5392214Sav145390 /* 5402214Sav145390 * Start the polling thread if it is not running already. 5412214Sav145390 */ 5422214Sav145390 mutex_enter(&mc_polling_lock); 5432214Sav145390 if (!mc_pollthr_running) { 5442214Sav145390 (void) thread_create(NULL, 0, (void (*)())mc_polling_thread, 5455080Swh31274 NULL, 0, &p0, TS_RUN, mc_poll_priority); 5462214Sav145390 } 5472214Sav145390 mutex_exit(&mc_polling_lock); 5481772Sjl139090 ddi_report_dev(devi); 5491772Sjl139090 5501772Sjl139090 return (DDI_SUCCESS); 5511772Sjl139090 5521772Sjl139090 bad: 553*6693Swh31274 ddi_remove_minor_node(devi, NULL); 5541772Sjl139090 ddi_soft_state_free(mc_statep, instance); 5551772Sjl139090 return (DDI_FAILURE); 5561772Sjl139090 } 5571772Sjl139090 5581772Sjl139090 /* ARGSUSED */ 5591772Sjl139090 static int 5601772Sjl139090 mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 5611772Sjl139090 { 5622214Sav145390 int rv; 5631772Sjl139090 int instance; 5641772Sjl139090 mc_opl_t *mcp; 5651772Sjl139090 5661772Sjl139090 /* get the instance of this devi */ 5671772Sjl139090 instance = ddi_get_instance(devi); 5681772Sjl139090 if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 5691772Sjl139090 return (DDI_FAILURE); 5701772Sjl139090 } 5711772Sjl139090 5721772Sjl139090 switch (cmd) { 5731772Sjl139090 case DDI_SUSPEND: 5742214Sav145390 rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED); 5752214Sav145390 return (rv); 5761772Sjl139090 case DDI_DETACH: 5771772Sjl139090 break; 5781772Sjl139090 default: 5791772Sjl139090 return (DDI_FAILURE); 5801772Sjl139090 } 5811772Sjl139090 5822214Sav145390 delete_mcp(mcp); 5831772Sjl139090 if (mc_board_del(mcp) != DDI_SUCCESS) { 5841772Sjl139090 return (DDI_FAILURE); 5851772Sjl139090 } 5861772Sjl139090 587*6693Swh31274 ddi_remove_minor_node(devi, NULL); 588*6693Swh31274 5891772Sjl139090 /* free up the soft state */ 5901772Sjl139090 ddi_soft_state_free(mc_statep, instance); 5911772Sjl139090 5921772Sjl139090 return (DDI_SUCCESS); 5931772Sjl139090 } 5941772Sjl139090 5951772Sjl139090 /* ARGSUSED */ 5961772Sjl139090 static int 5971772Sjl139090 mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 5981772Sjl139090 { 5991772Sjl139090 return (0); 6001772Sjl139090 } 6011772Sjl139090 6021772Sjl139090 /* ARGSUSED */ 6031772Sjl139090 static int 6041772Sjl139090 mc_close(dev_t devp, int flag, int otyp, cred_t *credp) 6051772Sjl139090 { 6061772Sjl139090 return (0); 6071772Sjl139090 } 6081772Sjl139090 6091772Sjl139090 /* ARGSUSED */ 6101772Sjl139090 static int 6111772Sjl139090 mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 6121772Sjl139090 int *rvalp) 6131772Sjl139090 { 614*6693Swh31274 mc_flt_page_t flt_page; 615*6693Swh31274 616*6693Swh31274 if (cmd == MCIOC_FAULT_PAGE) { 617*6693Swh31274 if (arg == NULL) 618*6693Swh31274 return (EINVAL); 619*6693Swh31274 620*6693Swh31274 if (ddi_copyin((const void *)arg, (void *)&flt_page, 621*6693Swh31274 sizeof (mc_flt_page_t), 0) < 0) 622*6693Swh31274 return (EFAULT); 623*6693Swh31274 624*6693Swh31274 return (mc_scf_log_event(&flt_page)); 625*6693Swh31274 } 6262214Sav145390 #ifdef DEBUG 6272214Sav145390 return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp)); 6282214Sav145390 #else 629*6693Swh31274 return (ENOTTY); 6302214Sav145390 #endif 6311772Sjl139090 } 6321772Sjl139090 6331772Sjl139090 /* 6341772Sjl139090 * PA validity check: 6352662Shyw * This function return 1 if the PA is a valid PA 6362662Shyw * in the running Solaris instance i.e. in physinstall 6372662Shyw * Otherwise, return 0. 6381772Sjl139090 */ 6391772Sjl139090 6401772Sjl139090 /* ARGSUSED */ 6411772Sjl139090 static int 6421772Sjl139090 pa_is_valid(mc_opl_t *mcp, uint64_t addr) 6431772Sjl139090 { 6441772Sjl139090 if (mcp->mlist == NULL) 6451772Sjl139090 mc_get_mlist(mcp); 6461772Sjl139090 6471772Sjl139090 if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) { 6481772Sjl139090 return (1); 6491772Sjl139090 } 6501772Sjl139090 return (0); 6511772Sjl139090 } 6521772Sjl139090 6531772Sjl139090 /* 6541772Sjl139090 * mac-pa translation routines. 6551772Sjl139090 * 6561772Sjl139090 * Input: mc driver state, (LSB#, Bank#, DIMM address) 6571772Sjl139090 * Output: physical address 6581772Sjl139090 * 6591772Sjl139090 * Valid - return value: 0 6601772Sjl139090 * Invalid - return value: -1 6611772Sjl139090 */ 6621772Sjl139090 static int 6631772Sjl139090 mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa) 6641772Sjl139090 { 6651772Sjl139090 int i; 6661772Sjl139090 uint64_t pa_offset = 0; 6671772Sjl139090 int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1; 6681772Sjl139090 int bank = maddr->ma_bank; 6691772Sjl139090 mc_addr_t maddr1; 6701772Sjl139090 int bank0, bank1; 6711772Sjl139090 6721772Sjl139090 MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank, 6735080Swh31274 maddr->ma_dimm_addr); 6741772Sjl139090 6751772Sjl139090 /* loc validity check */ 6761772Sjl139090 ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd); 6771772Sjl139090 ASSERT(bank >= 0 && OPL_BANK_MAX > bank); 6781772Sjl139090 6791772Sjl139090 /* Do translation */ 6801772Sjl139090 for (i = 0; i < PA_BITS_FOR_MAC; i++) { 6811772Sjl139090 int pa_bit = 0; 6821772Sjl139090 int mc_bit = mcp->mc_trans_table[cs][i]; 6831772Sjl139090 if (mc_bit < MC_ADDRESS_BITS) { 6841772Sjl139090 pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1; 6851772Sjl139090 } else if (mc_bit == MP_NONE) { 6861772Sjl139090 pa_bit = 0; 6871772Sjl139090 } else if (mc_bit == MP_BANK_0) { 6881772Sjl139090 pa_bit = bank & 1; 6891772Sjl139090 } else if (mc_bit == MP_BANK_1) { 6901772Sjl139090 pa_bit = (bank >> 1) & 1; 6911772Sjl139090 } else if (mc_bit == MP_BANK_2) { 6921772Sjl139090 pa_bit = (bank >> 2) & 1; 6931772Sjl139090 } 6941772Sjl139090 pa_offset |= ((uint64_t)pa_bit) << i; 6951772Sjl139090 } 6961772Sjl139090 *pa = mcp->mc_start_address + pa_offset; 6971772Sjl139090 MC_LOG("pa = %lx\n", *pa); 6981772Sjl139090 6991772Sjl139090 if (pa_to_maddr(mcp, *pa, &maddr1) == -1) { 7002214Sav145390 cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to " 7012214Sav145390 "convert PA %lx\n", maddr->ma_bd, bank, 7022214Sav145390 maddr->ma_dimm_addr, *pa); 7031772Sjl139090 return (-1); 7041772Sjl139090 } 7051772Sjl139090 7062214Sav145390 /* 7072214Sav145390 * In mirror mode, PA is always translated to the even bank. 7082214Sav145390 */ 7091772Sjl139090 if (IS_MIRROR(mcp, maddr->ma_bank)) { 7101772Sjl139090 bank0 = maddr->ma_bank & ~(1); 7111772Sjl139090 bank1 = maddr1.ma_bank & ~(1); 7121772Sjl139090 } else { 7131772Sjl139090 bank0 = maddr->ma_bank; 7141772Sjl139090 bank1 = maddr1.ma_bank; 7151772Sjl139090 } 7161772Sjl139090 /* 7171772Sjl139090 * there is no need to check ma_bd because it is generated from 7181772Sjl139090 * mcp. They are the same. 7191772Sjl139090 */ 7205080Swh31274 if ((bank0 == bank1) && (maddr->ma_dimm_addr == 7215080Swh31274 maddr1.ma_dimm_addr)) { 7221772Sjl139090 return (0); 7231772Sjl139090 } else { 724*6693Swh31274 MC_LOG("Translation error source /LSB%d/B%d/%x, " 7255080Swh31274 "PA %lx, target /LSB%d/B%d/%x\n", maddr->ma_bd, bank, 7265080Swh31274 maddr->ma_dimm_addr, *pa, maddr1.ma_bd, maddr1.ma_bank, 7275080Swh31274 maddr1.ma_dimm_addr); 7281772Sjl139090 return (-1); 7291772Sjl139090 } 7301772Sjl139090 } 7311772Sjl139090 7321772Sjl139090 /* 7331772Sjl139090 * PA to CS (used by pa_to_maddr). 7341772Sjl139090 */ 7351772Sjl139090 static int 7361772Sjl139090 pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset) 7371772Sjl139090 { 7381772Sjl139090 int i; 7392662Shyw int cs = 1; 7401772Sjl139090 7411772Sjl139090 for (i = 0; i < PA_BITS_FOR_MAC; i++) { 7421772Sjl139090 /* MAC address bit<29> is arranged on the same PA bit */ 7431772Sjl139090 /* on both table. So we may use any table. */ 7441772Sjl139090 if (mcp->mc_trans_table[0][i] == CS_SHIFT) { 7451772Sjl139090 cs = (pa_offset >> i) & 1; 7461772Sjl139090 break; 7471772Sjl139090 } 7481772Sjl139090 } 7491772Sjl139090 return (cs); 7501772Sjl139090 } 7511772Sjl139090 7521772Sjl139090 /* 7531772Sjl139090 * PA to DIMM (used by pa_to_maddr). 7541772Sjl139090 */ 7551772Sjl139090 /* ARGSUSED */ 7561772Sjl139090 static uint32_t 7571772Sjl139090 pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset) 7581772Sjl139090 { 7591772Sjl139090 int i; 7601772Sjl139090 int cs = pa_to_cs(mcp, pa_offset); 7611772Sjl139090 uint32_t dimm_addr = 0; 7621772Sjl139090 7631772Sjl139090 for (i = 0; i < PA_BITS_FOR_MAC; i++) { 7641772Sjl139090 int pa_bit_value = (pa_offset >> i) & 1; 7651772Sjl139090 int mc_bit = mcp->mc_trans_table[cs][i]; 7661772Sjl139090 if (mc_bit < MC_ADDRESS_BITS) { 7671772Sjl139090 dimm_addr |= pa_bit_value << mc_bit; 7681772Sjl139090 } 7691772Sjl139090 } 7702662Shyw dimm_addr |= cs << CS_SHIFT; 7711772Sjl139090 return (dimm_addr); 7721772Sjl139090 } 7731772Sjl139090 7741772Sjl139090 /* 7751772Sjl139090 * PA to Bank (used by pa_to_maddr). 7761772Sjl139090 */ 7771772Sjl139090 static int 7781772Sjl139090 pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset) 7791772Sjl139090 { 7801772Sjl139090 int i; 7811772Sjl139090 int cs = pa_to_cs(mcp, pa_offset); 7821772Sjl139090 int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT]; 7831772Sjl139090 7841772Sjl139090 7851772Sjl139090 for (i = 0; i < PA_BITS_FOR_MAC; i++) { 7861772Sjl139090 int pa_bit_value = (pa_offset >> i) & 1; 7871772Sjl139090 int mc_bit = mcp->mc_trans_table[cs][i]; 7881772Sjl139090 switch (mc_bit) { 7891772Sjl139090 case MP_BANK_0: 7901772Sjl139090 bankno |= pa_bit_value; 7911772Sjl139090 break; 7921772Sjl139090 case MP_BANK_1: 7931772Sjl139090 bankno |= pa_bit_value << 1; 7941772Sjl139090 break; 7951772Sjl139090 case MP_BANK_2: 7961772Sjl139090 bankno |= pa_bit_value << 2; 7971772Sjl139090 break; 7981772Sjl139090 } 7991772Sjl139090 } 8001772Sjl139090 8011772Sjl139090 return (bankno); 8021772Sjl139090 } 8031772Sjl139090 8041772Sjl139090 /* 8051772Sjl139090 * PA to MAC address translation 8061772Sjl139090 * 8071772Sjl139090 * Input: MAC driver state, physicall adress 8081772Sjl139090 * Output: LSB#, Bank id, mac address 8091772Sjl139090 * 8101772Sjl139090 * Valid - return value: 0 8111772Sjl139090 * Invalid - return value: -1 8121772Sjl139090 */ 8131772Sjl139090 8141772Sjl139090 int 8151772Sjl139090 pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr) 8161772Sjl139090 { 8171772Sjl139090 uint64_t pa_offset; 8181772Sjl139090 8192662Shyw if (!mc_rangecheck_pa(mcp, pa)) 8201772Sjl139090 return (-1); 8211772Sjl139090 8221772Sjl139090 /* Do translation */ 8231772Sjl139090 pa_offset = pa - mcp->mc_start_address; 8241772Sjl139090 8251772Sjl139090 maddr->ma_bd = mcp->mc_board_num; 8263045Sav145390 maddr->ma_phys_bd = mcp->mc_phys_board_num; 8271772Sjl139090 maddr->ma_bank = pa_to_bank(mcp, pa_offset); 8281772Sjl139090 maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset); 8295080Swh31274 MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", pa_offset, maddr->ma_bd, 8305080Swh31274 maddr->ma_bank, maddr->ma_dimm_addr); 8311772Sjl139090 return (0); 8321772Sjl139090 } 8331772Sjl139090 8342214Sav145390 /* 8352214Sav145390 * UNUM format for DC is "/CMUnn/MEMxyZ", where 8362214Sav145390 * nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3. 8372214Sav145390 * x = MAC 0..3 8382214Sav145390 * y = 0..3 (slot info). 8392214Sav145390 * Z = 'A' or 'B' 8402214Sav145390 * 8412214Sav145390 * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where 8422214Sav145390 * x = 0..3 (MEMB number) 8432214Sav145390 * y = 0..3 (slot info). 8442214Sav145390 * Z = 'A' or 'B' 8452214Sav145390 * 8466297Sjl139090 * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ", where 8472214Sav145390 * x = 0..7 (MEMB number) 8482214Sav145390 * y = 0..3 (slot info). 8492214Sav145390 * Z = 'A' or 'B' 8506297Sjl139090 * 8516297Sjl139090 * UNUM format for IKKAKU is "/MBU_A/MEMyZ", where 8526297Sjl139090 * y = 0..3 (slot info). 8536297Sjl139090 * Z = 'A' or 'B' 8546297Sjl139090 * 8552214Sav145390 */ 8562214Sav145390 int 8573045Sav145390 mc_set_mem_unum(char *buf, int buflen, int sb, int bank, 8582214Sav145390 uint32_t mf_type, uint32_t d_slot) 8592214Sav145390 { 8602214Sav145390 char *dimmnm; 8612214Sav145390 char memb_num; 8623045Sav145390 int cs; 8632214Sav145390 int i; 8643045Sav145390 int j; 8653045Sav145390 8663045Sav145390 cs = SLOT_TO_CS(d_slot); 8672214Sav145390 8686297Sjl139090 switch (plat_model) { 8696297Sjl139090 case MODEL_DC: 8705275Stsien if (mf_type == FLT_TYPE_INTERMITTENT_CE || 8715275Stsien mf_type == FLT_TYPE_PERMANENT_CE) { 8722214Sav145390 i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 8732214Sav145390 dimmnm = mc_dc_dimm_unum_table[i]; 8742214Sav145390 snprintf(buf, buflen, "/%s%02d/MEM%s", 8752214Sav145390 model_names[plat_model].unit_name, sb, dimmnm); 8762214Sav145390 } else { 8772214Sav145390 i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 8783045Sav145390 j = (cs == 0) ? i : i + 2; 8793045Sav145390 snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s", 8802214Sav145390 model_names[plat_model].unit_name, sb, 8813045Sav145390 mc_dc_dimm_unum_table[j], 8823045Sav145390 mc_dc_dimm_unum_table[j + 1]); 8832214Sav145390 } 8846297Sjl139090 break; 8856297Sjl139090 case MODEL_FF1: 8866297Sjl139090 case MODEL_FF2: 8875275Stsien if (mf_type == FLT_TYPE_INTERMITTENT_CE || 8885275Stsien mf_type == FLT_TYPE_PERMANENT_CE) { 8893045Sav145390 i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 8902214Sav145390 dimmnm = mc_ff_dimm_unum_table[i]; 8912214Sav145390 memb_num = dimmnm[0]; 8922214Sav145390 snprintf(buf, buflen, "/%s/%s%c/MEM%s", 8932214Sav145390 model_names[plat_model].unit_name, 8942214Sav145390 model_names[plat_model].mem_name, 8952214Sav145390 memb_num, &dimmnm[1]); 8962214Sav145390 } else { 8972214Sav145390 i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 8983045Sav145390 j = (cs == 0) ? i : i + 2; 8992214Sav145390 memb_num = mc_ff_dimm_unum_table[i][0], 9005080Swh31274 snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s", 9012214Sav145390 model_names[plat_model].unit_name, 9022214Sav145390 model_names[plat_model].mem_name, memb_num, 9033045Sav145390 &mc_ff_dimm_unum_table[j][1], 9043045Sav145390 &mc_ff_dimm_unum_table[j + 1][1]); 9052214Sav145390 } 9066297Sjl139090 break; 9076297Sjl139090 case MODEL_IKKAKU: 9086297Sjl139090 if (mf_type == FLT_TYPE_INTERMITTENT_CE || 9096297Sjl139090 mf_type == FLT_TYPE_PERMANENT_CE) { 9106297Sjl139090 i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 9116297Sjl139090 dimmnm = mc_ff_dimm_unum_table[i]; 9126297Sjl139090 snprintf(buf, buflen, "/%s/MEM%s", 9136297Sjl139090 model_names[plat_model].unit_name, &dimmnm[1]); 9146297Sjl139090 } else { 9156297Sjl139090 i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 9166297Sjl139090 j = (cs == 0) ? i : i + 2; 9176297Sjl139090 memb_num = mc_ff_dimm_unum_table[i][0], 9186297Sjl139090 snprintf(buf, buflen, "/%s/MEM%s MEM%s", 9196297Sjl139090 model_names[plat_model].unit_name, 9206297Sjl139090 &mc_ff_dimm_unum_table[j][1], 9216297Sjl139090 &mc_ff_dimm_unum_table[j + 1][1]); 9226297Sjl139090 } 9236297Sjl139090 break; 9246297Sjl139090 default: 9256297Sjl139090 return (-1); 9262214Sav145390 } 9272214Sav145390 return (0); 9282214Sav145390 } 9292214Sav145390 9301772Sjl139090 static void 9311772Sjl139090 mc_ereport_post(mc_aflt_t *mc_aflt) 9321772Sjl139090 { 9331772Sjl139090 char buf[FM_MAX_CLASS]; 9341772Sjl139090 char device_path[MAXPATHLEN]; 9352214Sav145390 char sid[MAXPATHLEN]; 9361772Sjl139090 nv_alloc_t *nva = NULL; 9371772Sjl139090 nvlist_t *ereport, *detector, *resource; 9381772Sjl139090 errorq_elem_t *eqep; 9391772Sjl139090 int nflts; 9401772Sjl139090 mc_flt_stat_t *flt_stat; 9412214Sav145390 int i, n; 9422214Sav145390 int blen = MAXPATHLEN; 9432214Sav145390 char *p, *s = NULL; 9441772Sjl139090 uint32_t values[2], synd[2], dslot[2]; 9452214Sav145390 uint64_t offset = (uint64_t)-1; 9462214Sav145390 int ret = -1; 9471772Sjl139090 9481772Sjl139090 if (panicstr) { 9491772Sjl139090 eqep = errorq_reserve(ereport_errorq); 9501772Sjl139090 if (eqep == NULL) 9511772Sjl139090 return; 9521772Sjl139090 ereport = errorq_elem_nvl(ereport_errorq, eqep); 9531772Sjl139090 nva = errorq_elem_nva(ereport_errorq, eqep); 9541772Sjl139090 } else { 9551772Sjl139090 ereport = fm_nvlist_create(nva); 9561772Sjl139090 } 9571772Sjl139090 9581772Sjl139090 /* 9591772Sjl139090 * Create the scheme "dev" FMRI. 9601772Sjl139090 */ 9611772Sjl139090 detector = fm_nvlist_create(nva); 9621772Sjl139090 resource = fm_nvlist_create(nva); 9631772Sjl139090 9641772Sjl139090 nflts = mc_aflt->mflt_nflts; 9651772Sjl139090 9661772Sjl139090 ASSERT(nflts >= 1 && nflts <= 2); 9671772Sjl139090 9681772Sjl139090 flt_stat = mc_aflt->mflt_stat[0]; 9691772Sjl139090 (void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path); 9701772Sjl139090 (void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 9711772Sjl139090 device_path, NULL); 9721772Sjl139090 9731772Sjl139090 /* 9741772Sjl139090 * Encode all the common data into the ereport. 9751772Sjl139090 */ 9765080Swh31274 (void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", MC_OPL_ERROR_CLASS, 9775080Swh31274 mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : MC_OPL_MI_SUBCLASS, 9785080Swh31274 mc_aflt->mflt_erpt_class); 9791772Sjl139090 9801772Sjl139090 MC_LOG("mc_ereport_post: ereport %s\n", buf); 9811772Sjl139090 9821772Sjl139090 9831772Sjl139090 fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, 9845080Swh31274 fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), detector, NULL); 9851772Sjl139090 9861772Sjl139090 /* 9871772Sjl139090 * Set payload. 9881772Sjl139090 */ 9891772Sjl139090 fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32, 9905080Swh31274 flt_stat->mf_flt_maddr.ma_bd, NULL); 9911772Sjl139090 9921772Sjl139090 fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64, 9935080Swh31274 flt_stat->mf_flt_paddr, NULL); 9941772Sjl139090 9955275Stsien if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE || 9965275Stsien flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 9975080Swh31274 fm_payload_set(ereport, MC_OPL_FLT_TYPE, DATA_TYPE_UINT8, 9985080Swh31274 ECC_STICKY, NULL); 9991772Sjl139090 } 10001772Sjl139090 10011772Sjl139090 for (i = 0; i < nflts; i++) 10021772Sjl139090 values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank; 10031772Sjl139090 10045080Swh31274 fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, nflts, 10055080Swh31274 values, NULL); 10061772Sjl139090 10071772Sjl139090 for (i = 0; i < nflts; i++) 10081772Sjl139090 values[i] = mc_aflt->mflt_stat[i]->mf_cntl; 10091772Sjl139090 10105080Swh31274 fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, nflts, 10115080Swh31274 values, NULL); 10121772Sjl139090 10131772Sjl139090 for (i = 0; i < nflts; i++) 10141772Sjl139090 values[i] = mc_aflt->mflt_stat[i]->mf_err_add; 10151772Sjl139090 10165275Stsien /* offset is set only for PCE and ICE */ 10175275Stsien if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_INTERMITTENT_CE || 10185275Stsien mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) { 10192214Sav145390 offset = values[0]; 10202214Sav145390 10212214Sav145390 } 10225080Swh31274 fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, nflts, 10235080Swh31274 values, NULL); 10241772Sjl139090 10251772Sjl139090 for (i = 0; i < nflts; i++) 10261772Sjl139090 values[i] = mc_aflt->mflt_stat[i]->mf_err_log; 10271772Sjl139090 10285080Swh31274 fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, nflts, 10295080Swh31274 values, NULL); 10301772Sjl139090 10311772Sjl139090 for (i = 0; i < nflts; i++) { 10321772Sjl139090 flt_stat = mc_aflt->mflt_stat[i]; 10331772Sjl139090 if (flt_stat->mf_errlog_valid) { 10341772Sjl139090 synd[i] = flt_stat->mf_synd; 10351772Sjl139090 dslot[i] = flt_stat->mf_dimm_slot; 10361772Sjl139090 values[i] = flt_stat->mf_dram_place; 10371772Sjl139090 } else { 10381772Sjl139090 synd[i] = 0; 10391772Sjl139090 dslot[i] = 0; 10401772Sjl139090 values[i] = 0; 10411772Sjl139090 } 10421772Sjl139090 } 10431772Sjl139090 10445080Swh31274 fm_payload_set(ereport, MC_OPL_ERR_SYND, DATA_TYPE_UINT32_ARRAY, nflts, 10455080Swh31274 synd, NULL); 10465080Swh31274 10475080Swh31274 fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, DATA_TYPE_UINT32_ARRAY, 10485080Swh31274 nflts, dslot, NULL); 10495080Swh31274 10505080Swh31274 fm_payload_set(ereport, MC_OPL_ERR_DRAM, DATA_TYPE_UINT32_ARRAY, nflts, 10515080Swh31274 values, NULL); 10521772Sjl139090 10531772Sjl139090 device_path[0] = 0; 10541772Sjl139090 p = &device_path[0]; 10552214Sav145390 sid[0] = 0; 10562214Sav145390 s = &sid[0]; 10572214Sav145390 ret = 0; 10581772Sjl139090 10591772Sjl139090 for (i = 0; i < nflts; i++) { 10602214Sav145390 int bank; 10611772Sjl139090 10621772Sjl139090 flt_stat = mc_aflt->mflt_stat[i]; 10632214Sav145390 bank = flt_stat->mf_flt_maddr.ma_bank; 10645080Swh31274 ret = mc_set_mem_unum(p + strlen(p), blen, 10655080Swh31274 flt_stat->mf_flt_maddr.ma_phys_bd, bank, flt_stat->mf_type, 10665080Swh31274 flt_stat->mf_dimm_slot); 10672214Sav145390 10682214Sav145390 if (ret != 0) { 10692214Sav145390 cmn_err(CE_WARN, 10702214Sav145390 "mc_ereport_post: Failed to determine the unum " 10712214Sav145390 "for board=%d bank=%d type=0x%x slot=0x%x", 10722214Sav145390 flt_stat->mf_flt_maddr.ma_bd, bank, 10732214Sav145390 flt_stat->mf_type, flt_stat->mf_dimm_slot); 10742214Sav145390 continue; 10751772Sjl139090 } 10762214Sav145390 n = strlen(device_path); 10771772Sjl139090 blen = MAXPATHLEN - n; 10781772Sjl139090 p = &device_path[n]; 10791772Sjl139090 if (i < (nflts - 1)) { 10801772Sjl139090 snprintf(p, blen, " "); 10812214Sav145390 blen--; 10822214Sav145390 p++; 10832214Sav145390 } 10842214Sav145390 10852214Sav145390 if (ret == 0) { 10862214Sav145390 ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s), 10873045Sav145390 blen, flt_stat->mf_flt_maddr.ma_phys_bd, bank, 10882214Sav145390 flt_stat->mf_type, flt_stat->mf_dimm_slot); 10892214Sav145390 10901772Sjl139090 } 10911772Sjl139090 } 10921772Sjl139090 10935080Swh31274 (void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, NULL, 10945080Swh31274 device_path, (ret == 0) ? sid : NULL, (ret == 0) ? offset : 10955080Swh31274 (uint64_t)-1); 10965080Swh31274 10975080Swh31274 fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, resource, 10985080Swh31274 NULL); 10991772Sjl139090 11001772Sjl139090 if (panicstr) { 11011772Sjl139090 errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); 11021772Sjl139090 } else { 11031772Sjl139090 (void) fm_ereport_post(ereport, EVCH_TRYHARD); 11041772Sjl139090 fm_nvlist_destroy(ereport, FM_NVA_FREE); 11051772Sjl139090 fm_nvlist_destroy(detector, FM_NVA_FREE); 11061772Sjl139090 fm_nvlist_destroy(resource, FM_NVA_FREE); 11071772Sjl139090 } 11081772Sjl139090 } 11091772Sjl139090 11102214Sav145390 11111772Sjl139090 static void 11121772Sjl139090 mc_err_drain(mc_aflt_t *mc_aflt) 11131772Sjl139090 { 11141772Sjl139090 int rv; 11151772Sjl139090 uint64_t pa = (uint64_t)(-1); 11162214Sav145390 int i; 11171772Sjl139090 11185080Swh31274 MC_LOG("mc_err_drain: %s\n", mc_aflt->mflt_erpt_class); 11191772Sjl139090 /* 11201772Sjl139090 * we come here only when we have: 11213152Sav145390 * In mirror mode: MUE, SUE 11225275Stsien * In normal mode: UE, Permanent CE, Intermittent CE 11231772Sjl139090 */ 11242214Sav145390 for (i = 0; i < mc_aflt->mflt_nflts; i++) { 11252214Sav145390 rv = mcaddr_to_pa(mc_aflt->mflt_mcp, 11265080Swh31274 &(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa); 11272662Shyw 11282662Shyw /* Ensure the pa is valid (not in isolated memory block) */ 11292662Shyw if (rv == 0 && pa_is_valid(mc_aflt->mflt_mcp, pa)) 11302214Sav145390 mc_aflt->mflt_stat[i]->mf_flt_paddr = pa; 11312214Sav145390 else 11322214Sav145390 mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1; 11332214Sav145390 } 11342214Sav145390 11352662Shyw MC_LOG("mc_err_drain:pa = %lx\n", pa); 11362662Shyw 11372662Shyw switch (page_retire_check(pa, NULL)) { 11382662Shyw case 0: 11392662Shyw case EAGAIN: 11402662Shyw MC_LOG("Page retired or pending\n"); 11412662Shyw return; 11422662Shyw case EIO: 11432662Shyw /* 11445275Stsien * Do page retirement except for the PCE and ICE cases. 11452662Shyw * This is taken care by the OPL DE 11462662Shyw */ 11475275Stsien if (mc_aflt->mflt_stat[0]->mf_type != 11485275Stsien FLT_TYPE_INTERMITTENT_CE && 11495275Stsien mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) { 11502662Shyw MC_LOG("offline page at pa %lx error %x\n", pa, 11515080Swh31274 mc_aflt->mflt_pr); 11522662Shyw (void) page_retire(pa, mc_aflt->mflt_pr); 11531772Sjl139090 } 11542662Shyw break; 11552662Shyw case EINVAL: 11562662Shyw default: 11572662Shyw /* 11582662Shyw * Some memory do not have page structure so 11592662Shyw * we keep going in case of EINVAL. 11602662Shyw */ 11612662Shyw break; 11621772Sjl139090 } 11632214Sav145390 11642214Sav145390 for (i = 0; i < mc_aflt->mflt_nflts; i++) { 11652214Sav145390 mc_aflt_t mc_aflt0; 11662214Sav145390 if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) { 11672214Sav145390 mc_aflt0 = *mc_aflt; 11682214Sav145390 mc_aflt0.mflt_nflts = 1; 11692214Sav145390 mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i]; 11702214Sav145390 mc_ereport_post(&mc_aflt0); 11712214Sav145390 } 11722214Sav145390 } 11731772Sjl139090 } 11741772Sjl139090 11751772Sjl139090 /* 11761772Sjl139090 * The restart address is actually defined in unit of PA[37:6] 11771772Sjl139090 * the mac patrol will convert that to dimm offset. If the 11781772Sjl139090 * address is not in the bank, it will continue to search for 11791772Sjl139090 * the next PA that is within the bank. 11801772Sjl139090 * 11811772Sjl139090 * Also the mac patrol scans the dimms based on PA, not 11821772Sjl139090 * dimm offset. 11831772Sjl139090 */ 11841772Sjl139090 static int 11852662Shyw restart_patrol(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr_info) 11861772Sjl139090 { 11871772Sjl139090 uint64_t pa; 11881772Sjl139090 int rv; 11892662Shyw 11905310Sdhain if (MC_REWRITE_MODE(mcp, bank)) { 11915310Sdhain return (0); 11925310Sdhain } 11932662Shyw if (rsaddr_info == NULL || (rsaddr_info->mi_valid == 0)) { 11941772Sjl139090 MAC_PTRL_START(mcp, bank); 11951772Sjl139090 return (0); 11961772Sjl139090 } 11971772Sjl139090 11982662Shyw rv = mcaddr_to_pa(mcp, &rsaddr_info->mi_restartaddr, &pa); 11991772Sjl139090 if (rv != 0) { 12001772Sjl139090 MC_LOG("cannot convert mcaddr to pa. use auto restart\n"); 12011772Sjl139090 MAC_PTRL_START(mcp, bank); 12021772Sjl139090 return (0); 12031772Sjl139090 } 12041772Sjl139090 12052662Shyw if (!mc_rangecheck_pa(mcp, pa)) { 12061772Sjl139090 /* pa is not on this board, just retry */ 12071772Sjl139090 cmn_err(CE_WARN, "restart_patrol: invalid address %lx " 12085080Swh31274 "on board %d\n", pa, mcp->mc_board_num); 12091772Sjl139090 MAC_PTRL_START(mcp, bank); 12101772Sjl139090 return (0); 12111772Sjl139090 } 12121772Sjl139090 12131772Sjl139090 MC_LOG("restart_patrol: pa = %lx\n", pa); 12142662Shyw 12152662Shyw if (!rsaddr_info->mi_injectrestart) { 12162662Shyw /* 12173152Sav145390 * For non-error injection restart we need to 12182662Shyw * determine if the current restart pa/page is 12192662Shyw * a "good" page. A "good" page is a page that 12202662Shyw * has not been page retired. If the current 12212662Shyw * page that contains the pa is "good", we will 12222662Shyw * do a HW auto restart and let HW patrol continue 12232662Shyw * where it last stopped. Most desired scenario. 12242662Shyw * 12252662Shyw * If the current page is not "good", we will advance 12262662Shyw * to the next page to find the next "good" page and 12272662Shyw * restart the patrol from there. 12282662Shyw */ 12292662Shyw int wrapcount = 0; 12302662Shyw uint64_t origpa = pa; 12312662Shyw while (wrapcount < 2) { 12325080Swh31274 if (!pa_is_valid(mcp, pa)) { 12335310Sdhain /* 12345310Sdhain * Not in physinstall - advance to the 12355310Sdhain * next memory isolation blocksize 12365310Sdhain */ 12375310Sdhain MC_LOG("Invalid PA\n"); 12385310Sdhain pa = roundup(pa + 1, mc_isolation_bsize); 12395080Swh31274 } else { 12405310Sdhain int rv; 12415310Sdhain if ((rv = page_retire_check(pa, NULL)) != 0 && 12425310Sdhain rv != EAGAIN) { 12435080Swh31274 /* 12445080Swh31274 * The page is "good" (not retired), 12455080Swh31274 * we will use automatic HW restart 12465080Swh31274 * algorithm if this is the original 12475080Swh31274 * current starting page. 12485080Swh31274 */ 12495310Sdhain if (pa == origpa) { 12505310Sdhain MC_LOG("Page has no error. " 12515310Sdhain "Auto restart\n"); 12525310Sdhain MAC_PTRL_START(mcp, bank); 12535310Sdhain return (0); 12545310Sdhain } else { 12555310Sdhain /* 12565310Sdhain * found a subsequent good page 12575310Sdhain */ 12585310Sdhain break; 12592662Shyw } 12601772Sjl139090 } 12612662Shyw 12625310Sdhain /* 12635310Sdhain * Skip to the next page 12645310Sdhain */ 12655310Sdhain pa = roundup(pa + 1, PAGESIZE); 12665310Sdhain MC_LOG("Skipping bad page to %lx\n", pa); 12675310Sdhain } 12685310Sdhain 12695310Sdhain /* Check to see if we hit the end of the memory range */ 12705080Swh31274 if (pa >= (mcp->mc_start_address + mcp->mc_size)) { 12715310Sdhain MC_LOG("Wrap around\n"); 12725310Sdhain pa = mcp->mc_start_address; 12735310Sdhain wrapcount++; 12745080Swh31274 } 12752662Shyw } 12762662Shyw 12772662Shyw if (wrapcount > 1) { 12785080Swh31274 MC_LOG("Failed to find a good page. Just restart\n"); 12795080Swh31274 MAC_PTRL_START(mcp, bank); 12805080Swh31274 return (0); 12811772Sjl139090 } 12821772Sjl139090 } 12831772Sjl139090 12841772Sjl139090 /* 12852662Shyw * We reached here either: 12862662Shyw * 1. We are doing an error injection restart that specify 12872662Shyw * the exact pa/page to restart. OR 12882662Shyw * 2. We found a subsequent good page different from the 12892662Shyw * original restart pa/page. 12902662Shyw * Restart MAC patrol: PA[37:6] 12911772Sjl139090 */ 12921772Sjl139090 MC_LOG("restart at pa = %lx\n", pa); 12931772Sjl139090 ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa)); 12941772Sjl139090 MAC_PTRL_START_ADD(mcp, bank); 12951772Sjl139090 12961772Sjl139090 return (0); 12971772Sjl139090 } 12981772Sjl139090 12995310Sdhain static void 13005310Sdhain mc_retry_info_put(mc_retry_info_t **q, mc_retry_info_t *p) 13015310Sdhain { 13025310Sdhain ASSERT(p != NULL); 13035310Sdhain p->ri_next = *q; 13045310Sdhain *q = p; 13055310Sdhain } 13065310Sdhain 13075310Sdhain static mc_retry_info_t * 13085310Sdhain mc_retry_info_get(mc_retry_info_t **q) 13095310Sdhain { 13105310Sdhain mc_retry_info_t *p; 13115310Sdhain 13125310Sdhain if ((p = *q) != NULL) { 13135310Sdhain *q = p->ri_next; 13145310Sdhain return (p); 13155310Sdhain } else { 13165310Sdhain return (NULL); 13175310Sdhain } 13185310Sdhain } 13195310Sdhain 13201772Sjl139090 /* 13211772Sjl139090 * Rewriting is used for two purposes. 13221772Sjl139090 * - to correct the error in memory. 13231772Sjl139090 * - to determine whether the error is permanent or intermittent. 13241772Sjl139090 * It's done by writing the address in MAC_BANKm_REWRITE_ADD 13251772Sjl139090 * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that, 13261772Sjl139090 * REW_END (and REW_CE/REW_UE if some error detected) is set when 13271772Sjl139090 * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM. 13281772Sjl139090 * 13291772Sjl139090 * Note that rewrite operation doesn't change RAW_UE to Marked UE. 13301772Sjl139090 * Therefore, we use it only CE case. 13311772Sjl139090 */ 13325310Sdhain 13331772Sjl139090 static uint32_t 13345310Sdhain do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr, int retrying) 13351772Sjl139090 { 13361772Sjl139090 uint32_t cntl; 13371772Sjl139090 int count = 0; 13385310Sdhain int max_count; 13395310Sdhain int retry_state; 13405310Sdhain 13415310Sdhain if (retrying) 13425310Sdhain max_count = 1; 13435310Sdhain else 13445310Sdhain max_count = mc_max_rewrite_loop; 13455310Sdhain 13465310Sdhain retry_state = RETRY_STATE_PENDING; 13475310Sdhain 13485310Sdhain if (!retrying && MC_REWRITE_MODE(mcp, bank)) { 13495310Sdhain goto timeout; 13505310Sdhain } 13515310Sdhain 13525310Sdhain retry_state = RETRY_STATE_ACTIVE; 13531772Sjl139090 13541772Sjl139090 /* first wait to make sure PTRL_STATUS is 0 */ 13555310Sdhain while (count++ < max_count) { 13561772Sjl139090 cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 13575310Sdhain if (!(cntl & MAC_CNTL_PTRL_STATUS)) { 13585310Sdhain count = 0; 13591772Sjl139090 break; 13605310Sdhain } 13612214Sav145390 drv_usecwait(mc_rewrite_delay); 13621772Sjl139090 } 13635310Sdhain if (count >= max_count) 13645310Sdhain goto timeout; 13651772Sjl139090 13661772Sjl139090 count = 0; 13671772Sjl139090 13681772Sjl139090 ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr); 13691772Sjl139090 MAC_REW_REQ(mcp, bank); 13701772Sjl139090 13715310Sdhain retry_state = RETRY_STATE_REWRITE; 13725310Sdhain 13731772Sjl139090 do { 13745310Sdhain if (count++ > max_count) { 13755310Sdhain goto timeout; 13762214Sav145390 } else { 13772214Sav145390 drv_usecwait(mc_rewrite_delay); 13782214Sav145390 } 13795310Sdhain cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 13801772Sjl139090 /* 13811772Sjl139090 * If there are other MEMORY or PCI activities, this 13821772Sjl139090 * will be BUSY, else it should be set immediately 13831772Sjl139090 */ 13841772Sjl139090 } while (!(cntl & MAC_CNTL_REW_END)); 13851772Sjl139090 13861772Sjl139090 MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 13871772Sjl139090 return (cntl); 13885310Sdhain timeout: 13895310Sdhain mc_set_rewrite(mcp, bank, dimm_addr, retry_state); 13905310Sdhain 13915310Sdhain return (0); 13921772Sjl139090 } 13935310Sdhain 13945310Sdhain void 13955310Sdhain mc_clear_rewrite(mc_opl_t *mcp, int bank) 13965310Sdhain { 13975310Sdhain struct mc_bank *bankp; 13985310Sdhain mc_retry_info_t *retry; 13995310Sdhain uint32_t rew_addr; 14005310Sdhain 14015310Sdhain bankp = &(mcp->mc_bank[bank]); 14025310Sdhain retry = bankp->mcb_active; 14035310Sdhain bankp->mcb_active = NULL; 14045310Sdhain mc_retry_info_put(&bankp->mcb_retry_freelist, retry); 14055310Sdhain 14065310Sdhain again: 14075310Sdhain bankp->mcb_rewrite_count = 0; 14085310Sdhain 14095310Sdhain while (retry = mc_retry_info_get(&bankp->mcb_retry_pending)) { 14105310Sdhain rew_addr = retry->ri_addr; 14115310Sdhain mc_retry_info_put(&bankp->mcb_retry_freelist, retry); 14125310Sdhain if (do_rewrite(mcp, bank, rew_addr, 1) == 0) 14135310Sdhain break; 14145310Sdhain } 14155310Sdhain 14165310Sdhain /* we break out if no more pending rewrite or we got timeout again */ 14175310Sdhain 14185310Sdhain if (!bankp->mcb_active && !bankp->mcb_retry_pending) { 14195310Sdhain if (!IS_MIRROR(mcp, bank)) { 14205310Sdhain MC_CLEAR_REWRITE_MODE(mcp, bank); 14215310Sdhain } else { 14225310Sdhain int mbank = bank ^ 1; 14235310Sdhain bankp = &(mcp->mc_bank[mbank]); 14245310Sdhain if (!bankp->mcb_active && !bankp->mcb_retry_pending) { 14255310Sdhain MC_CLEAR_REWRITE_MODE(mcp, bank); 14265310Sdhain MC_CLEAR_REWRITE_MODE(mcp, mbank); 14275310Sdhain } else { 14285310Sdhain bank = mbank; 14295310Sdhain goto again; 14305310Sdhain } 14315310Sdhain } 14325310Sdhain } 14335310Sdhain } 14345310Sdhain 14355310Sdhain void 14365310Sdhain mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state) 14375310Sdhain { 14385310Sdhain mc_retry_info_t *retry; 14395310Sdhain struct mc_bank *bankp; 14405310Sdhain 14415310Sdhain bankp = &mcp->mc_bank[bank]; 14425310Sdhain 14435310Sdhain retry = mc_retry_info_get(&bankp->mcb_retry_freelist); 14445310Sdhain 1445*6693Swh31274 if (retry == NULL) { 1446*6693Swh31274 mc_addr_t maddr; 1447*6693Swh31274 uint64_t paddr; 1448*6693Swh31274 /* 1449*6693Swh31274 * previous rewrite request has not completed yet. 1450*6693Swh31274 * So we discard this rewrite request. 1451*6693Swh31274 */ 1452*6693Swh31274 maddr.ma_bd = mcp->mc_board_num; 1453*6693Swh31274 maddr.ma_bank = bank; 1454*6693Swh31274 maddr.ma_dimm_addr = addr; 1455*6693Swh31274 if (mcaddr_to_pa(mcp, &maddr, &paddr) == 0) { 1456*6693Swh31274 cmn_err(CE_WARN, "Discard CE rewrite request" 1457*6693Swh31274 " for 0x%lx (/LSB%d/B%d/%x).\n", 1458*6693Swh31274 paddr, mcp->mc_board_num, bank, addr); 1459*6693Swh31274 } else { 1460*6693Swh31274 cmn_err(CE_WARN, "Discard CE rewrite request" 1461*6693Swh31274 " for /LSB%d/B%d/%x.\n", 1462*6693Swh31274 mcp->mc_board_num, bank, addr); 1463*6693Swh31274 } 1464*6693Swh31274 return; 1465*6693Swh31274 } 14665310Sdhain 14675310Sdhain retry->ri_addr = addr; 14685310Sdhain retry->ri_state = state; 14695310Sdhain 14705310Sdhain MC_SET_REWRITE_MODE(mcp, bank); 14715310Sdhain 14725310Sdhain if ((state > RETRY_STATE_PENDING)) { 14735310Sdhain ASSERT(bankp->mcb_active == NULL); 14745310Sdhain bankp->mcb_active = retry; 14755310Sdhain } else { 14765310Sdhain mc_retry_info_put(&bankp->mcb_retry_pending, retry); 14775310Sdhain } 14785310Sdhain 14795310Sdhain if (IS_MIRROR(mcp, bank)) { 14805310Sdhain int mbank = bank ^1; 14815310Sdhain MC_SET_REWRITE_MODE(mcp, mbank); 14825310Sdhain } 14835310Sdhain } 14845310Sdhain 14851772Sjl139090 void 14861772Sjl139090 mc_process_scf_log(mc_opl_t *mcp) 14871772Sjl139090 { 14882214Sav145390 int count; 14892214Sav145390 int n = 0; 14901772Sjl139090 scf_log_t *p; 14911772Sjl139090 int bank; 14921772Sjl139090 14932214Sav145390 for (bank = 0; bank < BANKNUM_PER_SB; bank++) { 14945080Swh31274 while ((p = mcp->mc_scf_log[bank]) != NULL && 14955080Swh31274 (n < mc_max_errlog_processed)) { 14965310Sdhain ASSERT(bank == p->sl_bank); 14975310Sdhain count = 0; 14985310Sdhain while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank)) 14995310Sdhain & MAC_STATIC_ERR_VLD)) { 15005310Sdhain if (count++ >= (mc_max_scf_loop)) { 15015310Sdhain break; 15021772Sjl139090 } 15035310Sdhain drv_usecwait(mc_scf_delay); 15045310Sdhain } 15055310Sdhain 15065310Sdhain if (count < mc_max_scf_loop) { 15075310Sdhain ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank), 15085310Sdhain p->sl_err_log); 15095310Sdhain 15105310Sdhain ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank), 15115310Sdhain p->sl_err_add|MAC_STATIC_ERR_VLD); 15125310Sdhain mcp->mc_scf_retry[bank] = 0; 15135310Sdhain } else { 15145310Sdhain /* 15155310Sdhain * if we try too many times, just drop the req 15165310Sdhain */ 15175310Sdhain if (mcp->mc_scf_retry[bank]++ <= 15185310Sdhain mc_max_scf_retry) { 15195310Sdhain return; 15205080Swh31274 } else { 15215310Sdhain if ((++mc_pce_dropped & 0xff) == 0) { 15225310Sdhain cmn_err(CE_WARN, "Cannot " 1523*6693Swh31274 "report CE to SCF\n"); 15245080Swh31274 } 15255080Swh31274 } 15265310Sdhain } 15275310Sdhain n++; 15285310Sdhain mcp->mc_scf_log[bank] = p->sl_next; 15295310Sdhain mcp->mc_scf_total[bank]--; 15305310Sdhain ASSERT(mcp->mc_scf_total[bank] >= 0); 15315310Sdhain kmem_free(p, sizeof (scf_log_t)); 15321772Sjl139090 } 15331772Sjl139090 } 15341772Sjl139090 } 15351772Sjl139090 void 15361772Sjl139090 mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank) 15371772Sjl139090 { 15381772Sjl139090 scf_log_t *p; 15391772Sjl139090 15402214Sav145390 if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) { 15412214Sav145390 if ((++mc_pce_dropped & 0xff) == 0) { 1542*6693Swh31274 cmn_err(CE_WARN, "Too many CE requests.\n"); 15432214Sav145390 } 15441772Sjl139090 return; 15451772Sjl139090 } 15461772Sjl139090 p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP); 15471772Sjl139090 p->sl_next = 0; 15481772Sjl139090 p->sl_err_add = flt_stat->mf_err_add; 15491772Sjl139090 p->sl_err_log = flt_stat->mf_err_log; 15501772Sjl139090 p->sl_bank = bank; 15511772Sjl139090 15522214Sav145390 if (mcp->mc_scf_log[bank] == NULL) { 15531772Sjl139090 /* 15541772Sjl139090 * we rely on mc_scf_log to detect NULL queue. 15551772Sjl139090 * mc_scf_log_tail is irrelevant is such case. 15561772Sjl139090 */ 15572214Sav145390 mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p; 15581772Sjl139090 } else { 15592214Sav145390 mcp->mc_scf_log_tail[bank]->sl_next = p; 15602214Sav145390 mcp->mc_scf_log_tail[bank] = p; 15611772Sjl139090 } 15622214Sav145390 mcp->mc_scf_total[bank]++; 15631772Sjl139090 } 15641772Sjl139090 /* 15651772Sjl139090 * This routine determines what kind of CE happens, intermittent 15661772Sjl139090 * or permanent as follows. (See 4.7.3 in Columbus2 PRM.) 15671772Sjl139090 * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register. 15681772Sjl139090 * - If CE is still detected on the same address even after doing 15691772Sjl139090 * rewrite operation twice, it is determined as permanent error. 15701772Sjl139090 * - If error is not detected anymore, it is determined as intermittent 15711772Sjl139090 * error. 15721772Sjl139090 * - If UE is detected due to rewrite operation, it should be treated 15731772Sjl139090 * as UE. 15741772Sjl139090 */ 15751772Sjl139090 15761772Sjl139090 /* ARGSUSED */ 15771772Sjl139090 static void 15781772Sjl139090 mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error) 15791772Sjl139090 { 15801772Sjl139090 uint32_t cntl; 15811772Sjl139090 int i; 15821772Sjl139090 15831772Sjl139090 flt_stat->mf_type = FLT_TYPE_PERMANENT_CE; 15841772Sjl139090 /* 15851772Sjl139090 * rewrite request 1st time reads and correct error data 15861772Sjl139090 * and write to DIMM. 2nd rewrite request must be issued 15871772Sjl139090 * after REW_CE/UE/END is 0. When the 2nd request is completed, 15881772Sjl139090 * if REW_CE = 1, then it is permanent CE. 15891772Sjl139090 */ 15901772Sjl139090 for (i = 0; i < 2; i++) { 15915310Sdhain cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add, 0); 15925310Sdhain 15935310Sdhain if (cntl == 0) { 15945310Sdhain /* timeout case */ 15955310Sdhain return; 15965310Sdhain } 15971772Sjl139090 /* 15981772Sjl139090 * If the error becomes UE or CMPE 15991772Sjl139090 * we return to the caller immediately. 16001772Sjl139090 */ 16011772Sjl139090 if (cntl & MAC_CNTL_REW_UE) { 16021772Sjl139090 if (ptrl_error) 16031772Sjl139090 flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE; 16041772Sjl139090 else 16051772Sjl139090 flt_stat->mf_cntl |= MAC_CNTL_MI_UE; 16061772Sjl139090 flt_stat->mf_type = FLT_TYPE_UE; 16071772Sjl139090 return; 16081772Sjl139090 } 16091772Sjl139090 if (cntl & MAC_CNTL_REW_CMPE) { 16101772Sjl139090 if (ptrl_error) 16111772Sjl139090 flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE; 16121772Sjl139090 else 16131772Sjl139090 flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE; 16141772Sjl139090 flt_stat->mf_type = FLT_TYPE_CMPE; 16151772Sjl139090 return; 16161772Sjl139090 } 16171772Sjl139090 } 16181772Sjl139090 if (!(cntl & MAC_CNTL_REW_CE)) { 16191772Sjl139090 flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE; 16201772Sjl139090 } 16211772Sjl139090 16221772Sjl139090 if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 16231772Sjl139090 /* report PERMANENT_CE to SP via SCF */ 16241772Sjl139090 if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) { 16251772Sjl139090 mc_queue_scf_log(mcp, flt_stat, bank); 16261772Sjl139090 } 16271772Sjl139090 } 16281772Sjl139090 } 16291772Sjl139090 16301772Sjl139090 #define IS_CMPE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\ 16311772Sjl139090 MAC_CNTL_MI_CMPE)) 16321772Sjl139090 #define IS_UE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE)) 16331772Sjl139090 #define IS_CE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE)) 16341772Sjl139090 #define IS_OK(cntl, f) (!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \ 16351772Sjl139090 MAC_CNTL_MI_ERRS))) 16361772Sjl139090 16371772Sjl139090 16381772Sjl139090 static int 16391772Sjl139090 IS_CE_ONLY(uint32_t cntl, int ptrl_error) 16401772Sjl139090 { 16411772Sjl139090 if (ptrl_error) { 16421772Sjl139090 return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE); 16431772Sjl139090 } else { 16441772Sjl139090 return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE); 16451772Sjl139090 } 16461772Sjl139090 } 16471772Sjl139090 16481772Sjl139090 void 16491772Sjl139090 mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value) 16501772Sjl139090 { 16512867Shyw int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank; 16522867Shyw 16532867Shyw if (mcp->mc_speedup_period[ebank] > 0) 16542214Sav145390 value |= mc_max_speed; 16552214Sav145390 else 16562214Sav145390 value |= mcp->mc_speed; 16571772Sjl139090 ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value); 16581772Sjl139090 } 16591772Sjl139090 16601772Sjl139090 static void 16611772Sjl139090 mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 16621772Sjl139090 { 16631772Sjl139090 flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 16645080Swh31274 MAC_CNTL_PTRL_ERRS; 16651772Sjl139090 flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank)); 16661772Sjl139090 flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank)); 16671772Sjl139090 flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 16683045Sav145390 flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num; 16691772Sjl139090 flt_stat->mf_flt_maddr.ma_bank = bank; 16701772Sjl139090 flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 16711772Sjl139090 } 16721772Sjl139090 16731772Sjl139090 static void 16741772Sjl139090 mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 16751772Sjl139090 { 16761772Sjl139090 uint32_t status, old_status; 16771772Sjl139090 16785080Swh31274 status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & MAC_CNTL_MI_ERRS; 16791772Sjl139090 old_status = 0; 16801772Sjl139090 16811772Sjl139090 /* we keep reading until the status is stable */ 16821772Sjl139090 while (old_status != status) { 16831772Sjl139090 old_status = status; 16845080Swh31274 flt_stat->mf_err_add = LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank)); 16855080Swh31274 flt_stat->mf_err_log = LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank)); 16861772Sjl139090 status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 16875080Swh31274 MAC_CNTL_MI_ERRS; 16881772Sjl139090 if (status == old_status) { 16891772Sjl139090 break; 16901772Sjl139090 } 16911772Sjl139090 } 16921772Sjl139090 16931772Sjl139090 flt_stat->mf_cntl = status; 16941772Sjl139090 flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 16953045Sav145390 flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num; 16961772Sjl139090 flt_stat->mf_flt_maddr.ma_bank = bank; 16971772Sjl139090 flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 16981772Sjl139090 } 16991772Sjl139090 17001772Sjl139090 17011772Sjl139090 /* 17021772Sjl139090 * Error philosophy for mirror mode: 17031772Sjl139090 * 17041772Sjl139090 * PTRL (The error address for both banks are same, since ptrl stops if it 17051772Sjl139090 * detects error.) 17063152Sav145390 * - Compare error log CMPE. 17071772Sjl139090 * 17081772Sjl139090 * - UE-UE Report MUE. No rewrite. 17091772Sjl139090 * 17101772Sjl139090 * - UE-* UE-(CE/OK). Rewrite to scrub UE. Report SUE. 17111772Sjl139090 * 17121772Sjl139090 * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 17131772Sjl139090 * If CE is permanent, inform SCF. Once for each 17141772Sjl139090 * Dimm. If CE becomes UE or CMPE, go back to above. 17151772Sjl139090 * 17161772Sjl139090 * 17171772Sjl139090 * MI (The error addresses for each bank are the same or different.) 17183152Sav145390 * - Compare error If addresses are the same. Just CMPE, so log CMPE. 17191772Sjl139090 * If addresses are different (this could happen 17203152Sav145390 * as a result of scrubbing. Report each separately. 17211772Sjl139090 * Only report error info on each side. 17221772Sjl139090 * 17231772Sjl139090 * - UE-UE Addresses are the same. Report MUE. 17241772Sjl139090 * Addresses are different. Report SUE on each bank. 17251772Sjl139090 * Rewrite to clear UE. 17261772Sjl139090 * 17271772Sjl139090 * - UE-* UE-(CE/OK) 17281772Sjl139090 * Rewrite to clear UE. Report SUE for the bank. 17291772Sjl139090 * 17301772Sjl139090 * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 17311772Sjl139090 * If CE becomes UE or CMPE, go back to above. 17321772Sjl139090 * 17331772Sjl139090 */ 17341772Sjl139090 17351772Sjl139090 static int 17361772Sjl139090 mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat) 17371772Sjl139090 { 17381772Sjl139090 int ptrl_error = mc_aflt->mflt_is_ptrl; 17391772Sjl139090 int i; 17401772Sjl139090 int rv = 0; 17415310Sdhain int bank; 17425310Sdhain int rewrite_timeout = 0; 17431772Sjl139090 17441772Sjl139090 MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n", 17455080Swh31274 flt_stat[0].mf_cntl, flt_stat[1].mf_cntl); 17461772Sjl139090 17471772Sjl139090 if (ptrl_error) { 17485080Swh31274 if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) & 17495080Swh31274 MAC_CNTL_PTRL_ERRS) == 0) 17501772Sjl139090 return (0); 17511772Sjl139090 } else { 17525080Swh31274 if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) & 17535080Swh31274 MAC_CNTL_MI_ERRS) == 0) 17541772Sjl139090 return (0); 17551772Sjl139090 } 17561772Sjl139090 17571772Sjl139090 /* 17581772Sjl139090 * First we take care of the case of CE 17591772Sjl139090 * because they can become UE or CMPE 17601772Sjl139090 */ 17611772Sjl139090 for (i = 0; i < 2; i++) { 17621772Sjl139090 if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) { 17635310Sdhain bank = flt_stat[i].mf_flt_maddr.ma_bank; 17645310Sdhain MC_LOG("CE detected on bank %d\n", bank); 17655310Sdhain mc_scrub_ce(mcp, bank, &flt_stat[i], ptrl_error); 17665310Sdhain if (MC_REWRITE_ACTIVE(mcp, bank)) { 17675310Sdhain rewrite_timeout = 1; 17685310Sdhain } 17691772Sjl139090 rv = 1; 17701772Sjl139090 } 17711772Sjl139090 } 17721772Sjl139090 17735310Sdhain if (rewrite_timeout) 17745310Sdhain return (0); 17755310Sdhain 17761772Sjl139090 /* The above scrubbing can turn CE into UE or CMPE */ 17771772Sjl139090 17781772Sjl139090 /* 17791772Sjl139090 * Now we distinguish two cases: same address or not 17801772Sjl139090 * the same address. It might seem more intuitive to 17811772Sjl139090 * distinguish PTRL v.s. MI error but it is more 17821772Sjl139090 * complicated that way. 17831772Sjl139090 */ 17841772Sjl139090 17851772Sjl139090 if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) { 17861772Sjl139090 17871772Sjl139090 if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) || 17881772Sjl139090 IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) { 17891772Sjl139090 flt_stat[0].mf_type = FLT_TYPE_CMPE; 17901772Sjl139090 flt_stat[1].mf_type = FLT_TYPE_CMPE; 17911772Sjl139090 mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 17921772Sjl139090 mc_aflt->mflt_nflts = 2; 17931772Sjl139090 mc_aflt->mflt_stat[0] = &flt_stat[0]; 17941772Sjl139090 mc_aflt->mflt_stat[1] = &flt_stat[1]; 17951772Sjl139090 mc_aflt->mflt_pr = PR_UE; 17963152Sav145390 /* 17973152Sav145390 * Compare error is result of MAC internal error, so 17983152Sav145390 * simply log it instead of publishing an ereport. SCF 17993152Sav145390 * diagnoses all the MAC internal and its i/f error. 18003152Sav145390 */ 18013152Sav145390 MC_LOG("cmpe error detected\n"); 18021772Sjl139090 return (1); 18031772Sjl139090 } 18041772Sjl139090 18051772Sjl139090 if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) && 18065080Swh31274 IS_UE(flt_stat[1].mf_cntl, ptrl_error)) { 18071772Sjl139090 /* Both side are UE's */ 18081772Sjl139090 18091772Sjl139090 MAC_SET_ERRLOG_INFO(&flt_stat[0]); 18101772Sjl139090 MAC_SET_ERRLOG_INFO(&flt_stat[1]); 18111772Sjl139090 MC_LOG("MUE detected\n"); 18122214Sav145390 flt_stat[0].mf_type = FLT_TYPE_MUE; 18132214Sav145390 flt_stat[1].mf_type = FLT_TYPE_MUE; 18141772Sjl139090 mc_aflt->mflt_erpt_class = MC_OPL_MUE; 18151772Sjl139090 mc_aflt->mflt_nflts = 2; 18161772Sjl139090 mc_aflt->mflt_stat[0] = &flt_stat[0]; 18171772Sjl139090 mc_aflt->mflt_stat[1] = &flt_stat[1]; 18181772Sjl139090 mc_aflt->mflt_pr = PR_UE; 18191772Sjl139090 mc_err_drain(mc_aflt); 18201772Sjl139090 return (1); 18211772Sjl139090 } 18221772Sjl139090 18231772Sjl139090 /* Now the only case is UE/CE, UE/OK, or don't care */ 18241772Sjl139090 for (i = 0; i < 2; i++) { 18255310Sdhain if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 18262214Sav145390 18272214Sav145390 /* rewrite can clear the one side UE error */ 18282214Sav145390 18291772Sjl139090 if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) { 18301772Sjl139090 (void) do_rewrite(mcp, 18311772Sjl139090 flt_stat[i].mf_flt_maddr.ma_bank, 18325310Sdhain flt_stat[i].mf_flt_maddr.ma_dimm_addr, 0); 18331772Sjl139090 } 18341772Sjl139090 flt_stat[i].mf_type = FLT_TYPE_UE; 18351772Sjl139090 MAC_SET_ERRLOG_INFO(&flt_stat[i]); 18361772Sjl139090 mc_aflt->mflt_erpt_class = MC_OPL_SUE; 18371772Sjl139090 mc_aflt->mflt_stat[0] = &flt_stat[i]; 18381772Sjl139090 mc_aflt->mflt_nflts = 1; 18391772Sjl139090 mc_aflt->mflt_pr = PR_MCE; 18401772Sjl139090 mc_err_drain(mc_aflt); 18411772Sjl139090 /* Once we hit a UE/CE or UE/OK case, done */ 18421772Sjl139090 return (1); 18435310Sdhain } 18441772Sjl139090 } 18451772Sjl139090 18461772Sjl139090 } else { 18471772Sjl139090 /* 18481772Sjl139090 * addresses are different. That means errors 18491772Sjl139090 * on the 2 banks are not related at all. 18501772Sjl139090 */ 18511772Sjl139090 for (i = 0; i < 2; i++) { 18525080Swh31274 if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) { 18535080Swh31274 flt_stat[i].mf_type = FLT_TYPE_CMPE; 18545080Swh31274 mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 18555080Swh31274 mc_aflt->mflt_nflts = 1; 18565080Swh31274 mc_aflt->mflt_stat[0] = &flt_stat[i]; 18575080Swh31274 mc_aflt->mflt_pr = PR_UE; 18585080Swh31274 /* 18595080Swh31274 * Compare error is result of MAC internal 18605080Swh31274 * error, so simply log it instead of 18615080Swh31274 * publishing an ereport. SCF diagnoses all 18625080Swh31274 * the MAC internal and its interface error. 18635080Swh31274 */ 18645080Swh31274 MC_LOG("cmpe error detected\n"); 18655080Swh31274 /* no more report on this bank */ 18665080Swh31274 flt_stat[i].mf_cntl = 0; 18675080Swh31274 rv = 1; 18685080Swh31274 } 18691772Sjl139090 } 18701772Sjl139090 18712214Sav145390 /* rewrite can clear the one side UE error */ 18722214Sav145390 18731772Sjl139090 for (i = 0; i < 2; i++) { 18745080Swh31274 if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 18755080Swh31274 (void) do_rewrite(mcp, 18765080Swh31274 flt_stat[i].mf_flt_maddr.ma_bank, 18775310Sdhain flt_stat[i].mf_flt_maddr.ma_dimm_addr, 18785310Sdhain 0); 18795080Swh31274 flt_stat[i].mf_type = FLT_TYPE_UE; 18805080Swh31274 MAC_SET_ERRLOG_INFO(&flt_stat[i]); 18815080Swh31274 mc_aflt->mflt_erpt_class = MC_OPL_SUE; 18825080Swh31274 mc_aflt->mflt_stat[0] = &flt_stat[i]; 18835080Swh31274 mc_aflt->mflt_nflts = 1; 18845080Swh31274 mc_aflt->mflt_pr = PR_MCE; 18855080Swh31274 mc_err_drain(mc_aflt); 18865080Swh31274 rv = 1; 18875080Swh31274 } 18881772Sjl139090 } 18891772Sjl139090 } 18901772Sjl139090 return (rv); 18911772Sjl139090 } 18921772Sjl139090 static void 18932662Shyw mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr) 18941772Sjl139090 { 18951772Sjl139090 mc_aflt_t mc_aflt; 18961772Sjl139090 mc_flt_stat_t flt_stat[2], mi_flt_stat[2]; 18972214Sav145390 int i; 18982214Sav145390 int mi_valid; 18992214Sav145390 19002662Shyw ASSERT(rsaddr); 19012662Shyw 19021772Sjl139090 bzero(&mc_aflt, sizeof (mc_aflt_t)); 19031772Sjl139090 bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t)); 19041772Sjl139090 bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t)); 19051772Sjl139090 19063373Sbm42561 19071772Sjl139090 mc_aflt.mflt_mcp = mcp; 19081772Sjl139090 mc_aflt.mflt_id = gethrtime(); 19091772Sjl139090 19101772Sjl139090 /* Now read all the registers into flt_stat */ 19111772Sjl139090 19122214Sav145390 for (i = 0; i < 2; i++) { 19132214Sav145390 MC_LOG("Reading registers of bank %d\n", bank); 19142214Sav145390 /* patrol registers */ 19152214Sav145390 mc_read_ptrl_reg(mcp, bank, &flt_stat[i]); 19162214Sav145390 19172662Shyw /* 19182662Shyw * In mirror mode, it is possible that only one bank 19192662Shyw * may report the error. We need to check for it to 19202662Shyw * ensure we pick the right addr value for patrol restart. 19212662Shyw * Note that if both banks reported errors, we pick the 19222662Shyw * 2nd one. Both banks should reported the same error address. 19232662Shyw */ 19242662Shyw if (flt_stat[i].mf_cntl & MAC_CNTL_PTRL_ERRS) 19252662Shyw rsaddr->mi_restartaddr = flt_stat[i].mf_flt_maddr; 19262214Sav145390 19272214Sav145390 MC_LOG("ptrl registers cntl %x add %x log %x\n", 19285080Swh31274 flt_stat[i].mf_cntl, flt_stat[i].mf_err_add, 19295080Swh31274 flt_stat[i].mf_err_log); 19302214Sav145390 19312214Sav145390 /* MI registers */ 19322214Sav145390 mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]); 19332214Sav145390 19342214Sav145390 MC_LOG("MI registers cntl %x add %x log %x\n", 19355080Swh31274 mi_flt_stat[i].mf_cntl, mi_flt_stat[i].mf_err_add, 19365080Swh31274 mi_flt_stat[i].mf_err_log); 19372214Sav145390 19382214Sav145390 bank = bank^1; 19392214Sav145390 } 19401772Sjl139090 19411772Sjl139090 /* clear errors once we read all the registers */ 19425080Swh31274 MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 19431772Sjl139090 19442214Sav145390 MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 19452214Sav145390 19462214Sav145390 /* Process MI errors first */ 19472214Sav145390 19482214Sav145390 /* if not error mode, cntl1 is 0 */ 19492214Sav145390 if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 19505080Swh31274 (mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 19512214Sav145390 mi_flt_stat[0].mf_cntl = 0; 19522214Sav145390 19532214Sav145390 if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 19545080Swh31274 (mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 19552214Sav145390 mi_flt_stat[1].mf_cntl = 0; 19562214Sav145390 19572214Sav145390 mc_aflt.mflt_is_ptrl = 0; 19582214Sav145390 mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]); 19592214Sav145390 19602214Sav145390 if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 19615080Swh31274 MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[0].mf_cntl & 19625080Swh31274 MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) && 1963*6693Swh31274 (flt_stat[0].mf_err_add == 1964*6693Swh31274 ROUNDDOWN(mi_flt_stat[0].mf_err_add, MC_BOUND_BYTE)) && 19655080Swh31274 (((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 19665080Swh31274 MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[1].mf_cntl & 19675080Swh31274 MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) && 1968*6693Swh31274 (flt_stat[1].mf_err_add == 1969*6693Swh31274 ROUNDDOWN(mi_flt_stat[1].mf_err_add, MC_BOUND_BYTE))) { 19702214Sav145390 #ifdef DEBUG 19712214Sav145390 MC_LOG("discarding PTRL error because " 19722214Sav145390 "it is the same as MI\n"); 19732214Sav145390 #endif 19742662Shyw rsaddr->mi_valid = mi_valid; 19752214Sav145390 return; 19762214Sav145390 } 19771772Sjl139090 /* if not error mode, cntl1 is 0 */ 19781772Sjl139090 if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 19795080Swh31274 (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 19801772Sjl139090 flt_stat[0].mf_cntl = 0; 19811772Sjl139090 19821772Sjl139090 if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 19835080Swh31274 (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 19841772Sjl139090 flt_stat[1].mf_cntl = 0; 19851772Sjl139090 19861772Sjl139090 mc_aflt.mflt_is_ptrl = 1; 19872662Shyw rsaddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]); 19881772Sjl139090 } 19891772Sjl139090 static int 19901772Sjl139090 mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt, 19911772Sjl139090 mc_flt_stat_t *flt_stat) 19921772Sjl139090 { 19931772Sjl139090 int ptrl_error = mc_aflt->mflt_is_ptrl; 19941772Sjl139090 int rv = 0; 19951772Sjl139090 19961772Sjl139090 mc_aflt->mflt_erpt_class = NULL; 19971772Sjl139090 if (IS_UE(flt_stat->mf_cntl, ptrl_error)) { 19983152Sav145390 MC_LOG("UE detected\n"); 19991772Sjl139090 flt_stat->mf_type = FLT_TYPE_UE; 20001772Sjl139090 mc_aflt->mflt_erpt_class = MC_OPL_UE; 20011772Sjl139090 mc_aflt->mflt_pr = PR_UE; 20021772Sjl139090 MAC_SET_ERRLOG_INFO(flt_stat); 20031772Sjl139090 rv = 1; 20041772Sjl139090 } else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) { 20053152Sav145390 MC_LOG("CE detected\n"); 20061772Sjl139090 MAC_SET_ERRLOG_INFO(flt_stat); 20071772Sjl139090 20083152Sav145390 /* Error type can change after scrubbing */ 20091772Sjl139090 mc_scrub_ce(mcp, bank, flt_stat, ptrl_error); 20105310Sdhain if (MC_REWRITE_ACTIVE(mcp, bank)) { 20115310Sdhain return (0); 20125310Sdhain } 20131772Sjl139090 20145275Stsien if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE) { 20155275Stsien mc_aflt->mflt_erpt_class = MC_OPL_ICE; 20165275Stsien mc_aflt->mflt_pr = PR_MCE; 20175275Stsien } else if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 20181772Sjl139090 mc_aflt->mflt_erpt_class = MC_OPL_CE; 20191772Sjl139090 mc_aflt->mflt_pr = PR_MCE; 20201772Sjl139090 } else if (flt_stat->mf_type == FLT_TYPE_UE) { 20211772Sjl139090 mc_aflt->mflt_erpt_class = MC_OPL_UE; 20221772Sjl139090 mc_aflt->mflt_pr = PR_UE; 20231772Sjl139090 } 20241772Sjl139090 rv = 1; 20251772Sjl139090 } 20265080Swh31274 MC_LOG("mc_process_error: fault type %x erpt %s\n", flt_stat->mf_type, 20275080Swh31274 mc_aflt->mflt_erpt_class); 20281772Sjl139090 if (mc_aflt->mflt_erpt_class) { 20291772Sjl139090 mc_aflt->mflt_stat[0] = flt_stat; 20301772Sjl139090 mc_aflt->mflt_nflts = 1; 20311772Sjl139090 mc_err_drain(mc_aflt); 20321772Sjl139090 } 20331772Sjl139090 return (rv); 20341772Sjl139090 } 20351772Sjl139090 20361772Sjl139090 static void 20372662Shyw mc_error_handler(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr) 20381772Sjl139090 { 20391772Sjl139090 mc_aflt_t mc_aflt; 20401772Sjl139090 mc_flt_stat_t flt_stat, mi_flt_stat; 20412214Sav145390 int mi_valid; 20421772Sjl139090 20431772Sjl139090 bzero(&mc_aflt, sizeof (mc_aflt_t)); 20441772Sjl139090 bzero(&flt_stat, sizeof (mc_flt_stat_t)); 20451772Sjl139090 bzero(&mi_flt_stat, sizeof (mc_flt_stat_t)); 20461772Sjl139090 20471772Sjl139090 mc_aflt.mflt_mcp = mcp; 20481772Sjl139090 mc_aflt.mflt_id = gethrtime(); 20491772Sjl139090 20501772Sjl139090 /* patrol registers */ 20511772Sjl139090 mc_read_ptrl_reg(mcp, bank, &flt_stat); 20521772Sjl139090 20532662Shyw ASSERT(rsaddr); 20542662Shyw rsaddr->mi_restartaddr = flt_stat.mf_flt_maddr; 20551772Sjl139090 20565080Swh31274 MC_LOG("ptrl registers cntl %x add %x log %x\n", flt_stat.mf_cntl, 20575080Swh31274 flt_stat.mf_err_add, flt_stat.mf_err_log); 20581772Sjl139090 20591772Sjl139090 /* MI registers */ 20601772Sjl139090 mc_read_mi_reg(mcp, bank, &mi_flt_stat); 20611772Sjl139090 20622214Sav145390 20635080Swh31274 MC_LOG("MI registers cntl %x add %x log %x\n", mi_flt_stat.mf_cntl, 20645080Swh31274 mi_flt_stat.mf_err_add, mi_flt_stat.mf_err_log); 20651772Sjl139090 20661772Sjl139090 /* clear errors once we read all the registers */ 20671772Sjl139090 MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 20681772Sjl139090 20692214Sav145390 mc_aflt.mflt_is_ptrl = 0; 20702214Sav145390 if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) && 20715080Swh31274 ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 20725080Swh31274 ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 20732214Sav145390 mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat); 20742214Sav145390 } 20752214Sav145390 20762214Sav145390 if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >> 20775080Swh31274 MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat.mf_cntl & 20785080Swh31274 MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) && 2079*6693Swh31274 (flt_stat.mf_err_add == 2080*6693Swh31274 ROUNDDOWN(mi_flt_stat.mf_err_add, MC_BOUND_BYTE))) { 20812214Sav145390 #ifdef DEBUG 20822214Sav145390 MC_LOG("discarding PTRL error because " 20832214Sav145390 "it is the same as MI\n"); 20842214Sav145390 #endif 20852662Shyw rsaddr->mi_valid = mi_valid; 20862214Sav145390 return; 20872214Sav145390 } 20882214Sav145390 20891772Sjl139090 mc_aflt.mflt_is_ptrl = 1; 20901772Sjl139090 if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) && 20915080Swh31274 ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 20925080Swh31274 ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 20935080Swh31274 rsaddr->mi_valid = mc_process_error(mcp, bank, &mc_aflt, 20945080Swh31274 &flt_stat); 20951772Sjl139090 } 20961772Sjl139090 } 20971772Sjl139090 /* 20981772Sjl139090 * memory patrol error handling algorithm: 20991772Sjl139090 * timeout() is used to do periodic polling 21001772Sjl139090 * This is the flow chart. 21011772Sjl139090 * timeout -> 21021772Sjl139090 * mc_check_errors() 21031772Sjl139090 * if memory bank is installed, read the status register 21041772Sjl139090 * if any error bit is set, 21051772Sjl139090 * -> mc_error_handler() 21063152Sav145390 * -> read all error registers 21071772Sjl139090 * -> mc_process_error() 21081772Sjl139090 * determine error type 21091772Sjl139090 * rewrite to clear error or scrub to determine CE type 21101772Sjl139090 * inform SCF on permanent CE 21111772Sjl139090 * -> mc_err_drain 21121772Sjl139090 * page offline processing 21131772Sjl139090 * -> mc_ereport_post() 21141772Sjl139090 */ 21151772Sjl139090 21161772Sjl139090 static void 21175310Sdhain mc_process_rewrite(mc_opl_t *mcp, int bank) 21185310Sdhain { 21195310Sdhain uint32_t rew_addr, cntl; 21205310Sdhain mc_retry_info_t *retry; 21215310Sdhain struct mc_bank *bankp; 21225310Sdhain 21235310Sdhain bankp = &(mcp->mc_bank[bank]); 21245310Sdhain retry = bankp->mcb_active; 21255310Sdhain if (retry == NULL) 21265310Sdhain return; 21275310Sdhain 21285310Sdhain if (retry->ri_state <= RETRY_STATE_ACTIVE) { 21295310Sdhain cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 21305310Sdhain if (cntl & MAC_CNTL_PTRL_STATUS) 21315310Sdhain return; 21325310Sdhain rew_addr = retry->ri_addr; 21335310Sdhain ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), rew_addr); 21345310Sdhain MAC_REW_REQ(mcp, bank); 21355310Sdhain 21365310Sdhain retry->ri_state = RETRY_STATE_REWRITE; 21375310Sdhain } 21385310Sdhain 21395310Sdhain cntl = ldphysio(MAC_PTRL_CNTL(mcp, bank)); 21405310Sdhain 21415310Sdhain if (cntl & MAC_CNTL_REW_END) { 21425310Sdhain MAC_CLEAR_ERRS(mcp, bank, 21435310Sdhain MAC_CNTL_REW_ERRS); 21445310Sdhain mc_clear_rewrite(mcp, bank); 21455310Sdhain } else { 21465310Sdhain /* 21475310Sdhain * If the rewrite does not complete in 21485310Sdhain * 1 hour, we have to consider this a HW 21495310Sdhain * failure. However, there is no recovery 21505310Sdhain * mechanism. The only thing we can do 21515310Sdhain * to to print a warning message to the 21525310Sdhain * console. We continue to increment the 21535310Sdhain * counter but we only print the message 21545310Sdhain * once. It will take the counter a long 21555310Sdhain * time to wrap around and the user might 21565310Sdhain * see a second message. In practice, 21575310Sdhain * we have never hit this condition but 21585310Sdhain * we have to keep the code here just in case. 21595310Sdhain */ 21605310Sdhain if (++mcp->mc_bank[bank].mcb_rewrite_count 21615310Sdhain == mc_max_rewrite_retry) { 21625310Sdhain cmn_err(CE_WARN, "Memory patrol feature is" 21635310Sdhain " partly suspended on /LSB%d/B%d" 21645310Sdhain " due to heavy memory load," 21655310Sdhain " and it will restart" 21665310Sdhain " automatically.\n", mcp->mc_board_num, 21675310Sdhain bank); 21685310Sdhain } 21695310Sdhain } 21705310Sdhain } 21715310Sdhain 21725310Sdhain static void 21731772Sjl139090 mc_check_errors_func(mc_opl_t *mcp) 21741772Sjl139090 { 21752662Shyw mc_rsaddr_info_t rsaddr_info; 21761772Sjl139090 int i, error_count = 0; 21771772Sjl139090 uint32_t stat, cntl; 21782214Sav145390 int running; 21792494Shyw int wrapped; 21802867Shyw int ebk; 21811772Sjl139090 21821772Sjl139090 /* 21831772Sjl139090 * scan errors. 21841772Sjl139090 */ 21852214Sav145390 if (mcp->mc_status & MC_MEMORYLESS) 21862214Sav145390 return; 21872214Sav145390 21881772Sjl139090 for (i = 0; i < BANKNUM_PER_SB; i++) { 21891772Sjl139090 if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 21905310Sdhain if (MC_REWRITE_ACTIVE(mcp, i)) { 21915310Sdhain mc_process_rewrite(mcp, i); 21925310Sdhain } 21931772Sjl139090 stat = ldphysio(MAC_PTRL_STAT(mcp, i)); 21941772Sjl139090 cntl = ldphysio(MAC_PTRL_CNTL(mcp, i)); 21952214Sav145390 running = cntl & MAC_CNTL_PTRL_START; 21962494Shyw wrapped = cntl & MAC_CNTL_PTRL_ADD_MAX; 21972494Shyw 21982867Shyw /* Compute the effective bank idx */ 21992867Shyw ebk = (IS_MIRROR(mcp, i)) ? MIRROR_IDX(i) : i; 22002867Shyw 22012494Shyw if (mc_debug_show_all || stat) { 22022494Shyw MC_LOG("/LSB%d/B%d stat %x cntl %x\n", 22035080Swh31274 mcp->mc_board_num, i, stat, cntl); 22042494Shyw } 22052494Shyw 22062494Shyw /* 22072494Shyw * Update stats and reset flag if the HW patrol 22082494Shyw * wrapped around in its scan. 22092494Shyw */ 22102494Shyw if (wrapped) { 22111772Sjl139090 MAC_CLEAR_MAX(mcp, i); 22122867Shyw mcp->mc_period[ebk]++; 22136297Sjl139090 if (IS_MIRROR(mcp, i)) { 22145080Swh31274 MC_LOG("mirror mc period %ld on " 22155080Swh31274 "/LSB%d/B%d\n", mcp->mc_period[ebk], 22165080Swh31274 mcp->mc_board_num, i); 22176297Sjl139090 } else { 22185080Swh31274 MC_LOG("mc period %ld on " 22195080Swh31274 "/LSB%d/B%d\n", mcp->mc_period[ebk], 22205080Swh31274 mcp->mc_board_num, i); 22212867Shyw } 22222494Shyw } 22232494Shyw 22242494Shyw if (running) { 22252494Shyw /* 22262494Shyw * Mac patrol HW is still running. 22272494Shyw * Normally when an error is detected, 22282494Shyw * the HW patrol will stop so that we 22292494Shyw * can collect error data for reporting. 22302494Shyw * Certain errors (MI errors) detected may not 22312494Shyw * cause the HW patrol to stop which is a 22322494Shyw * problem since we cannot read error data while 22332494Shyw * the HW patrol is running. SW is not allowed 22342494Shyw * to stop the HW patrol while it is running 22352494Shyw * as it may cause HW inconsistency. This is 22362494Shyw * described in a HW errata. 22372494Shyw * In situations where we detected errors 22382494Shyw * that may not cause the HW patrol to stop. 22392494Shyw * We speed up the HW patrol scanning in 22402494Shyw * the hope that it will find the 'real' PTRL 22412494Shyw * errors associated with the previous errors 22422494Shyw * causing the HW to finally stop so that we 22432494Shyw * can do the reporting. 22442494Shyw */ 22452494Shyw /* 22462494Shyw * Check to see if we did speed up 22472494Shyw * the HW patrol due to previous errors 22482494Shyw * detected that did not cause the patrol 22492494Shyw * to stop. We only do it if HW patrol scan 22502494Shyw * wrapped (counted as completing a 'period'). 22512494Shyw */ 22522867Shyw if (mcp->mc_speedup_period[ebk] > 0) { 22535080Swh31274 if (wrapped && 22545080Swh31274 (--mcp->mc_speedup_period[ebk] == 22555080Swh31274 0)) { 22565080Swh31274 /* 22575080Swh31274 * We did try to speed up. 22585080Swh31274 * The speed up period has 22595080Swh31274 * expired and the HW patrol 22605080Swh31274 * is still running. The 22615080Swh31274 * errors must be intermittent. 22625080Swh31274 * We have no choice but to 22635080Swh31274 * ignore them, reset the scan 22645080Swh31274 * speed to normal and clear 22655080Swh31274 * the MI error bits. For 22665080Swh31274 * mirror mode, we need to 22675080Swh31274 * clear errors on both banks. 22685080Swh31274 */ 22695080Swh31274 MC_LOG("Clearing MI errors\n"); 22705080Swh31274 MAC_CLEAR_ERRS(mcp, i, 22715080Swh31274 MAC_CNTL_MI_ERRS); 22725080Swh31274 22735080Swh31274 if (IS_MIRROR(mcp, i)) { 22745080Swh31274 MC_LOG("Clearing " 22755080Swh31274 "Mirror MI errs\n"); 22765080Swh31274 MAC_CLEAR_ERRS(mcp, 22775080Swh31274 i^1, 22785080Swh31274 MAC_CNTL_MI_ERRS); 22795080Swh31274 } 22802867Shyw } 22812494Shyw } else if (stat & MAC_STAT_MI_ERRS) { 22822494Shyw /* 22832494Shyw * MI errors detected but we cannot 22842494Shyw * report them since the HW patrol 22852494Shyw * is still running. 22862494Shyw * We will attempt to speed up the 22872494Shyw * scanning and hopefully the HW 22882494Shyw * can detect PRTL errors at the same 22892494Shyw * location that cause the HW patrol 22902494Shyw * to stop. 22912494Shyw */ 22922867Shyw mcp->mc_speedup_period[ebk] = 2; 22932214Sav145390 MAC_CMD(mcp, i, 0); 22942214Sav145390 } 22952494Shyw } else if (stat & (MAC_STAT_PTRL_ERRS | 22962494Shyw MAC_STAT_MI_ERRS)) { 22972494Shyw /* 22982494Shyw * HW Patrol has stopped and we found errors. 22992494Shyw * Proceed to collect and report error info. 23002494Shyw */ 23012867Shyw mcp->mc_speedup_period[ebk] = 0; 23022662Shyw rsaddr_info.mi_valid = 0; 23032662Shyw rsaddr_info.mi_injectrestart = 0; 23042662Shyw if (IS_MIRROR(mcp, i)) { 23055080Swh31274 mc_error_handler_mir(mcp, i, 23065080Swh31274 &rsaddr_info); 23072662Shyw } else { 23085080Swh31274 mc_error_handler(mcp, i, &rsaddr_info); 23092662Shyw } 23102494Shyw 23112494Shyw error_count++; 23122662Shyw restart_patrol(mcp, i, &rsaddr_info); 23131772Sjl139090 } else { 23142494Shyw /* 23152494Shyw * HW patrol scan has apparently stopped 23162494Shyw * but no errors detected/flagged. 23172494Shyw * Restart the HW patrol just to be sure. 23182867Shyw * In mirror mode, the odd bank might have 23192867Shyw * reported errors that caused the patrol to 23202867Shyw * stop. We'll defer the restart to the odd 23212867Shyw * bank in this case. 23222494Shyw */ 23232867Shyw if (!IS_MIRROR(mcp, i) || (i & 0x1)) 23242867Shyw restart_patrol(mcp, i, NULL); 23251772Sjl139090 } 23261772Sjl139090 } 23271772Sjl139090 } 23281772Sjl139090 if (error_count > 0) 23291772Sjl139090 mcp->mc_last_error += error_count; 23301772Sjl139090 else 23311772Sjl139090 mcp->mc_last_error = 0; 23321772Sjl139090 } 23331772Sjl139090 23342214Sav145390 /* 23352214Sav145390 * mc_polling -- Check errors for only one instance, 23362214Sav145390 * but process errors for all instances to make sure we drain the errors 23372214Sav145390 * faster than they can be accumulated. 23382214Sav145390 * 23392214Sav145390 * Polling on each board should be done only once per each 23402214Sav145390 * mc_patrol_interval_sec. This is equivalent to setting mc_tick_left 23412214Sav145390 * to OPL_MAX_BOARDS and decrement by 1 on each timeout. 23422214Sav145390 * Once mc_tick_left becomes negative, the board becomes a candidate 23432214Sav145390 * for polling because it has waited for at least 23442214Sav145390 * mc_patrol_interval_sec's long. If mc_timeout_period is calculated 23453152Sav145390 * differently, this has to be updated accordingly. 23462214Sav145390 */ 23471772Sjl139090 23481772Sjl139090 static void 23492214Sav145390 mc_polling(void) 23501772Sjl139090 { 23512214Sav145390 int i, scan_error; 23522214Sav145390 mc_opl_t *mcp; 23532214Sav145390 23542214Sav145390 23552214Sav145390 scan_error = 1; 23562214Sav145390 for (i = 0; i < OPL_MAX_BOARDS; i++) { 23572214Sav145390 mutex_enter(&mcmutex); 23582214Sav145390 if ((mcp = mc_instances[i]) == NULL) { 23592214Sav145390 mutex_exit(&mcmutex); 23602214Sav145390 continue; 23612214Sav145390 } 23622214Sav145390 mutex_enter(&mcp->mc_lock); 23632214Sav145390 mutex_exit(&mcmutex); 23642662Shyw if (!(mcp->mc_status & MC_POLL_RUNNING)) { 23652662Shyw mutex_exit(&mcp->mc_lock); 23662662Shyw continue; 23672662Shyw } 23682214Sav145390 if (scan_error && mcp->mc_tick_left <= 0) { 23692214Sav145390 mc_check_errors_func((void *)mcp); 23702214Sav145390 mcp->mc_tick_left = OPL_MAX_BOARDS; 23712214Sav145390 scan_error = 0; 23722214Sav145390 } else { 23732214Sav145390 mcp->mc_tick_left--; 23742214Sav145390 } 23752214Sav145390 mc_process_scf_log(mcp); 23762214Sav145390 mutex_exit(&mcp->mc_lock); 23771772Sjl139090 } 23781772Sjl139090 } 23791772Sjl139090 23801772Sjl139090 static void 23811772Sjl139090 get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr) 23821772Sjl139090 { 23831772Sjl139090 maddr->ma_bd = mcp->mc_board_num; 23841772Sjl139090 maddr->ma_bank = bank; 23851772Sjl139090 maddr->ma_dimm_addr = 0; 23861772Sjl139090 } 23871772Sjl139090 23881772Sjl139090 typedef struct mc_mem_range { 23891772Sjl139090 uint64_t addr; 23901772Sjl139090 uint64_t size; 23911772Sjl139090 } mc_mem_range_t; 23921772Sjl139090 23931772Sjl139090 static int 23941772Sjl139090 get_base_address(mc_opl_t *mcp) 23951772Sjl139090 { 23961772Sjl139090 mc_mem_range_t *mem_range; 23971772Sjl139090 int len; 23981772Sjl139090 23991772Sjl139090 if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 24005080Swh31274 "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) { 24011772Sjl139090 return (DDI_FAILURE); 24021772Sjl139090 } 24031772Sjl139090 24041772Sjl139090 mcp->mc_start_address = mem_range->addr; 24051772Sjl139090 mcp->mc_size = mem_range->size; 24061772Sjl139090 24071772Sjl139090 kmem_free(mem_range, len); 24081772Sjl139090 return (DDI_SUCCESS); 24091772Sjl139090 } 24101772Sjl139090 24111772Sjl139090 struct mc_addr_spec { 24121772Sjl139090 uint32_t bank; 24131772Sjl139090 uint32_t phys_hi; 24141772Sjl139090 uint32_t phys_lo; 24151772Sjl139090 }; 24161772Sjl139090 24171772Sjl139090 #define REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo) 24181772Sjl139090 24191772Sjl139090 static char *mc_tbl_name[] = { 24201772Sjl139090 "cs0-mc-pa-trans-table", 24211772Sjl139090 "cs1-mc-pa-trans-table" 24221772Sjl139090 }; 24231772Sjl139090 24242662Shyw /* 24252662Shyw * This routine performs a rangecheck for a given PA 24262662Shyw * to see if it belongs to the memory range for this board. 24272662Shyw * Return 1 if it is valid (within the range) and 0 otherwise 24282662Shyw */ 24291772Sjl139090 static int 24302662Shyw mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa) 24311772Sjl139090 { 24325080Swh31274 if ((pa < mcp->mc_start_address) || (mcp->mc_start_address + 24335080Swh31274 mcp->mc_size <= pa)) 24342662Shyw return (0); 24352662Shyw else 24362662Shyw return (1); 24371772Sjl139090 } 24381772Sjl139090 24391772Sjl139090 static void 24401772Sjl139090 mc_memlist_delete(struct memlist *mlist) 24411772Sjl139090 { 24421772Sjl139090 struct memlist *ml; 24431772Sjl139090 24441772Sjl139090 for (ml = mlist; ml; ml = mlist) { 24451772Sjl139090 mlist = ml->next; 24461772Sjl139090 kmem_free(ml, sizeof (struct memlist)); 24471772Sjl139090 } 24481772Sjl139090 } 24491772Sjl139090 24501772Sjl139090 static struct memlist * 24511772Sjl139090 mc_memlist_dup(struct memlist *mlist) 24521772Sjl139090 { 24531772Sjl139090 struct memlist *hl = NULL, *tl, **mlp; 24541772Sjl139090 24551772Sjl139090 if (mlist == NULL) 24561772Sjl139090 return (NULL); 24571772Sjl139090 24581772Sjl139090 mlp = &hl; 24591772Sjl139090 tl = *mlp; 24601772Sjl139090 for (; mlist; mlist = mlist->next) { 24611772Sjl139090 *mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP); 24621772Sjl139090 (*mlp)->address = mlist->address; 24631772Sjl139090 (*mlp)->size = mlist->size; 24641772Sjl139090 (*mlp)->prev = tl; 24651772Sjl139090 tl = *mlp; 24661772Sjl139090 mlp = &((*mlp)->next); 24671772Sjl139090 } 24681772Sjl139090 *mlp = NULL; 24691772Sjl139090 24701772Sjl139090 return (hl); 24711772Sjl139090 } 24721772Sjl139090 24731772Sjl139090 24741772Sjl139090 static struct memlist * 24751772Sjl139090 mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len) 24761772Sjl139090 { 24771772Sjl139090 uint64_t end; 24781772Sjl139090 struct memlist *ml, *tl, *nlp; 24791772Sjl139090 24801772Sjl139090 if (mlist == NULL) 24811772Sjl139090 return (NULL); 24821772Sjl139090 24831772Sjl139090 end = base + len; 24841772Sjl139090 if ((end <= mlist->address) || (base == end)) 24851772Sjl139090 return (mlist); 24861772Sjl139090 24871772Sjl139090 for (tl = ml = mlist; ml; tl = ml, ml = nlp) { 24881772Sjl139090 uint64_t mend; 24891772Sjl139090 24901772Sjl139090 nlp = ml->next; 24911772Sjl139090 24921772Sjl139090 if (end <= ml->address) 24931772Sjl139090 break; 24941772Sjl139090 24951772Sjl139090 mend = ml->address + ml->size; 24961772Sjl139090 if (base < mend) { 24971772Sjl139090 if (base <= ml->address) { 24981772Sjl139090 ml->address = end; 24991772Sjl139090 if (end >= mend) 25001772Sjl139090 ml->size = 0ull; 25011772Sjl139090 else 25021772Sjl139090 ml->size = mend - ml->address; 25031772Sjl139090 } else { 25041772Sjl139090 ml->size = base - ml->address; 25051772Sjl139090 if (end < mend) { 25061772Sjl139090 struct memlist *nl; 25071772Sjl139090 /* 25081772Sjl139090 * splitting an memlist entry. 25091772Sjl139090 */ 25101772Sjl139090 nl = kmem_alloc(sizeof (struct memlist), 25115080Swh31274 KM_SLEEP); 25121772Sjl139090 nl->address = end; 25131772Sjl139090 nl->size = mend - nl->address; 25141772Sjl139090 if ((nl->next = nlp) != NULL) 25151772Sjl139090 nlp->prev = nl; 25161772Sjl139090 nl->prev = ml; 25171772Sjl139090 ml->next = nl; 25181772Sjl139090 nlp = nl; 25191772Sjl139090 } 25201772Sjl139090 } 25211772Sjl139090 if (ml->size == 0ull) { 25221772Sjl139090 if (ml == mlist) { 25231772Sjl139090 if ((mlist = nlp) != NULL) 25241772Sjl139090 nlp->prev = NULL; 25251772Sjl139090 kmem_free(ml, sizeof (struct memlist)); 25261772Sjl139090 if (mlist == NULL) 25271772Sjl139090 break; 25281772Sjl139090 ml = nlp; 25291772Sjl139090 } else { 25301772Sjl139090 if ((tl->next = nlp) != NULL) 25311772Sjl139090 nlp->prev = tl; 25321772Sjl139090 kmem_free(ml, sizeof (struct memlist)); 25331772Sjl139090 ml = tl; 25341772Sjl139090 } 25351772Sjl139090 } 25361772Sjl139090 } 25371772Sjl139090 } 25381772Sjl139090 25391772Sjl139090 return (mlist); 25401772Sjl139090 } 25411772Sjl139090 25421772Sjl139090 static void 25431772Sjl139090 mc_get_mlist(mc_opl_t *mcp) 25441772Sjl139090 { 25451772Sjl139090 struct memlist *mlist; 25461772Sjl139090 25471772Sjl139090 memlist_read_lock(); 25481772Sjl139090 mlist = mc_memlist_dup(phys_install); 25491772Sjl139090 memlist_read_unlock(); 25501772Sjl139090 25511772Sjl139090 if (mlist) { 25521772Sjl139090 mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address); 25531772Sjl139090 } 25541772Sjl139090 25551772Sjl139090 if (mlist) { 25561772Sjl139090 uint64_t startpa, endpa; 25571772Sjl139090 25581772Sjl139090 startpa = mcp->mc_start_address + mcp->mc_size; 25591772Sjl139090 endpa = ptob(physmax + 1); 25601772Sjl139090 if (endpa > startpa) { 25615080Swh31274 mlist = mc_memlist_del_span(mlist, startpa, 25625080Swh31274 endpa - startpa); 25631772Sjl139090 } 25641772Sjl139090 } 25651772Sjl139090 25661772Sjl139090 if (mlist) { 25671772Sjl139090 mcp->mlist = mlist; 25681772Sjl139090 } 25691772Sjl139090 } 25701772Sjl139090 25711772Sjl139090 int 25721772Sjl139090 mc_board_add(mc_opl_t *mcp) 25731772Sjl139090 { 25741772Sjl139090 struct mc_addr_spec *macaddr; 25752214Sav145390 cs_status_t *cs_status; 25762214Sav145390 int len, len1, i, bk, cc; 25772662Shyw mc_rsaddr_info_t rsaddr; 25781772Sjl139090 uint32_t mirr; 25792214Sav145390 int nbanks = 0; 25802214Sav145390 uint64_t nbytes = 0; 25815080Swh31274 int mirror_mode = 0; 25825080Swh31274 int ret; 25831772Sjl139090 25841772Sjl139090 /* 25851772Sjl139090 * Get configurations from "pseudo-mc" node which includes: 25861772Sjl139090 * board# : LSB number 25871772Sjl139090 * mac-addr : physical base address of MAC registers 25881772Sjl139090 * csX-mac-pa-trans-table: translation table from DIMM address 25891772Sjl139090 * to physical address or vice versa. 25901772Sjl139090 */ 25911772Sjl139090 mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip, 25925080Swh31274 DDI_PROP_DONTPASS, "board#", -1); 25931772Sjl139090 25942214Sav145390 if (mcp->mc_board_num == -1) { 25952214Sav145390 return (DDI_FAILURE); 25962214Sav145390 } 25972214Sav145390 25981772Sjl139090 /* 25991772Sjl139090 * Get start address in this CAB. It can be gotten from 26001772Sjl139090 * "sb-mem-ranges" property. 26011772Sjl139090 */ 26021772Sjl139090 26031772Sjl139090 if (get_base_address(mcp) == DDI_FAILURE) { 26041772Sjl139090 return (DDI_FAILURE); 26051772Sjl139090 } 26061772Sjl139090 /* get mac-pa trans tables */ 26071772Sjl139090 for (i = 0; i < MC_TT_CS; i++) { 26081772Sjl139090 len = MC_TT_ENTRIES; 26091772Sjl139090 cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip, 26105080Swh31274 DDI_PROP_DONTPASS, mc_tbl_name[i], 26115080Swh31274 (caddr_t)mcp->mc_trans_table[i], &len); 26121772Sjl139090 26131772Sjl139090 if (cc != DDI_SUCCESS) { 26141772Sjl139090 bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES); 26151772Sjl139090 } 26161772Sjl139090 } 26171772Sjl139090 mcp->mlist = NULL; 26181772Sjl139090 26191772Sjl139090 mc_get_mlist(mcp); 26201772Sjl139090 26211772Sjl139090 /* initialize bank informations */ 26221772Sjl139090 cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 26235080Swh31274 "mc-addr", (caddr_t)&macaddr, &len); 26241772Sjl139090 if (cc != DDI_SUCCESS) { 26251772Sjl139090 cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc); 26262214Sav145390 return (DDI_FAILURE); 26272214Sav145390 } 26282214Sav145390 26292214Sav145390 cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 26305080Swh31274 "cs-status", (caddr_t)&cs_status, &len1); 26312214Sav145390 26322214Sav145390 if (cc != DDI_SUCCESS) { 26332214Sav145390 if (len > 0) 26342214Sav145390 kmem_free(macaddr, len); 26352214Sav145390 cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc); 26361772Sjl139090 return (DDI_FAILURE); 26371772Sjl139090 } 26383045Sav145390 /* get the physical board number for a given logical board number */ 26393045Sav145390 mcp->mc_phys_board_num = mc_opl_get_physical_board(mcp->mc_board_num); 26403045Sav145390 26413045Sav145390 if (mcp->mc_phys_board_num < 0) { 26423045Sav145390 if (len > 0) 26433045Sav145390 kmem_free(macaddr, len); 26443045Sav145390 cmn_err(CE_WARN, "Unable to obtain the physical board number"); 26453045Sav145390 return (DDI_FAILURE); 26463045Sav145390 } 26471772Sjl139090 26482214Sav145390 mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL); 26492214Sav145390 26502214Sav145390 for (i = 0; i < len1 / sizeof (cs_status_t); i++) { 26512214Sav145390 nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) | 26525080Swh31274 ((uint64_t)cs_status[i].cs_avail_low); 26532214Sav145390 } 26542214Sav145390 if (len1 > 0) 26552214Sav145390 kmem_free(cs_status, len1); 26562214Sav145390 nbanks = len / sizeof (struct mc_addr_spec); 26572214Sav145390 26582214Sav145390 if (nbanks > 0) 26592214Sav145390 nbytes /= nbanks; 26602214Sav145390 else { 26612214Sav145390 /* No need to free macaddr because len must be 0 */ 26622214Sav145390 mcp->mc_status |= MC_MEMORYLESS; 26632214Sav145390 return (DDI_SUCCESS); 26642214Sav145390 } 26652214Sav145390 26662214Sav145390 for (i = 0; i < BANKNUM_PER_SB; i++) { 26672214Sav145390 mcp->mc_scf_retry[i] = 0; 26682214Sav145390 mcp->mc_period[i] = 0; 26692214Sav145390 mcp->mc_speedup_period[i] = 0; 26702214Sav145390 } 26712214Sav145390 26722214Sav145390 /* 26732214Sav145390 * Get the memory size here. Let it be B (bytes). 26742214Sav145390 * Let T be the time in u.s. to scan 64 bytes. 26752214Sav145390 * If we want to complete 1 round of scanning in P seconds. 26762214Sav145390 * 26772214Sav145390 * B * T * 10^(-6) = P 26782214Sav145390 * --------------- 26792214Sav145390 * 64 26802214Sav145390 * 26812214Sav145390 * T = P * 64 * 10^6 26822214Sav145390 * ------------- 26832214Sav145390 * B 26842214Sav145390 * 26852214Sav145390 * = P * 64 * 10^6 26862214Sav145390 * ------------- 26872214Sav145390 * B 26882214Sav145390 * 26892214Sav145390 * The timing bits are set in PTRL_CNTL[28:26] where 26902214Sav145390 * 26912214Sav145390 * 0 - 1 m.s 26922214Sav145390 * 1 - 512 u.s. 26932214Sav145390 * 10 - 256 u.s. 26942214Sav145390 * 11 - 128 u.s. 26952214Sav145390 * 100 - 64 u.s. 26962214Sav145390 * 101 - 32 u.s. 26972214Sav145390 * 110 - 0 u.s. 26982214Sav145390 * 111 - reserved. 26992214Sav145390 * 27002214Sav145390 * 27012214Sav145390 * a[0] = 110, a[1] = 101, ... a[6] = 0 27022214Sav145390 * 27032214Sav145390 * cs-status property is int x 7 27042214Sav145390 * 0 - cs# 27052214Sav145390 * 1 - cs-status 27062214Sav145390 * 2 - cs-avail.hi 27072214Sav145390 * 3 - cs-avail.lo 27082214Sav145390 * 4 - dimm-capa.hi 27092214Sav145390 * 5 - dimm-capa.lo 27102214Sav145390 * 6 - #of dimms 27112214Sav145390 */ 27122214Sav145390 27132214Sav145390 if (nbytes > 0) { 27142214Sav145390 int i; 27152214Sav145390 uint64_t ms; 27162214Sav145390 ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes; 27172214Sav145390 mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds; 27182214Sav145390 for (i = 0; i < MC_MAX_SPEEDS - 1; i++) { 27192214Sav145390 if (ms < mc_scan_speeds[i + 1].mc_period) { 27202214Sav145390 mcp->mc_speed = mc_scan_speeds[i].mc_speeds; 27212214Sav145390 break; 27222214Sav145390 } 27232214Sav145390 } 27242214Sav145390 } else 27252214Sav145390 mcp->mc_speed = 0; 27262214Sav145390 27272214Sav145390 27281772Sjl139090 for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) { 27291772Sjl139090 struct mc_bank *bankp; 27305310Sdhain mc_retry_info_t *retry; 27311772Sjl139090 uint32_t reg; 27325310Sdhain int k; 27331772Sjl139090 27341772Sjl139090 /* 27351772Sjl139090 * setup bank 27361772Sjl139090 */ 27371772Sjl139090 bk = macaddr[i].bank; 27381772Sjl139090 bankp = &(mcp->mc_bank[bk]); 27391772Sjl139090 bankp->mcb_status = BANK_INSTALLED; 27401772Sjl139090 bankp->mcb_reg_base = REGS_PA(macaddr, i); 27411772Sjl139090 27425310Sdhain bankp->mcb_retry_freelist = NULL; 27435310Sdhain bankp->mcb_retry_pending = NULL; 27445310Sdhain bankp->mcb_active = NULL; 27455310Sdhain retry = &bankp->mcb_retry_infos[0]; 27465310Sdhain for (k = 0; k < MC_RETRY_COUNT; k++, retry++) { 27475310Sdhain mc_retry_info_put(&bankp->mcb_retry_freelist, retry); 27485310Sdhain } 27495310Sdhain 27501772Sjl139090 reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk)); 27511772Sjl139090 bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS); 27521772Sjl139090 27531772Sjl139090 /* 27541772Sjl139090 * check if mirror mode 27551772Sjl139090 */ 27561772Sjl139090 mirr = LD_MAC_REG(MAC_MIRR(mcp, bk)); 27571772Sjl139090 27581772Sjl139090 if (mirr & MAC_MIRR_MIRROR_MODE) { 27595080Swh31274 MC_LOG("Mirror -> /LSB%d/B%d\n", mcp->mc_board_num, 27605080Swh31274 bk); 27611772Sjl139090 bankp->mcb_status |= BANK_MIRROR_MODE; 27625080Swh31274 mirror_mode = 1; 27631772Sjl139090 /* 27641772Sjl139090 * The following bit is only used for 27651772Sjl139090 * error injection. We should clear it 27661772Sjl139090 */ 27671772Sjl139090 if (mirr & MAC_MIRR_BANK_EXCLUSIVE) 27685080Swh31274 ST_MAC_REG(MAC_MIRR(mcp, bk), 0); 27691772Sjl139090 } 27701772Sjl139090 27711772Sjl139090 /* 27721772Sjl139090 * restart if not mirror mode or the other bank 27731772Sjl139090 * of the mirror is not running 27741772Sjl139090 */ 27751772Sjl139090 if (!(mirr & MAC_MIRR_MIRROR_MODE) || 27765080Swh31274 !(mcp->mc_bank[bk^1].mcb_status & BANK_PTRL_RUNNING)) { 27775080Swh31274 MC_LOG("Starting up /LSB%d/B%d\n", mcp->mc_board_num, 27785080Swh31274 bk); 27792662Shyw get_ptrl_start_address(mcp, bk, &rsaddr.mi_restartaddr); 27802662Shyw rsaddr.mi_valid = 0; 27812662Shyw rsaddr.mi_injectrestart = 0; 27822662Shyw restart_patrol(mcp, bk, &rsaddr); 27831772Sjl139090 } else { 27841772Sjl139090 MC_LOG("Not starting up /LSB%d/B%d\n", 27855080Swh31274 mcp->mc_board_num, bk); 27861772Sjl139090 } 27871772Sjl139090 bankp->mcb_status |= BANK_PTRL_RUNNING; 27881772Sjl139090 } 27892214Sav145390 if (len > 0) 27902214Sav145390 kmem_free(macaddr, len); 27912214Sav145390 27925080Swh31274 ret = ndi_prop_update_int(DDI_DEV_T_NONE, mcp->mc_dip, "mirror-mode", 27935080Swh31274 mirror_mode); 27945080Swh31274 if (ret != DDI_PROP_SUCCESS) { 27955080Swh31274 cmn_err(CE_WARN, "Unable to update mirror-mode property"); 27965080Swh31274 } 27975080Swh31274 27982214Sav145390 mcp->mc_dimm_list = mc_get_dimm_list(mcp); 27991772Sjl139090 28001772Sjl139090 /* 28011772Sjl139090 * set interval in HZ. 28021772Sjl139090 */ 28031772Sjl139090 mcp->mc_last_error = 0; 28042214Sav145390 28051772Sjl139090 /* restart memory patrol checking */ 28061772Sjl139090 mcp->mc_status |= MC_POLL_RUNNING; 28071772Sjl139090 28081772Sjl139090 return (DDI_SUCCESS); 28091772Sjl139090 } 28101772Sjl139090 28111772Sjl139090 int 28121772Sjl139090 mc_board_del(mc_opl_t *mcp) 28131772Sjl139090 { 28141772Sjl139090 int i; 28151772Sjl139090 scf_log_t *p; 28161772Sjl139090 28171772Sjl139090 /* 28181772Sjl139090 * cleanup mac state 28191772Sjl139090 */ 28201772Sjl139090 mutex_enter(&mcp->mc_lock); 28212214Sav145390 if (mcp->mc_status & MC_MEMORYLESS) { 28222214Sav145390 mutex_exit(&mcp->mc_lock); 28232214Sav145390 mutex_destroy(&mcp->mc_lock); 28242214Sav145390 return (DDI_SUCCESS); 28252214Sav145390 } 28261772Sjl139090 for (i = 0; i < BANKNUM_PER_SB; i++) { 28271772Sjl139090 if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 28281772Sjl139090 mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED; 28291772Sjl139090 } 28301772Sjl139090 } 28311772Sjl139090 28321772Sjl139090 /* stop memory patrol checking */ 28332662Shyw mcp->mc_status &= ~MC_POLL_RUNNING; 28341772Sjl139090 28351772Sjl139090 /* just throw away all the scf logs */ 28362214Sav145390 for (i = 0; i < BANKNUM_PER_SB; i++) { 28375080Swh31274 while ((p = mcp->mc_scf_log[i]) != NULL) { 28385080Swh31274 mcp->mc_scf_log[i] = p->sl_next; 28395080Swh31274 mcp->mc_scf_total[i]--; 28405080Swh31274 kmem_free(p, sizeof (scf_log_t)); 28415080Swh31274 } 28421772Sjl139090 } 28431772Sjl139090 28441772Sjl139090 if (mcp->mlist) 28451772Sjl139090 mc_memlist_delete(mcp->mlist); 28461772Sjl139090 28472214Sav145390 if (mcp->mc_dimm_list) 28482214Sav145390 mc_free_dimm_list(mcp->mc_dimm_list); 28492214Sav145390 28501772Sjl139090 mutex_exit(&mcp->mc_lock); 28511772Sjl139090 28521772Sjl139090 mutex_destroy(&mcp->mc_lock); 28531772Sjl139090 return (DDI_SUCCESS); 28541772Sjl139090 } 28551772Sjl139090 28561772Sjl139090 int 28571772Sjl139090 mc_suspend(mc_opl_t *mcp, uint32_t flag) 28581772Sjl139090 { 28591772Sjl139090 /* stop memory patrol checking */ 28601772Sjl139090 mutex_enter(&mcp->mc_lock); 28612214Sav145390 if (mcp->mc_status & MC_MEMORYLESS) { 28622214Sav145390 mutex_exit(&mcp->mc_lock); 28632214Sav145390 return (DDI_SUCCESS); 28642214Sav145390 } 28652214Sav145390 28662662Shyw mcp->mc_status &= ~MC_POLL_RUNNING; 28672662Shyw 28681772Sjl139090 mcp->mc_status |= flag; 28691772Sjl139090 mutex_exit(&mcp->mc_lock); 28701772Sjl139090 28711772Sjl139090 return (DDI_SUCCESS); 28721772Sjl139090 } 28731772Sjl139090 28743354Sjl139090 void 28753354Sjl139090 opl_mc_update_mlist(void) 28763354Sjl139090 { 28773354Sjl139090 int i; 28783354Sjl139090 mc_opl_t *mcp; 28793354Sjl139090 28803354Sjl139090 /* 28813354Sjl139090 * memory information is not updated until 28823354Sjl139090 * the post attach/detach stage during DR. 28833354Sjl139090 * This interface is used by dr_mem to inform 28843354Sjl139090 * mc-opl to update the mlist. 28853354Sjl139090 */ 28863354Sjl139090 28873354Sjl139090 mutex_enter(&mcmutex); 28883354Sjl139090 for (i = 0; i < OPL_MAX_BOARDS; i++) { 28893354Sjl139090 if ((mcp = mc_instances[i]) == NULL) 28903354Sjl139090 continue; 28913354Sjl139090 mutex_enter(&mcp->mc_lock); 28923354Sjl139090 if (mcp->mlist) 28933354Sjl139090 mc_memlist_delete(mcp->mlist); 28943354Sjl139090 mcp->mlist = NULL; 28953354Sjl139090 mc_get_mlist(mcp); 28963354Sjl139090 mutex_exit(&mcp->mc_lock); 28973354Sjl139090 } 28983354Sjl139090 mutex_exit(&mcmutex); 28993354Sjl139090 } 29003354Sjl139090 29011772Sjl139090 /* caller must clear the SUSPEND bits or this will do nothing */ 29021772Sjl139090 29031772Sjl139090 int 29041772Sjl139090 mc_resume(mc_opl_t *mcp, uint32_t flag) 29051772Sjl139090 { 29061772Sjl139090 int i; 29071772Sjl139090 uint64_t basepa; 29081772Sjl139090 29091772Sjl139090 mutex_enter(&mcp->mc_lock); 29102214Sav145390 if (mcp->mc_status & MC_MEMORYLESS) { 29112214Sav145390 mutex_exit(&mcp->mc_lock); 29122214Sav145390 return (DDI_SUCCESS); 29132214Sav145390 } 29141772Sjl139090 basepa = mcp->mc_start_address; 29151772Sjl139090 if (get_base_address(mcp) == DDI_FAILURE) { 29161772Sjl139090 mutex_exit(&mcp->mc_lock); 29171772Sjl139090 return (DDI_FAILURE); 29181772Sjl139090 } 29191772Sjl139090 29201772Sjl139090 if (basepa != mcp->mc_start_address) { 29211772Sjl139090 if (mcp->mlist) 29221772Sjl139090 mc_memlist_delete(mcp->mlist); 29231772Sjl139090 mcp->mlist = NULL; 29241772Sjl139090 mc_get_mlist(mcp); 29251772Sjl139090 } 29261772Sjl139090 29271772Sjl139090 mcp->mc_status &= ~flag; 29281772Sjl139090 29291772Sjl139090 if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 29301772Sjl139090 mutex_exit(&mcp->mc_lock); 29311772Sjl139090 return (DDI_SUCCESS); 29321772Sjl139090 } 29331772Sjl139090 29341772Sjl139090 if (!(mcp->mc_status & MC_POLL_RUNNING)) { 29351772Sjl139090 /* restart memory patrol checking */ 29361772Sjl139090 mcp->mc_status |= MC_POLL_RUNNING; 29371772Sjl139090 for (i = 0; i < BANKNUM_PER_SB; i++) { 29381772Sjl139090 if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 29395310Sdhain mc_check_errors_func(mcp); 29401772Sjl139090 } 29411772Sjl139090 } 29421772Sjl139090 } 29431772Sjl139090 mutex_exit(&mcp->mc_lock); 29441772Sjl139090 29451772Sjl139090 return (DDI_SUCCESS); 29461772Sjl139090 } 29471772Sjl139090 29481772Sjl139090 static mc_opl_t * 29491772Sjl139090 mc_pa_to_mcp(uint64_t pa) 29501772Sjl139090 { 29512214Sav145390 mc_opl_t *mcp; 29522214Sav145390 int i; 29532214Sav145390 29541772Sjl139090 ASSERT(MUTEX_HELD(&mcmutex)); 29552214Sav145390 for (i = 0; i < OPL_MAX_BOARDS; i++) { 29562214Sav145390 if ((mcp = mc_instances[i]) == NULL) 29572214Sav145390 continue; 29581772Sjl139090 /* if mac patrol is suspended, we cannot rely on it */ 29592214Sav145390 if (!(mcp->mc_status & MC_POLL_RUNNING) || 29605080Swh31274 (mcp->mc_status & MC_SOFT_SUSPENDED)) 29611772Sjl139090 continue; 29622662Shyw if (mc_rangecheck_pa(mcp, pa)) { 29632214Sav145390 return (mcp); 29641772Sjl139090 } 29651772Sjl139090 } 29661772Sjl139090 return (NULL); 29671772Sjl139090 } 29681772Sjl139090 29691772Sjl139090 /* 29701772Sjl139090 * Get Physical Board number from Logical one. 29711772Sjl139090 */ 29721772Sjl139090 static int 29731772Sjl139090 mc_opl_get_physical_board(int sb) 29741772Sjl139090 { 29751772Sjl139090 if (&opl_get_physical_board) { 29761772Sjl139090 return (opl_get_physical_board(sb)); 29771772Sjl139090 } 29781772Sjl139090 29791772Sjl139090 cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n"); 29801772Sjl139090 return (-1); 29811772Sjl139090 } 29821772Sjl139090 29831772Sjl139090 /* ARGSUSED */ 29841772Sjl139090 int 29851772Sjl139090 mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen, 29861772Sjl139090 int *lenp) 29871772Sjl139090 { 29882214Sav145390 int i; 29893045Sav145390 int j; 29902214Sav145390 int sb; 29911772Sjl139090 int bank; 29923045Sav145390 int cs; 29936297Sjl139090 int rv = 0; 29942214Sav145390 mc_opl_t *mcp; 29952214Sav145390 char memb_num; 29961772Sjl139090 29971772Sjl139090 mutex_enter(&mcmutex); 29981772Sjl139090 29991772Sjl139090 if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) || 30005080Swh31274 (!pa_is_valid(mcp, flt_addr))) { 30011772Sjl139090 mutex_exit(&mcmutex); 30021772Sjl139090 if (snprintf(buf, buflen, "UNKNOWN") >= buflen) { 30031772Sjl139090 return (ENOSPC); 30041772Sjl139090 } else { 30051772Sjl139090 if (lenp) 30061772Sjl139090 *lenp = strlen(buf); 30071772Sjl139090 } 30081772Sjl139090 return (0); 30091772Sjl139090 } 30101772Sjl139090 30111772Sjl139090 bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address); 30123045Sav145390 sb = mcp->mc_phys_board_num; 30133045Sav145390 cs = pa_to_cs(mcp, flt_addr - mcp->mc_start_address); 30141772Sjl139090 30151772Sjl139090 if (sb == -1) { 30161772Sjl139090 mutex_exit(&mcmutex); 30171772Sjl139090 return (ENXIO); 30181772Sjl139090 } 30191772Sjl139090 30206297Sjl139090 switch (plat_model) { 30216297Sjl139090 case MODEL_DC: 30222214Sav145390 i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 30233045Sav145390 j = (cs == 0) ? i : i + 2; 30243045Sav145390 snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s", 30252214Sav145390 model_names[plat_model].unit_name, sb, 30263045Sav145390 mc_dc_dimm_unum_table[j], 30273045Sav145390 mc_dc_dimm_unum_table[j + 1]); 30286297Sjl139090 break; 30296297Sjl139090 case MODEL_FF2: 30306297Sjl139090 case MODEL_FF1: 30312214Sav145390 i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 30323045Sav145390 j = (cs == 0) ? i : i + 2; 30332214Sav145390 memb_num = mc_ff_dimm_unum_table[i][0]; 30343045Sav145390 snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s", 30352214Sav145390 model_names[plat_model].unit_name, 30362214Sav145390 model_names[plat_model].mem_name, memb_num, 30373045Sav145390 &mc_ff_dimm_unum_table[j][1], 30383045Sav145390 &mc_ff_dimm_unum_table[j + 1][1]); 30396297Sjl139090 break; 30406297Sjl139090 case MODEL_IKKAKU: 30416297Sjl139090 i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 30426297Sjl139090 j = (cs == 0) ? i : i + 2; 30436297Sjl139090 snprintf(buf, buflen, "/%s/MEM%s MEM%s", 30446297Sjl139090 model_names[plat_model].unit_name, 30456297Sjl139090 &mc_ff_dimm_unum_table[j][1], 30466297Sjl139090 &mc_ff_dimm_unum_table[j + 1][1]); 30476297Sjl139090 break; 30486297Sjl139090 default: 30496297Sjl139090 rv = ENXIO; 30502214Sav145390 } 30512214Sav145390 if (lenp) { 30522214Sav145390 *lenp = strlen(buf); 30531772Sjl139090 } 30541772Sjl139090 mutex_exit(&mcmutex); 30556297Sjl139090 return (rv); 30561772Sjl139090 } 30571772Sjl139090 30581772Sjl139090 int 30592214Sav145390 opl_mc_suspend(void) 30601772Sjl139090 { 30611772Sjl139090 mc_opl_t *mcp; 30622214Sav145390 int i; 30631772Sjl139090 30641772Sjl139090 mutex_enter(&mcmutex); 30652214Sav145390 for (i = 0; i < OPL_MAX_BOARDS; i++) { 30662214Sav145390 if ((mcp = mc_instances[i]) == NULL) 30672214Sav145390 continue; 30682214Sav145390 mc_suspend(mcp, MC_SOFT_SUSPENDED); 30691772Sjl139090 } 30701772Sjl139090 mutex_exit(&mcmutex); 30712214Sav145390 30721772Sjl139090 return (0); 30731772Sjl139090 } 30741772Sjl139090 30751772Sjl139090 int 30762214Sav145390 opl_mc_resume(void) 30771772Sjl139090 { 30781772Sjl139090 mc_opl_t *mcp; 30792214Sav145390 int i; 30801772Sjl139090 30811772Sjl139090 mutex_enter(&mcmutex); 30822214Sav145390 for (i = 0; i < OPL_MAX_BOARDS; i++) { 30832214Sav145390 if ((mcp = mc_instances[i]) == NULL) 30842214Sav145390 continue; 30852214Sav145390 mc_resume(mcp, MC_SOFT_SUSPENDED); 30861772Sjl139090 } 30871772Sjl139090 mutex_exit(&mcmutex); 30882214Sav145390 30891772Sjl139090 return (0); 30901772Sjl139090 } 30911772Sjl139090 static void 30921772Sjl139090 insert_mcp(mc_opl_t *mcp) 30931772Sjl139090 { 30941772Sjl139090 mutex_enter(&mcmutex); 30952214Sav145390 if (mc_instances[mcp->mc_board_num] != NULL) { 30962214Sav145390 MC_LOG("mc-opl instance for board# %d already exists\n", 30975080Swh31274 mcp->mc_board_num); 30982214Sav145390 } 30992214Sav145390 mc_instances[mcp->mc_board_num] = mcp; 31001772Sjl139090 mutex_exit(&mcmutex); 31011772Sjl139090 } 31021772Sjl139090 31031772Sjl139090 static void 31041772Sjl139090 delete_mcp(mc_opl_t *mcp) 31051772Sjl139090 { 31062214Sav145390 mutex_enter(&mcmutex); 31072214Sav145390 mc_instances[mcp->mc_board_num] = 0; 31082214Sav145390 mutex_exit(&mcmutex); 31091772Sjl139090 } 31101772Sjl139090 31111772Sjl139090 /* Error injection interface */ 31121772Sjl139090 31132494Shyw static void 31142494Shyw mc_lock_va(uint64_t pa, caddr_t new_va) 31152494Shyw { 31162494Shyw tte_t tte; 31172494Shyw 31182662Shyw vtag_flushpage(new_va, (uint64_t)ksfmmup); 31195080Swh31274 sfmmu_memtte(&tte, pa >> PAGESHIFT, PROC_DATA|HAT_NOSYNC, TTE8K); 31202494Shyw tte.tte_intlo |= TTE_LCK_INT; 31212494Shyw sfmmu_dtlb_ld_kva(new_va, &tte); 31222494Shyw } 31232494Shyw 31242494Shyw static void 31252494Shyw mc_unlock_va(caddr_t va) 31262494Shyw { 31272494Shyw vtag_flushpage(va, (uint64_t)ksfmmup); 31282494Shyw } 31292494Shyw 31301772Sjl139090 /* ARGSUSED */ 31311772Sjl139090 int 31321772Sjl139090 mc_inject_error(int error_type, uint64_t pa, uint32_t flags) 31331772Sjl139090 { 31341772Sjl139090 mc_opl_t *mcp; 31351772Sjl139090 int bank; 31361772Sjl139090 uint32_t dimm_addr; 31371772Sjl139090 uint32_t cntl; 31382662Shyw mc_rsaddr_info_t rsaddr; 31391772Sjl139090 uint32_t data, stat; 31401772Sjl139090 int both_sides = 0; 31411772Sjl139090 uint64_t pa0; 31422494Shyw int extra_injection_needed = 0; 31431772Sjl139090 extern void cpu_flush_ecache(void); 31441772Sjl139090 31451772Sjl139090 MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags); 31461772Sjl139090 31471772Sjl139090 mutex_enter(&mcmutex); 31481772Sjl139090 if ((mcp = mc_pa_to_mcp(pa)) == NULL) { 31491772Sjl139090 mutex_exit(&mcmutex); 31501772Sjl139090 MC_LOG("mc_inject_error: invalid pa\n"); 31511772Sjl139090 return (ENOTSUP); 31521772Sjl139090 } 31531772Sjl139090 31541772Sjl139090 mutex_enter(&mcp->mc_lock); 31551772Sjl139090 mutex_exit(&mcmutex); 31561772Sjl139090 31571772Sjl139090 if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 31581772Sjl139090 mutex_exit(&mcp->mc_lock); 31591772Sjl139090 MC_LOG("mc-opl has been suspended. No error injection.\n"); 31601772Sjl139090 return (EBUSY); 31611772Sjl139090 } 31621772Sjl139090 31631772Sjl139090 /* convert pa to offset within the board */ 31641772Sjl139090 MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address); 31651772Sjl139090 31661772Sjl139090 if (!pa_is_valid(mcp, pa)) { 31671772Sjl139090 mutex_exit(&mcp->mc_lock); 31681772Sjl139090 return (EINVAL); 31691772Sjl139090 } 31701772Sjl139090 31711772Sjl139090 pa0 = pa - mcp->mc_start_address; 31721772Sjl139090 31731772Sjl139090 bank = pa_to_bank(mcp, pa0); 31741772Sjl139090 31751772Sjl139090 if (flags & MC_INJECT_FLAG_OTHER) 31761772Sjl139090 bank = bank ^ 1; 31771772Sjl139090 31781772Sjl139090 if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) { 31791772Sjl139090 mutex_exit(&mcp->mc_lock); 31801772Sjl139090 MC_LOG("Not mirror mode\n"); 31811772Sjl139090 return (EINVAL); 31821772Sjl139090 } 31831772Sjl139090 31841772Sjl139090 dimm_addr = pa_to_dimm(mcp, pa0); 31851772Sjl139090 31865080Swh31274 MC_LOG("injecting error to /LSB%d/B%d/%x\n", mcp->mc_board_num, bank, 31875080Swh31274 dimm_addr); 31881772Sjl139090 31891772Sjl139090 31901772Sjl139090 switch (error_type) { 31911772Sjl139090 case MC_INJECT_INTERMITTENT_MCE: 31921772Sjl139090 case MC_INJECT_PERMANENT_MCE: 31931772Sjl139090 case MC_INJECT_MUE: 31941772Sjl139090 both_sides = 1; 31951772Sjl139090 } 31961772Sjl139090 31971772Sjl139090 if (flags & MC_INJECT_FLAG_RESET) 31981772Sjl139090 ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0); 31991772Sjl139090 32001772Sjl139090 ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK); 32011772Sjl139090 32021772Sjl139090 if (both_sides) { 32031772Sjl139090 ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0); 32045080Swh31274 ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), dimm_addr & 32055080Swh31274 MAC_EG_ADD_MASK); 32061772Sjl139090 } 32071772Sjl139090 32081772Sjl139090 switch (error_type) { 32092494Shyw case MC_INJECT_SUE: 32102494Shyw extra_injection_needed = 1; 32112494Shyw /*FALLTHROUGH*/ 32121772Sjl139090 case MC_INJECT_UE: 32131772Sjl139090 case MC_INJECT_MUE: 32141772Sjl139090 if (flags & MC_INJECT_FLAG_PATH) { 32155080Swh31274 cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 | 32165080Swh31274 MAC_EG_FORCE_READ16 | MAC_EG_RDERR_ONCE; 32171772Sjl139090 } else { 32185080Swh31274 cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR00 | 32195080Swh31274 MAC_EG_FORCE_DERR16 | MAC_EG_DERR_ONCE; 32201772Sjl139090 } 32211772Sjl139090 flags |= MC_INJECT_FLAG_ST; 32221772Sjl139090 break; 32231772Sjl139090 case MC_INJECT_INTERMITTENT_CE: 32241772Sjl139090 case MC_INJECT_INTERMITTENT_MCE: 32251772Sjl139090 if (flags & MC_INJECT_FLAG_PATH) { 32265080Swh31274 cntl = MAC_EG_ADD_FIX |MAC_EG_FORCE_READ00 | 32275080Swh31274 MAC_EG_RDERR_ONCE; 32281772Sjl139090 } else { 32295080Swh31274 cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 | 32305080Swh31274 MAC_EG_DERR_ONCE; 32311772Sjl139090 } 32322494Shyw extra_injection_needed = 1; 32331772Sjl139090 flags |= MC_INJECT_FLAG_ST; 32341772Sjl139090 break; 32351772Sjl139090 case MC_INJECT_PERMANENT_CE: 32361772Sjl139090 case MC_INJECT_PERMANENT_MCE: 32371772Sjl139090 if (flags & MC_INJECT_FLAG_PATH) { 32385080Swh31274 cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 | 32395080Swh31274 MAC_EG_RDERR_ALWAYS; 32401772Sjl139090 } else { 32415080Swh31274 cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 | 32425080Swh31274 MAC_EG_DERR_ALWAYS; 32431772Sjl139090 } 32441772Sjl139090 flags |= MC_INJECT_FLAG_ST; 32451772Sjl139090 break; 32461772Sjl139090 case MC_INJECT_CMPE: 32471772Sjl139090 data = 0xabcdefab; 32481772Sjl139090 stphys(pa, data); 32491772Sjl139090 cpu_flush_ecache(); 32501772Sjl139090 MC_LOG("CMPE: writing data %x to %lx\n", data, pa); 32511772Sjl139090 ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE); 32521772Sjl139090 stphys(pa, data ^ 0xffffffff); 32532662Shyw membar_sync(); 32541772Sjl139090 cpu_flush_ecache(); 32551772Sjl139090 ST_MAC_REG(MAC_MIRR(mcp, bank), 0); 32561772Sjl139090 MC_LOG("CMPE: write new data %xto %lx\n", data, pa); 32571772Sjl139090 cntl = 0; 32581772Sjl139090 break; 32591772Sjl139090 case MC_INJECT_NOP: 32601772Sjl139090 cntl = 0; 32611772Sjl139090 break; 32621772Sjl139090 default: 32631772Sjl139090 MC_LOG("mc_inject_error: invalid option\n"); 32641772Sjl139090 cntl = 0; 32651772Sjl139090 } 32661772Sjl139090 32671772Sjl139090 if (cntl) { 32681772Sjl139090 ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK); 32691772Sjl139090 ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 32701772Sjl139090 32711772Sjl139090 if (both_sides) { 32721772Sjl139090 ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 32735080Swh31274 MAC_EG_SETUP_MASK); 32741772Sjl139090 ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 32751772Sjl139090 } 32761772Sjl139090 } 32771772Sjl139090 32781772Sjl139090 /* 32791772Sjl139090 * For all injection cases except compare error, we 32801772Sjl139090 * must write to the PA to trigger the error. 32811772Sjl139090 */ 32821772Sjl139090 32831772Sjl139090 if (flags & MC_INJECT_FLAG_ST) { 32841772Sjl139090 data = 0xf0e0d0c0; 32851772Sjl139090 MC_LOG("Writing %x to %lx\n", data, pa); 32861772Sjl139090 stphys(pa, data); 32871772Sjl139090 cpu_flush_ecache(); 32881772Sjl139090 } 32891772Sjl139090 32901772Sjl139090 32911772Sjl139090 if (flags & MC_INJECT_FLAG_LD) { 32922494Shyw if (flags & MC_INJECT_FLAG_PREFETCH) { 32932494Shyw /* 32942494Shyw * Use strong prefetch operation to 32952494Shyw * inject MI errors. 32962494Shyw */ 32972494Shyw page_t *pp; 32982494Shyw extern void mc_prefetch(caddr_t); 32992494Shyw 33002494Shyw MC_LOG("prefetch\n"); 33012494Shyw 33022494Shyw pp = page_numtopp_nolock(pa >> PAGESHIFT); 33032494Shyw if (pp != NULL) { 33042494Shyw caddr_t va, va1; 33052494Shyw 33062494Shyw va = ppmapin(pp, PROT_READ|PROT_WRITE, 33075080Swh31274 (caddr_t)-1); 33082494Shyw kpreempt_disable(); 33092494Shyw mc_lock_va((uint64_t)pa, va); 33102494Shyw va1 = va + (pa & (PAGESIZE - 1)); 33112494Shyw mc_prefetch(va1); 33122494Shyw mc_unlock_va(va); 33132494Shyw kpreempt_enable(); 33142494Shyw ppmapout(va); 33152494Shyw 33162494Shyw /* 33172494Shyw * For MI errors, we need one extra 33182494Shyw * injection for HW patrol to stop. 33192494Shyw */ 33202494Shyw extra_injection_needed = 1; 33211772Sjl139090 } else { 33222494Shyw cmn_err(CE_WARN, "Cannot find page structure" 33235080Swh31274 " for PA %lx\n", pa); 33241772Sjl139090 } 33251772Sjl139090 } else { 33261772Sjl139090 MC_LOG("Reading from %lx\n", pa); 33271772Sjl139090 data = ldphys(pa); 33281772Sjl139090 MC_LOG("data = %x\n", data); 33291772Sjl139090 } 33302494Shyw 33312494Shyw if (extra_injection_needed) { 33322494Shyw /* 33332494Shyw * These are the injection cases where the 33342494Shyw * requested injected errors will not cause the HW 33352494Shyw * patrol to stop. For these cases, we need to inject 33362494Shyw * an extra 'real' PTRL error to force the 33372494Shyw * HW patrol to stop so that we can report the 33382494Shyw * errors injected. Note that we cannot read 33392494Shyw * and report error status while the HW patrol 33402494Shyw * is running. 33412494Shyw */ 33422494Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 33435080Swh31274 cntl & MAC_EG_SETUP_MASK); 33442494Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 33452494Shyw 33462494Shyw if (both_sides) { 33475080Swh31274 ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 33485080Swh31274 MAC_EG_SETUP_MASK); 33495080Swh31274 ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 33502494Shyw } 33512494Shyw data = 0xf0e0d0c0; 33522494Shyw MC_LOG("Writing %x to %lx\n", data, pa); 33532494Shyw stphys(pa, data); 33542494Shyw cpu_flush_ecache(); 33552494Shyw } 33561772Sjl139090 } 33571772Sjl139090 33581772Sjl139090 if (flags & MC_INJECT_FLAG_RESTART) { 33591772Sjl139090 MC_LOG("Restart patrol\n"); 33602662Shyw rsaddr.mi_restartaddr.ma_bd = mcp->mc_board_num; 33612662Shyw rsaddr.mi_restartaddr.ma_bank = bank; 33622662Shyw rsaddr.mi_restartaddr.ma_dimm_addr = dimm_addr; 33632662Shyw rsaddr.mi_valid = 1; 33642662Shyw rsaddr.mi_injectrestart = 1; 33652662Shyw restart_patrol(mcp, bank, &rsaddr); 33661772Sjl139090 } 33671772Sjl139090 33681772Sjl139090 if (flags & MC_INJECT_FLAG_POLL) { 33692214Sav145390 int running; 33702867Shyw int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank; 33711772Sjl139090 33721772Sjl139090 MC_LOG("Poll patrol error\n"); 33731772Sjl139090 stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank)); 33741772Sjl139090 cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 33752214Sav145390 running = cntl & MAC_CNTL_PTRL_START; 33762494Shyw 33772494Shyw if (!running && 33782494Shyw (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS))) { 33792494Shyw /* 33802494Shyw * HW patrol stopped and we have errors to 33812494Shyw * report. Do it. 33822494Shyw */ 33832867Shyw mcp->mc_speedup_period[ebank] = 0; 33842662Shyw rsaddr.mi_valid = 0; 33852662Shyw rsaddr.mi_injectrestart = 0; 33862662Shyw if (IS_MIRROR(mcp, bank)) { 33872662Shyw mc_error_handler_mir(mcp, bank, &rsaddr); 33882662Shyw } else { 33892662Shyw mc_error_handler(mcp, bank, &rsaddr); 33902662Shyw } 33912662Shyw 33922662Shyw restart_patrol(mcp, bank, &rsaddr); 33932494Shyw } else { 33942494Shyw /* 33952494Shyw * We are expecting to report injected 33962494Shyw * errors but the HW patrol is still running. 33972494Shyw * Speed up the scanning 33982494Shyw */ 33992867Shyw mcp->mc_speedup_period[ebank] = 2; 34002494Shyw MAC_CMD(mcp, bank, 0); 34011772Sjl139090 restart_patrol(mcp, bank, NULL); 34022494Shyw } 34031772Sjl139090 } 34041772Sjl139090 34051772Sjl139090 mutex_exit(&mcp->mc_lock); 34061772Sjl139090 return (0); 34071772Sjl139090 } 34082494Shyw 34091772Sjl139090 void 34101772Sjl139090 mc_stphysio(uint64_t pa, uint32_t data) 34111772Sjl139090 { 34121772Sjl139090 MC_LOG("0x%x -> pa(%lx)\n", data, pa); 34131772Sjl139090 stphysio(pa, data); 34142214Sav145390 34152214Sav145390 /* force the above write to be processed by mac patrol */ 34162494Shyw data = ldphysio(pa); 34172494Shyw MC_LOG("pa(%lx) = 0x%x\n", pa, data); 34181772Sjl139090 } 34191772Sjl139090 34201772Sjl139090 uint32_t 34211772Sjl139090 mc_ldphysio(uint64_t pa) 34221772Sjl139090 { 34231772Sjl139090 uint32_t rv; 34241772Sjl139090 34251772Sjl139090 rv = ldphysio(pa); 34261772Sjl139090 MC_LOG("pa(%lx) = 0x%x\n", pa, rv); 34271772Sjl139090 return (rv); 34281772Sjl139090 } 34292214Sav145390 34302214Sav145390 #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 34312214Sav145390 34322214Sav145390 /* 34332214Sav145390 * parse_unum_memory -- extract the board number and the DIMM name from 34342214Sav145390 * the unum. 34352214Sav145390 * 34362214Sav145390 * Return 0 for success and non-zero for a failure. 34372214Sav145390 */ 34382214Sav145390 int 34392214Sav145390 parse_unum_memory(char *unum, int *board, char *dname) 34402214Sav145390 { 34412214Sav145390 char *c; 34422214Sav145390 char x, y, z; 34432214Sav145390 34442214Sav145390 if ((c = strstr(unum, "CMU")) != NULL) { 34452214Sav145390 /* DC Model */ 34462214Sav145390 c += 3; 34472214Sav145390 *board = (uint8_t)stoi(&c); 34482214Sav145390 if ((c = strstr(c, "MEM")) == NULL) { 34492214Sav145390 return (1); 34502214Sav145390 } 34512214Sav145390 c += 3; 34522214Sav145390 if (strlen(c) < 3) { 34532214Sav145390 return (2); 34542214Sav145390 } 34552214Sav145390 if ((!isdigit(c[0])) || (!(isdigit(c[1]))) || 34562214Sav145390 ((c[2] != 'A') && (c[2] != 'B'))) { 34572214Sav145390 return (3); 34582214Sav145390 } 34592214Sav145390 x = c[0]; 34602214Sav145390 y = c[1]; 34612214Sav145390 z = c[2]; 34622214Sav145390 } else if ((c = strstr(unum, "MBU_")) != NULL) { 34636297Sjl139090 /* FF1/FF2/Ikkaku Model */ 34642214Sav145390 c += 4; 34652214Sav145390 if ((c[0] != 'A') && (c[0] != 'B')) { 34662214Sav145390 return (4); 34672214Sav145390 } 34686297Sjl139090 if (plat_model == MODEL_IKKAKU) { 34696297Sjl139090 /* Ikkaku Model */ 34706297Sjl139090 x = '0'; 34716297Sjl139090 *board = 0; 34726297Sjl139090 } else { 34736297Sjl139090 /* FF1/FF2 Model */ 34746297Sjl139090 if ((c = strstr(c, "MEMB")) == NULL) { 34756297Sjl139090 return (5); 34766297Sjl139090 } 34776297Sjl139090 c += 4; 34786297Sjl139090 34796297Sjl139090 x = c[0]; 34806297Sjl139090 *board = ((uint8_t)stoi(&c)) / 4; 34812214Sav145390 } 34826297Sjl139090 34832214Sav145390 if ((c = strstr(c, "MEM")) == NULL) { 34842214Sav145390 return (6); 34852214Sav145390 } 34862214Sav145390 c += 3; 34872214Sav145390 if (strlen(c) < 2) { 34882214Sav145390 return (7); 34892214Sav145390 } 34902214Sav145390 if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) { 34912214Sav145390 return (8); 34922214Sav145390 } 34932214Sav145390 y = c[0]; 34942214Sav145390 z = c[1]; 34952214Sav145390 } else { 34962214Sav145390 return (9); 34972214Sav145390 } 34982214Sav145390 if (*board < 0) { 34992214Sav145390 return (10); 35002214Sav145390 } 35012214Sav145390 dname[0] = x; 35022214Sav145390 dname[1] = y; 35032214Sav145390 dname[2] = z; 35042214Sav145390 dname[3] = '\0'; 35052214Sav145390 return (0); 35062214Sav145390 } 35072214Sav145390 35082214Sav145390 /* 35092214Sav145390 * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and 35102214Sav145390 * the DIMM name. 35112214Sav145390 */ 35122214Sav145390 int 35132214Sav145390 mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 35142214Sav145390 int buflen, int *lenp) 35152214Sav145390 { 35162214Sav145390 int ret = ENODEV; 35172214Sav145390 mc_dimm_info_t *d = NULL; 35182214Sav145390 35192214Sav145390 if ((d = mcp->mc_dimm_list) == NULL) 35202214Sav145390 return (ENOTSUP); 35212214Sav145390 35222214Sav145390 for (; d != NULL; d = d->md_next) { 35232214Sav145390 if (strcmp(d->md_dimmname, dname) == 0) { 35242214Sav145390 break; 35252214Sav145390 } 35262214Sav145390 } 35272214Sav145390 if (d != NULL) { 35282214Sav145390 *lenp = strlen(d->md_serial) + strlen(d->md_partnum); 35292214Sav145390 if (buflen <= *lenp) { 35302214Sav145390 cmn_err(CE_WARN, "mc_get_mem_sid_dimm: " 35312214Sav145390 "buflen is smaller than %d\n", *lenp); 35322214Sav145390 ret = ENOSPC; 35332214Sav145390 } else { 35342214Sav145390 snprintf(buf, buflen, "%s:%s", 35352214Sav145390 d->md_serial, d->md_partnum); 35362214Sav145390 ret = 0; 35372214Sav145390 } 35382214Sav145390 } 35392214Sav145390 MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n", 35402214Sav145390 ret, dname, (ret == 0) ? buf : ""); 35412214Sav145390 return (ret); 35422214Sav145390 } 35432214Sav145390 35442214Sav145390 int 35453045Sav145390 mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int sb, 35462214Sav145390 int bank, uint32_t mf_type, uint32_t d_slot) 35472214Sav145390 { 35482214Sav145390 int lenp = buflen; 35492214Sav145390 int id; 35502214Sav145390 int ret; 35512214Sav145390 char *dimmnm; 35522214Sav145390 35535275Stsien if (mf_type == FLT_TYPE_INTERMITTENT_CE || 35545275Stsien mf_type == FLT_TYPE_PERMANENT_CE) { 35552214Sav145390 if (plat_model == MODEL_DC) { 35566297Sjl139090 /* 35576297Sjl139090 * All DC models 35586297Sjl139090 */ 35592214Sav145390 id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 35603045Sav145390 dimmnm = mc_dc_dimm_unum_table[id]; 35612214Sav145390 } else { 35626297Sjl139090 /* 35636297Sjl139090 * All FF and Ikkaku models 35646297Sjl139090 */ 35652214Sav145390 id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 35663045Sav145390 dimmnm = mc_ff_dimm_unum_table[id]; 35672214Sav145390 } 35682214Sav145390 if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen, 35692214Sav145390 &lenp)) != 0) { 35702214Sav145390 return (ret); 35712214Sav145390 } 35722214Sav145390 } else { 35732214Sav145390 return (1); 35742214Sav145390 } 35752214Sav145390 35762214Sav145390 return (0); 35772214Sav145390 } 35782214Sav145390 35792214Sav145390 /* 35802214Sav145390 * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum. 35812214Sav145390 */ 35822214Sav145390 int 35832214Sav145390 mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp) 35842214Sav145390 { 35852214Sav145390 int i; 35862214Sav145390 int ret = ENODEV; 35872214Sav145390 int board; 35882214Sav145390 char dname[MCOPL_MAX_DIMMNAME + 1]; 35892214Sav145390 mc_opl_t *mcp; 35902214Sav145390 35912214Sav145390 MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen); 35922214Sav145390 if ((ret = parse_unum_memory(unum, &board, dname)) != 0) { 35932214Sav145390 MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 35942214Sav145390 unum, ret); 35952214Sav145390 return (EINVAL); 35962214Sav145390 } 35972214Sav145390 35982214Sav145390 if (board < 0) { 35992214Sav145390 MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n", 36002214Sav145390 board, dname); 36012214Sav145390 return (EINVAL); 36022214Sav145390 } 36032214Sav145390 36042214Sav145390 mutex_enter(&mcmutex); 36053152Sav145390 /* 36063152Sav145390 * return ENOENT if we can not find the matching board. 36073152Sav145390 */ 36083152Sav145390 ret = ENOENT; 36092214Sav145390 for (i = 0; i < OPL_MAX_BOARDS; i++) { 36102214Sav145390 if ((mcp = mc_instances[i]) == NULL) 36112214Sav145390 continue; 36122214Sav145390 mutex_enter(&mcp->mc_lock); 36133045Sav145390 if (mcp->mc_phys_board_num != board) { 36143045Sav145390 mutex_exit(&mcp->mc_lock); 36153045Sav145390 continue; 36163045Sav145390 } 36173045Sav145390 ret = mc_get_mem_sid_dimm(mcp, dname, buf, buflen, lenp); 36183045Sav145390 if (ret == 0) { 36192214Sav145390 mutex_exit(&mcp->mc_lock); 36202214Sav145390 break; 36212214Sav145390 } 36222214Sav145390 mutex_exit(&mcp->mc_lock); 36232214Sav145390 } 36242214Sav145390 mutex_exit(&mcmutex); 36252214Sav145390 return (ret); 36262214Sav145390 } 36272214Sav145390 36282214Sav145390 /* 36292214Sav145390 * mc_get_mem_offset -- get the offset in a DIMM for a given physical address. 36302214Sav145390 */ 36312214Sav145390 int 36322214Sav145390 mc_get_mem_offset(uint64_t paddr, uint64_t *offp) 36332214Sav145390 { 36342214Sav145390 int i; 36352214Sav145390 int ret = ENODEV; 36362214Sav145390 mc_addr_t maddr; 36372214Sav145390 mc_opl_t *mcp; 36382214Sav145390 36392214Sav145390 mutex_enter(&mcmutex); 36402501Sraghuram for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) { 36412214Sav145390 if ((mcp = mc_instances[i]) == NULL) 36422214Sav145390 continue; 36432214Sav145390 mutex_enter(&mcp->mc_lock); 36442214Sav145390 if (!pa_is_valid(mcp, paddr)) { 36452214Sav145390 mutex_exit(&mcp->mc_lock); 36462214Sav145390 continue; 36472214Sav145390 } 36482214Sav145390 if (pa_to_maddr(mcp, paddr, &maddr) == 0) { 36492214Sav145390 *offp = maddr.ma_dimm_addr; 36502214Sav145390 ret = 0; 36512214Sav145390 } 36522214Sav145390 mutex_exit(&mcp->mc_lock); 36532214Sav145390 } 36542214Sav145390 mutex_exit(&mcmutex); 36552214Sav145390 MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n", 36562214Sav145390 ret, paddr, *offp); 36572214Sav145390 return (ret); 36582214Sav145390 } 36592214Sav145390 36602214Sav145390 /* 36612214Sav145390 * dname_to_bankslot - Get the bank and slot number from the DIMM name. 36622214Sav145390 */ 36632214Sav145390 int 36642214Sav145390 dname_to_bankslot(char *dname, int *bank, int *slot) 36652214Sav145390 { 36662214Sav145390 int i; 36672214Sav145390 int tsz; 36682214Sav145390 char **tbl; 36692214Sav145390 36706297Sjl139090 if (plat_model == MODEL_DC) { 36716297Sjl139090 /* 36726297Sjl139090 * All DC models 36736297Sjl139090 */ 36742214Sav145390 tbl = mc_dc_dimm_unum_table; 36752214Sav145390 tsz = OPL_MAX_DIMMS; 36762214Sav145390 } else { 36776297Sjl139090 /* 36786297Sjl139090 * All FF and Ikkaku models 36796297Sjl139090 */ 36802214Sav145390 tbl = mc_ff_dimm_unum_table; 36812214Sav145390 tsz = 2 * OPL_MAX_DIMMS; 36822214Sav145390 } 36832214Sav145390 36842214Sav145390 for (i = 0; i < tsz; i++) { 36852214Sav145390 if (strcmp(dname, tbl[i]) == 0) { 36862214Sav145390 break; 36872214Sav145390 } 36882214Sav145390 } 36892214Sav145390 if (i == tsz) { 36902214Sav145390 return (1); 36912214Sav145390 } 36922214Sav145390 *bank = INDEX_TO_BANK(i); 36932214Sav145390 *slot = INDEX_TO_SLOT(i); 36942214Sav145390 return (0); 36952214Sav145390 } 36962214Sav145390 36972214Sav145390 /* 36982214Sav145390 * mc_get_mem_addr -- get the physical address of a DIMM corresponding 36992214Sav145390 * to the unum and sid. 37002214Sav145390 */ 37012214Sav145390 int 37022214Sav145390 mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr) 37032214Sav145390 { 37042214Sav145390 int board; 37052214Sav145390 int bank; 37062214Sav145390 int slot; 37072214Sav145390 int i; 37082214Sav145390 int ret = ENODEV; 37092214Sav145390 char dname[MCOPL_MAX_DIMMNAME + 1]; 37102214Sav145390 mc_addr_t maddr; 37112214Sav145390 mc_opl_t *mcp; 37122214Sav145390 37132214Sav145390 MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n", 37142214Sav145390 unum, sid, offset); 37152214Sav145390 if (parse_unum_memory(unum, &board, dname) != 0) { 37162214Sav145390 MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 37172214Sav145390 unum, ret); 37182214Sav145390 return (EINVAL); 37192214Sav145390 } 37202214Sav145390 37212214Sav145390 if (board < 0) { 37222214Sav145390 MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n", 37232214Sav145390 board, dname); 37242214Sav145390 return (EINVAL); 37252214Sav145390 } 37262214Sav145390 37272214Sav145390 mutex_enter(&mcmutex); 37282214Sav145390 for (i = 0; i < OPL_MAX_BOARDS; i++) { 37292214Sav145390 if ((mcp = mc_instances[i]) == NULL) 37302214Sav145390 continue; 37312214Sav145390 mutex_enter(&mcp->mc_lock); 37323045Sav145390 if (mcp->mc_phys_board_num != board) { 37332214Sav145390 mutex_exit(&mcp->mc_lock); 37342214Sav145390 continue; 37352214Sav145390 } 37362214Sav145390 37372214Sav145390 ret = dname_to_bankslot(dname, &bank, &slot); 37382214Sav145390 MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot); 37392214Sav145390 if (ret != 0) { 37402214Sav145390 MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n"); 37412214Sav145390 ret = ENODEV; 37422214Sav145390 } else { 37433045Sav145390 maddr.ma_bd = mcp->mc_board_num; 37442214Sav145390 maddr.ma_bank = bank; 37452214Sav145390 maddr.ma_dimm_addr = offset; 37462214Sav145390 ret = mcaddr_to_pa(mcp, &maddr, paddr); 37472214Sav145390 if (ret != 0) { 37482214Sav145390 MC_LOG("mc_get_mem_addr: " 37492214Sav145390 "mcaddr_to_pa failed\n"); 37502214Sav145390 ret = ENODEV; 3751*6693Swh31274 mutex_exit(&mcp->mc_lock); 3752*6693Swh31274 continue; 37532214Sav145390 } 37543045Sav145390 mutex_exit(&mcp->mc_lock); 37553045Sav145390 break; 37562214Sav145390 } 37572214Sav145390 mutex_exit(&mcp->mc_lock); 37582214Sav145390 } 37592214Sav145390 mutex_exit(&mcmutex); 37602214Sav145390 MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr); 37612214Sav145390 return (ret); 37622214Sav145390 } 37632214Sav145390 37642214Sav145390 static void 37652214Sav145390 mc_free_dimm_list(mc_dimm_info_t *d) 37662214Sav145390 { 37672214Sav145390 mc_dimm_info_t *next; 37682214Sav145390 37692214Sav145390 while (d != NULL) { 37702214Sav145390 next = d->md_next; 37712214Sav145390 kmem_free(d, sizeof (mc_dimm_info_t)); 37722214Sav145390 d = next; 37732214Sav145390 } 37742214Sav145390 } 37752214Sav145390 37762214Sav145390 /* 37772214Sav145390 * mc_get_dimm_list -- get the list of dimms with serial-id info 37782214Sav145390 * from the SP. 37792214Sav145390 */ 37802214Sav145390 mc_dimm_info_t * 37812214Sav145390 mc_get_dimm_list(mc_opl_t *mcp) 37822214Sav145390 { 37832214Sav145390 uint32_t bufsz; 37842214Sav145390 uint32_t maxbufsz; 37852214Sav145390 int ret; 37862214Sav145390 int sexp; 37872214Sav145390 board_dimm_info_t *bd_dimmp; 37882214Sav145390 mc_dimm_info_t *dimm_list = NULL; 37892214Sav145390 37902214Sav145390 maxbufsz = bufsz = sizeof (board_dimm_info_t) + 37912214Sav145390 ((MCOPL_MAX_DIMMNAME + MCOPL_MAX_SERIAL + 37922214Sav145390 MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS); 37932214Sav145390 37942214Sav145390 bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP); 37952214Sav145390 ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz); 37962214Sav145390 37972214Sav145390 MC_LOG("mc_get_dimm_list: scf_service_getinfo returned=%d\n", ret); 37982214Sav145390 if (ret == 0) { 37992214Sav145390 sexp = sizeof (board_dimm_info_t) + 38002214Sav145390 ((bd_dimmp->bd_dnamesz + bd_dimmp->bd_serialsz + 38012214Sav145390 bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms); 38022214Sav145390 38032214Sav145390 if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) && 38042214Sav145390 (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) && 38052214Sav145390 (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) && 38062214Sav145390 (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) && 38072214Sav145390 (sexp <= bufsz)) { 38082214Sav145390 38092214Sav145390 #ifdef DEBUG 38102214Sav145390 if (oplmc_debug) 38112214Sav145390 mc_dump_dimm_info(bd_dimmp); 38122214Sav145390 #endif 38132214Sav145390 dimm_list = mc_prepare_dimmlist(bd_dimmp); 38142214Sav145390 38152214Sav145390 } else { 38162214Sav145390 cmn_err(CE_WARN, "DIMM info version mismatch\n"); 38172214Sav145390 } 38182214Sav145390 } 38192214Sav145390 kmem_free(bd_dimmp, maxbufsz); 38202214Sav145390 MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list); 38212214Sav145390 return (dimm_list); 38222214Sav145390 } 38232214Sav145390 38242214Sav145390 /* 38253152Sav145390 * mc_prepare_dimmlist - Prepare the dimm list from the information 38263152Sav145390 * received from the SP. 38272214Sav145390 */ 38282214Sav145390 mc_dimm_info_t * 38292214Sav145390 mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp) 38302214Sav145390 { 38312214Sav145390 char *dimm_name; 38322214Sav145390 char *serial; 38332214Sav145390 char *part; 38342214Sav145390 int dimm; 38352214Sav145390 int dnamesz = bd_dimmp->bd_dnamesz; 38362214Sav145390 int sersz = bd_dimmp->bd_serialsz; 38372214Sav145390 int partsz = bd_dimmp->bd_partnumsz; 38382214Sav145390 mc_dimm_info_t *dimm_list = NULL; 38392214Sav145390 mc_dimm_info_t *d; 38402214Sav145390 38412214Sav145390 dimm_name = (char *)(bd_dimmp + 1); 38422214Sav145390 for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 38432214Sav145390 38442214Sav145390 d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t), 38452214Sav145390 KM_SLEEP); 38463373Sbm42561 38473373Sbm42561 bcopy(dimm_name, d->md_dimmname, dnamesz); 38483373Sbm42561 d->md_dimmname[dnamesz] = 0; 38493373Sbm42561 38502214Sav145390 serial = dimm_name + dnamesz; 38513373Sbm42561 bcopy(serial, d->md_serial, sersz); 38523373Sbm42561 d->md_serial[sersz] = 0; 38533373Sbm42561 38542214Sav145390 part = serial + sersz; 38553373Sbm42561 bcopy(part, d->md_partnum, partsz); 38563373Sbm42561 d->md_partnum[partsz] = 0; 38572214Sav145390 38582214Sav145390 d->md_next = dimm_list; 38592214Sav145390 dimm_list = d; 38602214Sav145390 dimm_name = part + partsz; 38612214Sav145390 } 38622214Sav145390 return (dimm_list); 38632214Sav145390 } 38642214Sav145390 3865*6693Swh31274 static int 3866*6693Swh31274 mc_get_mem_fmri(mc_flt_page_t *fpag, char **unum) 3867*6693Swh31274 { 3868*6693Swh31274 if (fpag->fmri_addr == 0 || fpag->fmri_sz > MEM_FMRI_MAX_BUFSIZE) 3869*6693Swh31274 return (EINVAL); 3870*6693Swh31274 3871*6693Swh31274 *unum = kmem_alloc(fpag->fmri_sz, KM_SLEEP); 3872*6693Swh31274 if (copyin((void *)fpag->fmri_addr, *unum, fpag->fmri_sz) != 0) { 3873*6693Swh31274 kmem_free(*unum, fpag->fmri_sz); 3874*6693Swh31274 return (EFAULT); 3875*6693Swh31274 } 3876*6693Swh31274 return (0); 3877*6693Swh31274 } 3878*6693Swh31274 3879*6693Swh31274 static int 3880*6693Swh31274 mc_scf_log_event(mc_flt_page_t *flt_pag) 3881*6693Swh31274 { 3882*6693Swh31274 mc_opl_t *mcp; 3883*6693Swh31274 int board, bank, slot; 3884*6693Swh31274 int len, rv = 0; 3885*6693Swh31274 char *unum, *sid; 3886*6693Swh31274 char dname[MCOPL_MAX_DIMMNAME + 1]; 3887*6693Swh31274 size_t sid_sz; 3888*6693Swh31274 uint64_t pa; 3889*6693Swh31274 mc_flt_stat_t flt_stat; 3890*6693Swh31274 3891*6693Swh31274 if ((sid_sz = cpu_get_name_bufsize()) == 0) 3892*6693Swh31274 return (ENOTSUP); 3893*6693Swh31274 3894*6693Swh31274 if ((rv = mc_get_mem_fmri(flt_pag, &unum)) != 0) { 3895*6693Swh31274 MC_LOG("mc_scf_log_event: mc_get_mem_fmri failed\n"); 3896*6693Swh31274 return (rv); 3897*6693Swh31274 } 3898*6693Swh31274 3899*6693Swh31274 sid = kmem_zalloc(sid_sz, KM_SLEEP); 3900*6693Swh31274 3901*6693Swh31274 if ((rv = mc_get_mem_sid(unum, sid, sid_sz, &len)) != 0) { 3902*6693Swh31274 MC_LOG("mc_scf_log_event: mc_get_mem_sid failed\n"); 3903*6693Swh31274 goto out; 3904*6693Swh31274 } 3905*6693Swh31274 3906*6693Swh31274 if ((rv = mc_get_mem_addr(unum, sid, (uint64_t)flt_pag->err_add, 3907*6693Swh31274 &pa)) != 0) { 3908*6693Swh31274 MC_LOG("mc_scf_log_event: mc_get_mem_addr failed\n"); 3909*6693Swh31274 goto out; 3910*6693Swh31274 } 3911*6693Swh31274 3912*6693Swh31274 if (parse_unum_memory(unum, &board, dname) != 0) { 3913*6693Swh31274 MC_LOG("mc_scf_log_event: parse_unum_memory failed\n"); 3914*6693Swh31274 rv = EINVAL; 3915*6693Swh31274 goto out; 3916*6693Swh31274 } 3917*6693Swh31274 3918*6693Swh31274 if (board < 0) { 3919*6693Swh31274 MC_LOG("mc_scf_log_event: Invalid board=%d dimm=%s\n", 3920*6693Swh31274 board, dname); 3921*6693Swh31274 rv = EINVAL; 3922*6693Swh31274 goto out; 3923*6693Swh31274 } 3924*6693Swh31274 3925*6693Swh31274 if (dname_to_bankslot(dname, &bank, &slot) != 0) { 3926*6693Swh31274 MC_LOG("mc_scf_log_event: dname_to_bankslot failed\n"); 3927*6693Swh31274 rv = EINVAL; 3928*6693Swh31274 goto out; 3929*6693Swh31274 } 3930*6693Swh31274 3931*6693Swh31274 mutex_enter(&mcmutex); 3932*6693Swh31274 3933*6693Swh31274 flt_stat.mf_err_add = flt_pag->err_add; 3934*6693Swh31274 flt_stat.mf_err_log = flt_pag->err_log; 3935*6693Swh31274 flt_stat.mf_flt_paddr = pa; 3936*6693Swh31274 3937*6693Swh31274 if ((mcp = mc_pa_to_mcp(pa)) == NULL) { 3938*6693Swh31274 mutex_exit(&mcmutex); 3939*6693Swh31274 MC_LOG("mc_scf_log_event: invalid pa\n"); 3940*6693Swh31274 rv = EINVAL; 3941*6693Swh31274 goto out; 3942*6693Swh31274 } 3943*6693Swh31274 3944*6693Swh31274 MC_LOG("mc_scf_log_event: DIMM%s, /LSB%d/B%d/%x, pa %lx elog %x\n", 3945*6693Swh31274 unum, mcp->mc_board_num, bank, flt_pag->err_add, pa, 3946*6693Swh31274 flt_pag->err_log); 3947*6693Swh31274 3948*6693Swh31274 mutex_enter(&mcp->mc_lock); 3949*6693Swh31274 3950*6693Swh31274 if (!pa_is_valid(mcp, pa)) { 3951*6693Swh31274 mutex_exit(&mcp->mc_lock); 3952*6693Swh31274 mutex_exit(&mcmutex); 3953*6693Swh31274 rv = EINVAL; 3954*6693Swh31274 goto out; 3955*6693Swh31274 } 3956*6693Swh31274 3957*6693Swh31274 rv = 0; 3958*6693Swh31274 3959*6693Swh31274 mc_queue_scf_log(mcp, &flt_stat, bank); 3960*6693Swh31274 3961*6693Swh31274 mutex_exit(&mcp->mc_lock); 3962*6693Swh31274 mutex_exit(&mcmutex); 3963*6693Swh31274 3964*6693Swh31274 out: 3965*6693Swh31274 kmem_free(unum, flt_pag->fmri_sz); 3966*6693Swh31274 kmem_free(sid, sid_sz); 3967*6693Swh31274 3968*6693Swh31274 return (rv); 3969*6693Swh31274 } 3970*6693Swh31274 39712214Sav145390 #ifdef DEBUG 39722214Sav145390 void 39732214Sav145390 mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz) 39742214Sav145390 { 39752214Sav145390 char dname[MCOPL_MAX_DIMMNAME + 1]; 39762214Sav145390 char serial[MCOPL_MAX_SERIAL + 1]; 39772214Sav145390 char part[ MCOPL_MAX_PARTNUM + 1]; 39782214Sav145390 char *b; 39792214Sav145390 39802214Sav145390 b = buf; 39813373Sbm42561 bcopy(b, dname, dnamesz); 39823373Sbm42561 dname[dnamesz] = 0; 39833373Sbm42561 39842214Sav145390 b += dnamesz; 39853373Sbm42561 bcopy(b, serial, serialsz); 39863373Sbm42561 serial[serialsz] = 0; 39873373Sbm42561 39882214Sav145390 b += serialsz; 39893373Sbm42561 bcopy(b, part, partnumsz); 39903373Sbm42561 part[partnumsz] = 0; 39913373Sbm42561 39922214Sav145390 printf("DIMM=%s Serial=%s PartNum=%s\n", dname, serial, part); 39932214Sav145390 } 39942214Sav145390 39952214Sav145390 void 39962214Sav145390 mc_dump_dimm_info(board_dimm_info_t *bd_dimmp) 39972214Sav145390 { 39982214Sav145390 int dimm; 39992214Sav145390 int dnamesz = bd_dimmp->bd_dnamesz; 40002214Sav145390 int sersz = bd_dimmp->bd_serialsz; 40012214Sav145390 int partsz = bd_dimmp->bd_partnumsz; 40022214Sav145390 char *buf; 40032214Sav145390 40042214Sav145390 printf("Version=%d Board=%02d DIMMs=%d NameSize=%d " 40052214Sav145390 "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version, 40062214Sav145390 bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz, 40072214Sav145390 bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz); 40082214Sav145390 printf("======================================================\n"); 40092214Sav145390 40102214Sav145390 buf = (char *)(bd_dimmp + 1); 40112214Sav145390 for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 40122214Sav145390 mc_dump_dimm(buf, dnamesz, sersz, partsz); 40132214Sav145390 buf += dnamesz + sersz + partsz; 40142214Sav145390 } 40152214Sav145390 printf("======================================================\n"); 40162214Sav145390 } 40172214Sav145390 40182214Sav145390 40192214Sav145390 /* ARGSUSED */ 40202214Sav145390 static int 40212214Sav145390 mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 40222214Sav145390 int *rvalp) 40232214Sav145390 { 4024*6693Swh31274 caddr_t buf, kbuf; 40252214Sav145390 uint64_t pa; 40262214Sav145390 int rv = 0; 40272214Sav145390 int i; 40282214Sav145390 uint32_t flags; 40292214Sav145390 static uint32_t offset = 0; 40302214Sav145390 40312214Sav145390 40322214Sav145390 flags = (cmd >> 4) & 0xfffffff; 40332214Sav145390 40342214Sav145390 cmd &= 0xf; 40352214Sav145390 40362214Sav145390 MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags); 40372214Sav145390 40382214Sav145390 if (arg != NULL) { 40392214Sav145390 if (ddi_copyin((const void *)arg, (void *)&pa, 40405080Swh31274 sizeof (uint64_t), 0) < 0) { 40412214Sav145390 rv = EFAULT; 40422214Sav145390 return (rv); 40432214Sav145390 } 40442214Sav145390 buf = NULL; 40452214Sav145390 } else { 40462214Sav145390 buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP); 40472214Sav145390 40482214Sav145390 pa = va_to_pa(buf); 40492214Sav145390 pa += offset; 40502214Sav145390 40512214Sav145390 offset += 64; 40522214Sav145390 if (offset >= PAGESIZE) 40532214Sav145390 offset = 0; 40542214Sav145390 } 40552214Sav145390 40562214Sav145390 switch (cmd) { 40572214Sav145390 case MCI_CE: 40585080Swh31274 mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa, flags); 40592214Sav145390 break; 40602214Sav145390 case MCI_PERM_CE: 40615080Swh31274 mc_inject_error(MC_INJECT_PERMANENT_CE, pa, flags); 40622214Sav145390 break; 40632214Sav145390 case MCI_UE: 40645080Swh31274 mc_inject_error(MC_INJECT_UE, pa, flags); 40652214Sav145390 break; 40662214Sav145390 case MCI_M_CE: 40675080Swh31274 mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa, flags); 40682214Sav145390 break; 40692214Sav145390 case MCI_M_PCE: 40705080Swh31274 mc_inject_error(MC_INJECT_PERMANENT_MCE, pa, flags); 40712214Sav145390 break; 40722214Sav145390 case MCI_M_UE: 40735080Swh31274 mc_inject_error(MC_INJECT_MUE, pa, flags); 40742214Sav145390 break; 40752214Sav145390 case MCI_CMP: 40765080Swh31274 mc_inject_error(MC_INJECT_CMPE, pa, flags); 40772214Sav145390 break; 40782214Sav145390 case MCI_NOP: 40795080Swh31274 mc_inject_error(MC_INJECT_NOP, pa, flags); break; 40802214Sav145390 case MCI_SHOW_ALL: 40812214Sav145390 mc_debug_show_all = 1; 40822214Sav145390 break; 40832214Sav145390 case MCI_SHOW_NONE: 40842214Sav145390 mc_debug_show_all = 0; 40852214Sav145390 break; 40862214Sav145390 case MCI_ALLOC: 40872214Sav145390 /* 40882214Sav145390 * just allocate some kernel memory and never free it 40892214Sav145390 * 512 MB seems to be the maximum size supported. 40902214Sav145390 */ 40912214Sav145390 cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512); 40922214Sav145390 for (i = 0; i < flags; i++) { 4093*6693Swh31274 kbuf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP); 40942214Sav145390 cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n", 4095*6693Swh31274 (u_longlong_t)kbuf, (u_longlong_t)va_to_pa(kbuf)); 40962214Sav145390 } 40972214Sav145390 break; 40982214Sav145390 case MCI_SUSPEND: 40992214Sav145390 (void) opl_mc_suspend(); 41002214Sav145390 break; 41012214Sav145390 case MCI_RESUME: 41022214Sav145390 (void) opl_mc_resume(); 41032214Sav145390 break; 41042214Sav145390 default: 41052214Sav145390 rv = ENXIO; 41062214Sav145390 } 4107*6693Swh31274 if (buf) 4108*6693Swh31274 kmem_free(buf, PAGESIZE); 4109*6693Swh31274 41102214Sav145390 return (rv); 41112214Sav145390 } 41122214Sav145390 41132214Sav145390 #endif /* DEBUG */ 4114