125cf1a30Sjl /* 225cf1a30Sjl * CDDL HEADER START 325cf1a30Sjl * 425cf1a30Sjl * The contents of this file are subject to the terms of the 525cf1a30Sjl * Common Development and Distribution License (the "License"). 625cf1a30Sjl * You may not use this file except in compliance with the License. 725cf1a30Sjl * 825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925cf1a30Sjl * or http://www.opensolaris.org/os/licensing. 1025cf1a30Sjl * See the License for the specific language governing permissions 1125cf1a30Sjl * and limitations under the License. 1225cf1a30Sjl * 1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each 1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the 1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying 1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner] 1825cf1a30Sjl * 1925cf1a30Sjl * CDDL HEADER END 2025cf1a30Sjl */ 211e2e7a75Shuah /* 221e2e7a75Shuah * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 231e2e7a75Shuah * Use is subject to license terms. 241e2e7a75Shuah */ 2525cf1a30Sjl /* 2625cf1a30Sjl * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 2725cf1a30Sjl */ 2825cf1a30Sjl 2925cf1a30Sjl #pragma ident "%Z%%M% %I% %E% SMI" 3025cf1a30Sjl 3125cf1a30Sjl #include <sys/types.h> 3225cf1a30Sjl #include <sys/sysmacros.h> 3325cf1a30Sjl #include <sys/conf.h> 3425cf1a30Sjl #include <sys/modctl.h> 3525cf1a30Sjl #include <sys/stat.h> 3625cf1a30Sjl #include <sys/async.h> 371e2e7a75Shuah #include <sys/machcpuvar.h> 3825cf1a30Sjl #include <sys/machsystm.h> 390cc8ae86Sav #include <sys/promif.h> 4025cf1a30Sjl #include <sys/ksynch.h> 4125cf1a30Sjl #include <sys/ddi.h> 4225cf1a30Sjl #include <sys/sunddi.h> 4325cf1a30Sjl #include <sys/ddifm.h> 4425cf1a30Sjl #include <sys/fm/protocol.h> 4525cf1a30Sjl #include <sys/fm/util.h> 4625cf1a30Sjl #include <sys/kmem.h> 4725cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h> 4825cf1a30Sjl #include <sys/memlist.h> 4925cf1a30Sjl #include <sys/param.h> 500cc8ae86Sav #include <sys/disp.h> 5125cf1a30Sjl #include <vm/page.h> 5225cf1a30Sjl #include <sys/mc-opl.h> 530cc8ae86Sav #include <sys/opl.h> 540cc8ae86Sav #include <sys/opl_dimm.h> 550cc8ae86Sav #include <sys/scfd/scfostoescf.h> 56cfb9e062Shyw #include <sys/cpu_module.h> 57cfb9e062Shyw #include <vm/seg_kmem.h> 58cfb9e062Shyw #include <sys/vmem.h> 59cfb9e062Shyw #include <vm/hat_sfmmu.h> 60cfb9e062Shyw #include <sys/vmsystm.h> 61738dd194Shyw #include <sys/membar.h> 6225cf1a30Sjl 6325cf1a30Sjl /* 6425cf1a30Sjl * Function prototypes 6525cf1a30Sjl */ 6625cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *); 6725cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *); 6825cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 6925cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t); 7025cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t); 7125cf1a30Sjl 720cc8ae86Sav static int mc_poll_init(void); 730cc8ae86Sav static void mc_poll_fini(void); 7425cf1a30Sjl static int mc_board_add(mc_opl_t *mcp); 7525cf1a30Sjl static int mc_board_del(mc_opl_t *mcp); 7625cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag); 7725cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag); 780cc8ae86Sav int opl_mc_suspend(void); 790cc8ae86Sav int opl_mc_resume(void); 8025cf1a30Sjl 8125cf1a30Sjl static void insert_mcp(mc_opl_t *mcp); 8225cf1a30Sjl static void delete_mcp(mc_opl_t *mcp); 8325cf1a30Sjl 8425cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr); 8525cf1a30Sjl 86738dd194Shyw static int mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa); 8725cf1a30Sjl 8825cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *); 890cc8ae86Sav int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr); 900cc8ae86Sav int mc_get_mem_offset(uint64_t paddr, uint64_t *offp); 910cc8ae86Sav int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp); 920cc8ae86Sav int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 930cc8ae86Sav int buflen, int *lenp); 940cc8ae86Sav mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp); 950cc8ae86Sav mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp); 960cc8ae86Sav int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank, 970cc8ae86Sav uint32_t mf_type, uint32_t d_slot); 980cc8ae86Sav static void mc_free_dimm_list(mc_dimm_info_t *d); 9925cf1a30Sjl static void mc_get_mlist(mc_opl_t *); 1000cc8ae86Sav static void mc_polling(void); 1010cc8ae86Sav static int mc_opl_get_physical_board(int); 1020cc8ae86Sav 1030cc8ae86Sav #ifdef DEBUG 1040cc8ae86Sav static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *); 1050cc8ae86Sav void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz); 1060cc8ae86Sav void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp); 1070cc8ae86Sav #endif 10825cf1a30Sjl 10925cf1a30Sjl #pragma weak opl_get_physical_board 11025cf1a30Sjl extern int opl_get_physical_board(int); 1110cc8ae86Sav extern int plat_max_boards(void); 11225cf1a30Sjl 11325cf1a30Sjl /* 11425cf1a30Sjl * Configuration data structures 11525cf1a30Sjl */ 11625cf1a30Sjl static struct cb_ops mc_cb_ops = { 11725cf1a30Sjl mc_open, /* open */ 11825cf1a30Sjl mc_close, /* close */ 11925cf1a30Sjl nulldev, /* strategy */ 12025cf1a30Sjl nulldev, /* print */ 12125cf1a30Sjl nodev, /* dump */ 12225cf1a30Sjl nulldev, /* read */ 12325cf1a30Sjl nulldev, /* write */ 12425cf1a30Sjl mc_ioctl, /* ioctl */ 12525cf1a30Sjl nodev, /* devmap */ 12625cf1a30Sjl nodev, /* mmap */ 12725cf1a30Sjl nodev, /* segmap */ 12825cf1a30Sjl nochpoll, /* poll */ 12925cf1a30Sjl ddi_prop_op, /* cb_prop_op */ 13025cf1a30Sjl 0, /* streamtab */ 13125cf1a30Sjl D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 13225cf1a30Sjl CB_REV, /* rev */ 13325cf1a30Sjl nodev, /* cb_aread */ 13425cf1a30Sjl nodev /* cb_awrite */ 13525cf1a30Sjl }; 13625cf1a30Sjl 13725cf1a30Sjl static struct dev_ops mc_ops = { 13825cf1a30Sjl DEVO_REV, /* rev */ 13925cf1a30Sjl 0, /* refcnt */ 14025cf1a30Sjl ddi_getinfo_1to1, /* getinfo */ 14125cf1a30Sjl nulldev, /* identify */ 14225cf1a30Sjl nulldev, /* probe */ 14325cf1a30Sjl mc_attach, /* attach */ 14425cf1a30Sjl mc_detach, /* detach */ 14525cf1a30Sjl nulldev, /* reset */ 14625cf1a30Sjl &mc_cb_ops, /* cb_ops */ 14725cf1a30Sjl (struct bus_ops *)0, /* bus_ops */ 14825cf1a30Sjl nulldev /* power */ 14925cf1a30Sjl }; 15025cf1a30Sjl 15125cf1a30Sjl /* 15225cf1a30Sjl * Driver globals 15325cf1a30Sjl */ 15425cf1a30Sjl 1550cc8ae86Sav static enum { 1560cc8ae86Sav MODEL_FF1 = 0, 1570cc8ae86Sav MODEL_FF2 = 1, 1580cc8ae86Sav MODEL_DC = 2 1590cc8ae86Sav } plat_model = MODEL_DC; /* The default behaviour is DC */ 1600cc8ae86Sav 1610cc8ae86Sav static struct plat_model_names { 1620cc8ae86Sav const char *unit_name; 1630cc8ae86Sav const char *mem_name; 1640cc8ae86Sav } model_names[] = { 1650cc8ae86Sav { "MBU_A", "MEMB" }, 1660cc8ae86Sav { "MBU_B", "MEMB" }, 1670cc8ae86Sav { "CMU", "" } 1680cc8ae86Sav }; 16925cf1a30Sjl 1700cc8ae86Sav /* 1710cc8ae86Sav * The DIMM Names for DC platform. 1720cc8ae86Sav * The index into this table is made up of (bank, dslot), 1730cc8ae86Sav * Where dslot occupies bits 0-1 and bank occupies 2-4. 1740cc8ae86Sav */ 1750cc8ae86Sav static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = { 1760cc8ae86Sav /* --------CMUnn----------- */ 1770cc8ae86Sav /* --CS0-----|--CS1------ */ 1780cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 179c964b0e6Sraghuram "03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */ 180c964b0e6Sraghuram "13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */ 181c964b0e6Sraghuram "23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */ 182c964b0e6Sraghuram "33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */ 183c964b0e6Sraghuram "01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */ 184c964b0e6Sraghuram "11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */ 185c964b0e6Sraghuram "21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */ 186c964b0e6Sraghuram "31A", "30A", "31B", "30B" /* Bank 7 (MAC 3 bank 1) */ 1870cc8ae86Sav }; 1880cc8ae86Sav 1890cc8ae86Sav /* 1900cc8ae86Sav * The DIMM Names for FF1/FF2 platforms. 1910cc8ae86Sav * The index into this table is made up of (board, bank, dslot), 1920cc8ae86Sav * Where dslot occupies bits 0-1, bank occupies 2-4 and 1930cc8ae86Sav * board occupies the bit 5. 1940cc8ae86Sav */ 1950cc8ae86Sav static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = { 1960cc8ae86Sav /* --------CMU0---------- */ 1970cc8ae86Sav /* --CS0-----|--CS1------ */ 1980cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 199c964b0e6Sraghuram "03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */ 200c964b0e6Sraghuram "01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */ 201c964b0e6Sraghuram "13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */ 202c964b0e6Sraghuram "11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */ 203c964b0e6Sraghuram "23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */ 204c964b0e6Sraghuram "21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */ 205c964b0e6Sraghuram "33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */ 206c964b0e6Sraghuram "31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */ 2070cc8ae86Sav /* --------CMU1---------- */ 2080cc8ae86Sav /* --CS0-----|--CS1------ */ 2090cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 210c964b0e6Sraghuram "43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */ 211c964b0e6Sraghuram "41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */ 212c964b0e6Sraghuram "53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */ 213c964b0e6Sraghuram "51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */ 214c964b0e6Sraghuram "63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */ 215c964b0e6Sraghuram "61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */ 216c964b0e6Sraghuram "73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */ 217c964b0e6Sraghuram "71A", "70A", "71B", "70B" /* Bank 7 (MAC 3 bank 1) */ 2180cc8ae86Sav }; 2190cc8ae86Sav 2200cc8ae86Sav #define BD_BK_SLOT_TO_INDEX(bd, bk, s) \ 2210cc8ae86Sav (((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03)) 2220cc8ae86Sav 2230cc8ae86Sav #define INDEX_TO_BANK(i) (((i) & 0x1C) >> 2) 2240cc8ae86Sav #define INDEX_TO_SLOT(i) ((i) & 0x03) 2250cc8ae86Sav 226*aeb241b2Sav #define SLOT_TO_CS(slot) ((slot & 0x3) >> 1) 227*aeb241b2Sav 2280cc8ae86Sav /* Isolation unit size is 64 MB */ 2290cc8ae86Sav #define MC_ISOLATION_BSIZE (64 * 1024 * 1024) 2300cc8ae86Sav 2310cc8ae86Sav #define MC_MAX_SPEEDS 7 2320cc8ae86Sav 2330cc8ae86Sav typedef struct { 2340cc8ae86Sav uint32_t mc_speeds; 2350cc8ae86Sav uint32_t mc_period; 2360cc8ae86Sav } mc_scan_speed_t; 2370cc8ae86Sav 2380cc8ae86Sav #define MC_CNTL_SPEED_SHIFT 26 2390cc8ae86Sav 24037afe445Shyw /* 24137afe445Shyw * In mirror mode, we normalized the bank idx to "even" since 24237afe445Shyw * the HW treats them as one unit w.r.t programming. 24337afe445Shyw * This bank index will be the "effective" bank index. 24437afe445Shyw * All mirrored bank state info on mc_period, mc_speedup_period 24537afe445Shyw * will be stored in the even bank structure to avoid code duplication. 24637afe445Shyw */ 24737afe445Shyw #define MIRROR_IDX(bankidx) (bankidx & ~1) 24837afe445Shyw 2490cc8ae86Sav static mc_scan_speed_t mc_scan_speeds[MC_MAX_SPEEDS] = { 2500cc8ae86Sav {0x6 << MC_CNTL_SPEED_SHIFT, 0}, 2510cc8ae86Sav {0x5 << MC_CNTL_SPEED_SHIFT, 32}, 2520cc8ae86Sav {0x4 << MC_CNTL_SPEED_SHIFT, 64}, 2530cc8ae86Sav {0x3 << MC_CNTL_SPEED_SHIFT, 128}, 2540cc8ae86Sav {0x2 << MC_CNTL_SPEED_SHIFT, 256}, 2550cc8ae86Sav {0x1 << MC_CNTL_SPEED_SHIFT, 512}, 2560cc8ae86Sav {0x0 << MC_CNTL_SPEED_SHIFT, 1024} 2570cc8ae86Sav }; 2580cc8ae86Sav 2590cc8ae86Sav static uint32_t mc_max_speed = (0x6 << 26); 2600cc8ae86Sav 2610cc8ae86Sav int mc_isolation_bsize = MC_ISOLATION_BSIZE; 2620cc8ae86Sav int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC; 2630cc8ae86Sav int mc_max_scf_retry = 16; 2640cc8ae86Sav int mc_max_scf_logs = 64; 2650cc8ae86Sav int mc_max_errlog_processed = BANKNUM_PER_SB*2; 2660cc8ae86Sav int mc_scan_period = 12 * 60 * 60; /* 12 hours period */ 2670cc8ae86Sav int mc_max_rewrite_loop = 100; 2680cc8ae86Sav int mc_rewrite_delay = 10; 2690cc8ae86Sav /* 2700cc8ae86Sav * it takes SCF about 300 m.s. to process a requst. We can bail out 2710cc8ae86Sav * if it is busy. It does not pay to wait for it too long. 2720cc8ae86Sav */ 2730cc8ae86Sav int mc_max_scf_loop = 2; 2740cc8ae86Sav int mc_scf_delay = 100; 2750cc8ae86Sav int mc_pce_dropped = 0; 2760cc8ae86Sav int mc_poll_priority = MINCLSYSPRI; 27725cf1a30Sjl 2780cc8ae86Sav 2790cc8ae86Sav /* 2800cc8ae86Sav * Mutex heierachy in mc-opl 2810cc8ae86Sav * If both mcmutex and mc_lock must be held, 2820cc8ae86Sav * mcmutex must be acquired first, and then mc_lock. 2830cc8ae86Sav */ 2840cc8ae86Sav 2850cc8ae86Sav static kmutex_t mcmutex; 2860cc8ae86Sav mc_opl_t *mc_instances[OPL_MAX_BOARDS]; 2870cc8ae86Sav 2880cc8ae86Sav static kmutex_t mc_polling_lock; 2890cc8ae86Sav static kcondvar_t mc_polling_cv; 2900cc8ae86Sav static kcondvar_t mc_poll_exit_cv; 2910cc8ae86Sav static int mc_poll_cmd = 0; 2920cc8ae86Sav static int mc_pollthr_running = 0; 2930cc8ae86Sav int mc_timeout_period = 0; /* this is in m.s. */ 29425cf1a30Sjl void *mc_statep; 29525cf1a30Sjl 29625cf1a30Sjl #ifdef DEBUG 2972742aa22Shyw int oplmc_debug = 0; 29825cf1a30Sjl #endif 29925cf1a30Sjl 3000cc8ae86Sav static int mc_debug_show_all = 0; 30125cf1a30Sjl 30225cf1a30Sjl extern struct mod_ops mod_driverops; 30325cf1a30Sjl 30425cf1a30Sjl static struct modldrv modldrv = { 30525cf1a30Sjl &mod_driverops, /* module type, this one is a driver */ 3060cc8ae86Sav "OPL Memory-controller %I%", /* module name */ 30725cf1a30Sjl &mc_ops, /* driver ops */ 30825cf1a30Sjl }; 30925cf1a30Sjl 31025cf1a30Sjl static struct modlinkage modlinkage = { 31125cf1a30Sjl MODREV_1, /* rev */ 31225cf1a30Sjl (void *)&modldrv, 31325cf1a30Sjl NULL 31425cf1a30Sjl }; 31525cf1a30Sjl 31625cf1a30Sjl #pragma weak opl_get_mem_unum 3170cc8ae86Sav #pragma weak opl_get_mem_sid 3180cc8ae86Sav #pragma weak opl_get_mem_offset 3190cc8ae86Sav #pragma weak opl_get_mem_addr 3200cc8ae86Sav 32125cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *); 3220cc8ae86Sav extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp); 3230cc8ae86Sav extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp); 3240cc8ae86Sav extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset, 3250cc8ae86Sav uint64_t *paddr); 3260cc8ae86Sav 32725cf1a30Sjl 32825cf1a30Sjl /* 32925cf1a30Sjl * pseudo-mc node portid format 33025cf1a30Sjl * 33125cf1a30Sjl * [10] = 0 33225cf1a30Sjl * [9] = 1 33325cf1a30Sjl * [8] = LSB_ID[4] = 0 33425cf1a30Sjl * [7:4] = LSB_ID[3:0] 33525cf1a30Sjl * [3:0] = 0 33625cf1a30Sjl * 33725cf1a30Sjl */ 33825cf1a30Sjl 33925cf1a30Sjl /* 34025cf1a30Sjl * These are the module initialization routines. 34125cf1a30Sjl */ 34225cf1a30Sjl int 34325cf1a30Sjl _init(void) 34425cf1a30Sjl { 3450cc8ae86Sav int error; 3460cc8ae86Sav int plen; 3470cc8ae86Sav char model[20]; 3480cc8ae86Sav pnode_t node; 34925cf1a30Sjl 35025cf1a30Sjl 35125cf1a30Sjl if ((error = ddi_soft_state_init(&mc_statep, 35225cf1a30Sjl sizeof (mc_opl_t), 1)) != 0) 35325cf1a30Sjl return (error); 35425cf1a30Sjl 3550cc8ae86Sav if ((error = mc_poll_init()) != 0) { 3560cc8ae86Sav ddi_soft_state_fini(&mc_statep); 3570cc8ae86Sav return (error); 3580cc8ae86Sav } 3590cc8ae86Sav 36025cf1a30Sjl mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL); 36125cf1a30Sjl if (&opl_get_mem_unum) 36225cf1a30Sjl opl_get_mem_unum = mc_get_mem_unum; 3630cc8ae86Sav if (&opl_get_mem_sid) 3640cc8ae86Sav opl_get_mem_sid = mc_get_mem_sid; 3650cc8ae86Sav if (&opl_get_mem_offset) 3660cc8ae86Sav opl_get_mem_offset = mc_get_mem_offset; 3670cc8ae86Sav if (&opl_get_mem_addr) 3680cc8ae86Sav opl_get_mem_addr = mc_get_mem_addr; 3690cc8ae86Sav 3700cc8ae86Sav node = prom_rootnode(); 3710cc8ae86Sav plen = prom_getproplen(node, "model"); 3720cc8ae86Sav 3730cc8ae86Sav if (plen > 0 && plen < sizeof (model)) { 3740cc8ae86Sav (void) prom_getprop(node, "model", model); 3750cc8ae86Sav model[plen] = '\0'; 3760cc8ae86Sav if (strcmp(model, "FF1") == 0) 3770cc8ae86Sav plat_model = MODEL_FF1; 3780cc8ae86Sav else if (strcmp(model, "FF2") == 0) 3790cc8ae86Sav plat_model = MODEL_FF2; 3800cc8ae86Sav else if (strncmp(model, "DC", 2) == 0) 3810cc8ae86Sav plat_model = MODEL_DC; 3820cc8ae86Sav } 38325cf1a30Sjl 38425cf1a30Sjl error = mod_install(&modlinkage); 38525cf1a30Sjl if (error != 0) { 38625cf1a30Sjl if (&opl_get_mem_unum) 38725cf1a30Sjl opl_get_mem_unum = NULL; 3880cc8ae86Sav if (&opl_get_mem_sid) 3890cc8ae86Sav opl_get_mem_sid = NULL; 3900cc8ae86Sav if (&opl_get_mem_offset) 3910cc8ae86Sav opl_get_mem_offset = NULL; 3920cc8ae86Sav if (&opl_get_mem_addr) 3930cc8ae86Sav opl_get_mem_addr = NULL; 39425cf1a30Sjl mutex_destroy(&mcmutex); 3950cc8ae86Sav mc_poll_fini(); 39625cf1a30Sjl ddi_soft_state_fini(&mc_statep); 39725cf1a30Sjl } 39825cf1a30Sjl return (error); 39925cf1a30Sjl } 40025cf1a30Sjl 40125cf1a30Sjl int 40225cf1a30Sjl _fini(void) 40325cf1a30Sjl { 40425cf1a30Sjl int error; 40525cf1a30Sjl 40625cf1a30Sjl if ((error = mod_remove(&modlinkage)) != 0) 40725cf1a30Sjl return (error); 40825cf1a30Sjl 40925cf1a30Sjl if (&opl_get_mem_unum) 41025cf1a30Sjl opl_get_mem_unum = NULL; 4110cc8ae86Sav if (&opl_get_mem_sid) 4120cc8ae86Sav opl_get_mem_sid = NULL; 4130cc8ae86Sav if (&opl_get_mem_offset) 4140cc8ae86Sav opl_get_mem_offset = NULL; 4150cc8ae86Sav if (&opl_get_mem_addr) 4160cc8ae86Sav opl_get_mem_addr = NULL; 41725cf1a30Sjl 4180cc8ae86Sav mutex_destroy(&mcmutex); 4190cc8ae86Sav mc_poll_fini(); 42025cf1a30Sjl ddi_soft_state_fini(&mc_statep); 42125cf1a30Sjl 42225cf1a30Sjl return (0); 42325cf1a30Sjl } 42425cf1a30Sjl 42525cf1a30Sjl int 42625cf1a30Sjl _info(struct modinfo *modinfop) 42725cf1a30Sjl { 42825cf1a30Sjl return (mod_info(&modlinkage, modinfop)); 42925cf1a30Sjl } 43025cf1a30Sjl 4310cc8ae86Sav static void 4320cc8ae86Sav mc_polling_thread() 4330cc8ae86Sav { 4340cc8ae86Sav mutex_enter(&mc_polling_lock); 4350cc8ae86Sav mc_pollthr_running = 1; 4360cc8ae86Sav while (!(mc_poll_cmd & MC_POLL_EXIT)) { 4370cc8ae86Sav mc_polling(); 4380cc8ae86Sav cv_timedwait(&mc_polling_cv, &mc_polling_lock, 4390cc8ae86Sav ddi_get_lbolt() + mc_timeout_period); 4400cc8ae86Sav } 4410cc8ae86Sav mc_pollthr_running = 0; 4420cc8ae86Sav 4430cc8ae86Sav /* 4440cc8ae86Sav * signal if any one is waiting for this thread to exit. 4450cc8ae86Sav */ 4460cc8ae86Sav cv_signal(&mc_poll_exit_cv); 4470cc8ae86Sav mutex_exit(&mc_polling_lock); 4480cc8ae86Sav thread_exit(); 4490cc8ae86Sav /* NOTREACHED */ 4500cc8ae86Sav } 4510cc8ae86Sav 4520cc8ae86Sav static int 4530cc8ae86Sav mc_poll_init() 4540cc8ae86Sav { 4550cc8ae86Sav mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL); 4560cc8ae86Sav cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL); 4570cc8ae86Sav cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL); 4580cc8ae86Sav return (0); 4590cc8ae86Sav } 4600cc8ae86Sav 4610cc8ae86Sav static void 4620cc8ae86Sav mc_poll_fini() 4630cc8ae86Sav { 4640cc8ae86Sav mutex_enter(&mc_polling_lock); 4650cc8ae86Sav if (mc_pollthr_running) { 4660cc8ae86Sav mc_poll_cmd = MC_POLL_EXIT; 4670cc8ae86Sav cv_signal(&mc_polling_cv); 4680cc8ae86Sav while (mc_pollthr_running) { 4690cc8ae86Sav cv_wait(&mc_poll_exit_cv, &mc_polling_lock); 4700cc8ae86Sav } 4710cc8ae86Sav } 4720cc8ae86Sav mutex_exit(&mc_polling_lock); 4730cc8ae86Sav mutex_destroy(&mc_polling_lock); 4740cc8ae86Sav cv_destroy(&mc_polling_cv); 4750cc8ae86Sav cv_destroy(&mc_poll_exit_cv); 4760cc8ae86Sav } 4770cc8ae86Sav 47825cf1a30Sjl static int 47925cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 48025cf1a30Sjl { 48125cf1a30Sjl mc_opl_t *mcp; 48225cf1a30Sjl int instance; 4830cc8ae86Sav int rv; 48425cf1a30Sjl 48525cf1a30Sjl /* get the instance of this devi */ 48625cf1a30Sjl instance = ddi_get_instance(devi); 48725cf1a30Sjl 48825cf1a30Sjl switch (cmd) { 48925cf1a30Sjl case DDI_ATTACH: 49025cf1a30Sjl break; 49125cf1a30Sjl case DDI_RESUME: 49225cf1a30Sjl mcp = ddi_get_soft_state(mc_statep, instance); 4930cc8ae86Sav rv = mc_resume(mcp, MC_DRIVER_SUSPENDED); 4940cc8ae86Sav return (rv); 49525cf1a30Sjl default: 49625cf1a30Sjl return (DDI_FAILURE); 49725cf1a30Sjl } 49825cf1a30Sjl 49925cf1a30Sjl if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS) 50025cf1a30Sjl return (DDI_FAILURE); 50125cf1a30Sjl 50225cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 50325cf1a30Sjl goto bad; 50425cf1a30Sjl } 50525cf1a30Sjl 5060cc8ae86Sav if (mc_timeout_period == 0) { 5070cc8ae86Sav mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi, 5080cc8ae86Sav DDI_PROP_DONTPASS, "mc-timeout-interval-sec", 5090cc8ae86Sav mc_patrol_interval_sec); 5100cc8ae86Sav mc_timeout_period = drv_usectohz( 5110cc8ae86Sav 1000000 * mc_patrol_interval_sec / OPL_MAX_BOARDS); 5120cc8ae86Sav } 5130cc8ae86Sav 51425cf1a30Sjl /* set informations in mc state */ 51525cf1a30Sjl mcp->mc_dip = devi; 51625cf1a30Sjl 51725cf1a30Sjl if (mc_board_add(mcp)) 51825cf1a30Sjl goto bad; 51925cf1a30Sjl 52025cf1a30Sjl insert_mcp(mcp); 5210cc8ae86Sav 5220cc8ae86Sav /* 5230cc8ae86Sav * Start the polling thread if it is not running already. 5240cc8ae86Sav */ 5250cc8ae86Sav mutex_enter(&mc_polling_lock); 5260cc8ae86Sav if (!mc_pollthr_running) { 5270cc8ae86Sav (void) thread_create(NULL, 0, (void (*)())mc_polling_thread, 5280cc8ae86Sav NULL, 0, &p0, TS_RUN, mc_poll_priority); 5290cc8ae86Sav } 5300cc8ae86Sav mutex_exit(&mc_polling_lock); 53125cf1a30Sjl ddi_report_dev(devi); 53225cf1a30Sjl 53325cf1a30Sjl return (DDI_SUCCESS); 53425cf1a30Sjl 53525cf1a30Sjl bad: 53625cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 53725cf1a30Sjl return (DDI_FAILURE); 53825cf1a30Sjl } 53925cf1a30Sjl 54025cf1a30Sjl /* ARGSUSED */ 54125cf1a30Sjl static int 54225cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 54325cf1a30Sjl { 5440cc8ae86Sav int rv; 54525cf1a30Sjl int instance; 54625cf1a30Sjl mc_opl_t *mcp; 54725cf1a30Sjl 54825cf1a30Sjl /* get the instance of this devi */ 54925cf1a30Sjl instance = ddi_get_instance(devi); 55025cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 55125cf1a30Sjl return (DDI_FAILURE); 55225cf1a30Sjl } 55325cf1a30Sjl 55425cf1a30Sjl switch (cmd) { 55525cf1a30Sjl case DDI_SUSPEND: 5560cc8ae86Sav rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED); 5570cc8ae86Sav return (rv); 55825cf1a30Sjl case DDI_DETACH: 55925cf1a30Sjl break; 56025cf1a30Sjl default: 56125cf1a30Sjl return (DDI_FAILURE); 56225cf1a30Sjl } 56325cf1a30Sjl 5640cc8ae86Sav delete_mcp(mcp); 56525cf1a30Sjl if (mc_board_del(mcp) != DDI_SUCCESS) { 56625cf1a30Sjl return (DDI_FAILURE); 56725cf1a30Sjl } 56825cf1a30Sjl 56925cf1a30Sjl /* free up the soft state */ 57025cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 57125cf1a30Sjl 57225cf1a30Sjl return (DDI_SUCCESS); 57325cf1a30Sjl } 57425cf1a30Sjl 57525cf1a30Sjl /* ARGSUSED */ 57625cf1a30Sjl static int 57725cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 57825cf1a30Sjl { 57925cf1a30Sjl return (0); 58025cf1a30Sjl } 58125cf1a30Sjl 58225cf1a30Sjl /* ARGSUSED */ 58325cf1a30Sjl static int 58425cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp) 58525cf1a30Sjl { 58625cf1a30Sjl return (0); 58725cf1a30Sjl } 58825cf1a30Sjl 58925cf1a30Sjl /* ARGSUSED */ 59025cf1a30Sjl static int 59125cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 59225cf1a30Sjl int *rvalp) 59325cf1a30Sjl { 5940cc8ae86Sav #ifdef DEBUG 5950cc8ae86Sav return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp)); 5960cc8ae86Sav #else 59725cf1a30Sjl return (ENXIO); 5980cc8ae86Sav #endif 59925cf1a30Sjl } 60025cf1a30Sjl 60125cf1a30Sjl /* 60225cf1a30Sjl * PA validity check: 603738dd194Shyw * This function return 1 if the PA is a valid PA 604738dd194Shyw * in the running Solaris instance i.e. in physinstall 605738dd194Shyw * Otherwise, return 0. 60625cf1a30Sjl */ 60725cf1a30Sjl 60825cf1a30Sjl /* ARGSUSED */ 60925cf1a30Sjl static int 61025cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr) 61125cf1a30Sjl { 61225cf1a30Sjl if (mcp->mlist == NULL) 61325cf1a30Sjl mc_get_mlist(mcp); 61425cf1a30Sjl 61525cf1a30Sjl if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) { 61625cf1a30Sjl return (1); 61725cf1a30Sjl } 61825cf1a30Sjl return (0); 61925cf1a30Sjl } 62025cf1a30Sjl 62125cf1a30Sjl /* 62225cf1a30Sjl * mac-pa translation routines. 62325cf1a30Sjl * 62425cf1a30Sjl * Input: mc driver state, (LSB#, Bank#, DIMM address) 62525cf1a30Sjl * Output: physical address 62625cf1a30Sjl * 62725cf1a30Sjl * Valid - return value: 0 62825cf1a30Sjl * Invalid - return value: -1 62925cf1a30Sjl */ 63025cf1a30Sjl static int 63125cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa) 63225cf1a30Sjl { 63325cf1a30Sjl int i; 63425cf1a30Sjl uint64_t pa_offset = 0; 63525cf1a30Sjl int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1; 63625cf1a30Sjl int bank = maddr->ma_bank; 63725cf1a30Sjl mc_addr_t maddr1; 63825cf1a30Sjl int bank0, bank1; 63925cf1a30Sjl 64025cf1a30Sjl MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank, 64125cf1a30Sjl maddr->ma_dimm_addr); 64225cf1a30Sjl 64325cf1a30Sjl /* loc validity check */ 64425cf1a30Sjl ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd); 64525cf1a30Sjl ASSERT(bank >= 0 && OPL_BANK_MAX > bank); 64625cf1a30Sjl 64725cf1a30Sjl /* Do translation */ 64825cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 64925cf1a30Sjl int pa_bit = 0; 65025cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 65125cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 65225cf1a30Sjl pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1; 65325cf1a30Sjl } else if (mc_bit == MP_NONE) { 65425cf1a30Sjl pa_bit = 0; 65525cf1a30Sjl } else if (mc_bit == MP_BANK_0) { 65625cf1a30Sjl pa_bit = bank & 1; 65725cf1a30Sjl } else if (mc_bit == MP_BANK_1) { 65825cf1a30Sjl pa_bit = (bank >> 1) & 1; 65925cf1a30Sjl } else if (mc_bit == MP_BANK_2) { 66025cf1a30Sjl pa_bit = (bank >> 2) & 1; 66125cf1a30Sjl } 66225cf1a30Sjl pa_offset |= ((uint64_t)pa_bit) << i; 66325cf1a30Sjl } 66425cf1a30Sjl *pa = mcp->mc_start_address + pa_offset; 66525cf1a30Sjl MC_LOG("pa = %lx\n", *pa); 66625cf1a30Sjl 66725cf1a30Sjl if (pa_to_maddr(mcp, *pa, &maddr1) == -1) { 6680cc8ae86Sav cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to " 6690cc8ae86Sav "convert PA %lx\n", maddr->ma_bd, bank, 6700cc8ae86Sav maddr->ma_dimm_addr, *pa); 67125cf1a30Sjl return (-1); 67225cf1a30Sjl } 67325cf1a30Sjl 6740cc8ae86Sav /* 6750cc8ae86Sav * In mirror mode, PA is always translated to the even bank. 6760cc8ae86Sav */ 67725cf1a30Sjl if (IS_MIRROR(mcp, maddr->ma_bank)) { 67825cf1a30Sjl bank0 = maddr->ma_bank & ~(1); 67925cf1a30Sjl bank1 = maddr1.ma_bank & ~(1); 68025cf1a30Sjl } else { 68125cf1a30Sjl bank0 = maddr->ma_bank; 68225cf1a30Sjl bank1 = maddr1.ma_bank; 68325cf1a30Sjl } 68425cf1a30Sjl /* 68525cf1a30Sjl * there is no need to check ma_bd because it is generated from 68625cf1a30Sjl * mcp. They are the same. 68725cf1a30Sjl */ 68825cf1a30Sjl if ((bank0 == bank1) && 68925cf1a30Sjl (maddr->ma_dimm_addr == maddr1.ma_dimm_addr)) { 69025cf1a30Sjl return (0); 69125cf1a30Sjl } else { 69225cf1a30Sjl cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, " 69325cf1a30Sjl "PA %lx, target /LSB%d/B%d/%x\n", 69425cf1a30Sjl maddr->ma_bd, bank, maddr->ma_dimm_addr, 69525cf1a30Sjl *pa, maddr1.ma_bd, maddr1.ma_bank, 69625cf1a30Sjl maddr1.ma_dimm_addr); 69725cf1a30Sjl return (-1); 69825cf1a30Sjl } 69925cf1a30Sjl } 70025cf1a30Sjl 70125cf1a30Sjl /* 70225cf1a30Sjl * PA to CS (used by pa_to_maddr). 70325cf1a30Sjl */ 70425cf1a30Sjl static int 70525cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset) 70625cf1a30Sjl { 70725cf1a30Sjl int i; 708738dd194Shyw int cs = 1; 70925cf1a30Sjl 71025cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 71125cf1a30Sjl /* MAC address bit<29> is arranged on the same PA bit */ 71225cf1a30Sjl /* on both table. So we may use any table. */ 71325cf1a30Sjl if (mcp->mc_trans_table[0][i] == CS_SHIFT) { 71425cf1a30Sjl cs = (pa_offset >> i) & 1; 71525cf1a30Sjl break; 71625cf1a30Sjl } 71725cf1a30Sjl } 71825cf1a30Sjl return (cs); 71925cf1a30Sjl } 72025cf1a30Sjl 72125cf1a30Sjl /* 72225cf1a30Sjl * PA to DIMM (used by pa_to_maddr). 72325cf1a30Sjl */ 72425cf1a30Sjl /* ARGSUSED */ 72525cf1a30Sjl static uint32_t 72625cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset) 72725cf1a30Sjl { 72825cf1a30Sjl int i; 72925cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 73025cf1a30Sjl uint32_t dimm_addr = 0; 73125cf1a30Sjl 73225cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 73325cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 73425cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 73525cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 73625cf1a30Sjl dimm_addr |= pa_bit_value << mc_bit; 73725cf1a30Sjl } 73825cf1a30Sjl } 739738dd194Shyw dimm_addr |= cs << CS_SHIFT; 74025cf1a30Sjl return (dimm_addr); 74125cf1a30Sjl } 74225cf1a30Sjl 74325cf1a30Sjl /* 74425cf1a30Sjl * PA to Bank (used by pa_to_maddr). 74525cf1a30Sjl */ 74625cf1a30Sjl static int 74725cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset) 74825cf1a30Sjl { 74925cf1a30Sjl int i; 75025cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 75125cf1a30Sjl int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT]; 75225cf1a30Sjl 75325cf1a30Sjl 75425cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 75525cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 75625cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 75725cf1a30Sjl switch (mc_bit) { 75825cf1a30Sjl case MP_BANK_0: 75925cf1a30Sjl bankno |= pa_bit_value; 76025cf1a30Sjl break; 76125cf1a30Sjl case MP_BANK_1: 76225cf1a30Sjl bankno |= pa_bit_value << 1; 76325cf1a30Sjl break; 76425cf1a30Sjl case MP_BANK_2: 76525cf1a30Sjl bankno |= pa_bit_value << 2; 76625cf1a30Sjl break; 76725cf1a30Sjl } 76825cf1a30Sjl } 76925cf1a30Sjl 77025cf1a30Sjl return (bankno); 77125cf1a30Sjl } 77225cf1a30Sjl 77325cf1a30Sjl /* 77425cf1a30Sjl * PA to MAC address translation 77525cf1a30Sjl * 77625cf1a30Sjl * Input: MAC driver state, physicall adress 77725cf1a30Sjl * Output: LSB#, Bank id, mac address 77825cf1a30Sjl * 77925cf1a30Sjl * Valid - return value: 0 78025cf1a30Sjl * Invalid - return value: -1 78125cf1a30Sjl */ 78225cf1a30Sjl 78325cf1a30Sjl int 78425cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr) 78525cf1a30Sjl { 78625cf1a30Sjl uint64_t pa_offset; 78725cf1a30Sjl 788738dd194Shyw if (!mc_rangecheck_pa(mcp, pa)) 78925cf1a30Sjl return (-1); 79025cf1a30Sjl 79125cf1a30Sjl /* Do translation */ 79225cf1a30Sjl pa_offset = pa - mcp->mc_start_address; 79325cf1a30Sjl 79425cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 795*aeb241b2Sav maddr->ma_phys_bd = mcp->mc_phys_board_num; 79625cf1a30Sjl maddr->ma_bank = pa_to_bank(mcp, pa_offset); 79725cf1a30Sjl maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset); 79825cf1a30Sjl MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", 79925cf1a30Sjl pa_offset, maddr->ma_bd, maddr->ma_bank, maddr->ma_dimm_addr); 80025cf1a30Sjl return (0); 80125cf1a30Sjl } 80225cf1a30Sjl 8030cc8ae86Sav /* 8040cc8ae86Sav * UNUM format for DC is "/CMUnn/MEMxyZ", where 8050cc8ae86Sav * nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3. 8060cc8ae86Sav * x = MAC 0..3 8070cc8ae86Sav * y = 0..3 (slot info). 8080cc8ae86Sav * Z = 'A' or 'B' 8090cc8ae86Sav * 8100cc8ae86Sav * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where 8110cc8ae86Sav * x = 0..3 (MEMB number) 8120cc8ae86Sav * y = 0..3 (slot info). 8130cc8ae86Sav * Z = 'A' or 'B' 8140cc8ae86Sav * 8150cc8ae86Sav * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ" 8160cc8ae86Sav * x = 0..7 (MEMB number) 8170cc8ae86Sav * y = 0..3 (slot info). 8180cc8ae86Sav * Z = 'A' or 'B' 8190cc8ae86Sav */ 8200cc8ae86Sav int 821*aeb241b2Sav mc_set_mem_unum(char *buf, int buflen, int sb, int bank, 8220cc8ae86Sav uint32_t mf_type, uint32_t d_slot) 8230cc8ae86Sav { 8240cc8ae86Sav char *dimmnm; 8250cc8ae86Sav char memb_num; 826*aeb241b2Sav int cs; 8270cc8ae86Sav int i; 828*aeb241b2Sav int j; 8290cc8ae86Sav 830*aeb241b2Sav cs = SLOT_TO_CS(d_slot); 8310cc8ae86Sav 8320cc8ae86Sav if (plat_model == MODEL_DC) { 8330cc8ae86Sav if (mf_type == FLT_TYPE_PERMANENT_CE) { 8340cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 8350cc8ae86Sav dimmnm = mc_dc_dimm_unum_table[i]; 8360cc8ae86Sav snprintf(buf, buflen, "/%s%02d/MEM%s", 8370cc8ae86Sav model_names[plat_model].unit_name, sb, dimmnm); 8380cc8ae86Sav } else { 8390cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 840*aeb241b2Sav j = (cs == 0) ? i : i + 2; 841*aeb241b2Sav snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s", 8420cc8ae86Sav model_names[plat_model].unit_name, sb, 843*aeb241b2Sav mc_dc_dimm_unum_table[j], 844*aeb241b2Sav mc_dc_dimm_unum_table[j + 1]); 8450cc8ae86Sav } 8460cc8ae86Sav } else { 8470cc8ae86Sav if (mf_type == FLT_TYPE_PERMANENT_CE) { 848*aeb241b2Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 8490cc8ae86Sav dimmnm = mc_ff_dimm_unum_table[i]; 8500cc8ae86Sav memb_num = dimmnm[0]; 8510cc8ae86Sav snprintf(buf, buflen, "/%s/%s%c/MEM%s", 8520cc8ae86Sav model_names[plat_model].unit_name, 8530cc8ae86Sav model_names[plat_model].mem_name, 8540cc8ae86Sav memb_num, &dimmnm[1]); 8550cc8ae86Sav } else { 8560cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 857*aeb241b2Sav j = (cs == 0) ? i : i + 2; 8580cc8ae86Sav memb_num = mc_ff_dimm_unum_table[i][0], 8590cc8ae86Sav snprintf(buf, buflen, 860*aeb241b2Sav "/%s/%s%c/MEM%s MEM%s", 8610cc8ae86Sav model_names[plat_model].unit_name, 8620cc8ae86Sav model_names[plat_model].mem_name, memb_num, 863*aeb241b2Sav &mc_ff_dimm_unum_table[j][1], 864*aeb241b2Sav &mc_ff_dimm_unum_table[j + 1][1]); 8650cc8ae86Sav } 8660cc8ae86Sav } 8670cc8ae86Sav return (0); 8680cc8ae86Sav } 8690cc8ae86Sav 87025cf1a30Sjl static void 87125cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt) 87225cf1a30Sjl { 87325cf1a30Sjl char buf[FM_MAX_CLASS]; 87425cf1a30Sjl char device_path[MAXPATHLEN]; 8750cc8ae86Sav char sid[MAXPATHLEN]; 87625cf1a30Sjl nv_alloc_t *nva = NULL; 87725cf1a30Sjl nvlist_t *ereport, *detector, *resource; 87825cf1a30Sjl errorq_elem_t *eqep; 87925cf1a30Sjl int nflts; 88025cf1a30Sjl mc_flt_stat_t *flt_stat; 8810cc8ae86Sav int i, n; 8820cc8ae86Sav int blen = MAXPATHLEN; 8830cc8ae86Sav char *p, *s = NULL; 88425cf1a30Sjl uint32_t values[2], synd[2], dslot[2]; 8850cc8ae86Sav uint64_t offset = (uint64_t)-1; 8860cc8ae86Sav int ret = -1; 88725cf1a30Sjl 88825cf1a30Sjl if (panicstr) { 88925cf1a30Sjl eqep = errorq_reserve(ereport_errorq); 89025cf1a30Sjl if (eqep == NULL) 89125cf1a30Sjl return; 89225cf1a30Sjl ereport = errorq_elem_nvl(ereport_errorq, eqep); 89325cf1a30Sjl nva = errorq_elem_nva(ereport_errorq, eqep); 89425cf1a30Sjl } else { 89525cf1a30Sjl ereport = fm_nvlist_create(nva); 89625cf1a30Sjl } 89725cf1a30Sjl 89825cf1a30Sjl /* 89925cf1a30Sjl * Create the scheme "dev" FMRI. 90025cf1a30Sjl */ 90125cf1a30Sjl detector = fm_nvlist_create(nva); 90225cf1a30Sjl resource = fm_nvlist_create(nva); 90325cf1a30Sjl 90425cf1a30Sjl nflts = mc_aflt->mflt_nflts; 90525cf1a30Sjl 90625cf1a30Sjl ASSERT(nflts >= 1 && nflts <= 2); 90725cf1a30Sjl 90825cf1a30Sjl flt_stat = mc_aflt->mflt_stat[0]; 90925cf1a30Sjl (void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path); 91025cf1a30Sjl (void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 91125cf1a30Sjl device_path, NULL); 91225cf1a30Sjl 91325cf1a30Sjl /* 91425cf1a30Sjl * Encode all the common data into the ereport. 91525cf1a30Sjl */ 91625cf1a30Sjl (void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", 91725cf1a30Sjl MC_OPL_ERROR_CLASS, 91825cf1a30Sjl mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : 91925cf1a30Sjl MC_OPL_MI_SUBCLASS, 92025cf1a30Sjl mc_aflt->mflt_erpt_class); 92125cf1a30Sjl 92225cf1a30Sjl MC_LOG("mc_ereport_post: ereport %s\n", buf); 92325cf1a30Sjl 92425cf1a30Sjl 92525cf1a30Sjl fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, 92625cf1a30Sjl fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), 92725cf1a30Sjl detector, NULL); 92825cf1a30Sjl 92925cf1a30Sjl /* 93025cf1a30Sjl * Set payload. 93125cf1a30Sjl */ 93225cf1a30Sjl fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32, 93325cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd, NULL); 93425cf1a30Sjl 93525cf1a30Sjl fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64, 93625cf1a30Sjl flt_stat->mf_flt_paddr, NULL); 93725cf1a30Sjl 93825cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 93925cf1a30Sjl fm_payload_set(ereport, MC_OPL_FLT_TYPE, 94025cf1a30Sjl DATA_TYPE_UINT8, ECC_STICKY, NULL); 94125cf1a30Sjl } 94225cf1a30Sjl 94325cf1a30Sjl for (i = 0; i < nflts; i++) 94425cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank; 94525cf1a30Sjl 94625cf1a30Sjl fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, 94725cf1a30Sjl nflts, values, NULL); 94825cf1a30Sjl 94925cf1a30Sjl for (i = 0; i < nflts; i++) 95025cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_cntl; 95125cf1a30Sjl 95225cf1a30Sjl fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, 95325cf1a30Sjl nflts, values, NULL); 95425cf1a30Sjl 95525cf1a30Sjl for (i = 0; i < nflts; i++) 95625cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_add; 95725cf1a30Sjl 9580cc8ae86Sav /* offset is set only for PCE */ 9590cc8ae86Sav if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) { 9600cc8ae86Sav offset = values[0]; 9610cc8ae86Sav 9620cc8ae86Sav } 96325cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, 96425cf1a30Sjl nflts, values, NULL); 96525cf1a30Sjl 96625cf1a30Sjl for (i = 0; i < nflts; i++) 96725cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_log; 96825cf1a30Sjl 96925cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, 97025cf1a30Sjl nflts, values, NULL); 97125cf1a30Sjl 97225cf1a30Sjl for (i = 0; i < nflts; i++) { 97325cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 97425cf1a30Sjl if (flt_stat->mf_errlog_valid) { 97525cf1a30Sjl synd[i] = flt_stat->mf_synd; 97625cf1a30Sjl dslot[i] = flt_stat->mf_dimm_slot; 97725cf1a30Sjl values[i] = flt_stat->mf_dram_place; 97825cf1a30Sjl } else { 97925cf1a30Sjl synd[i] = 0; 98025cf1a30Sjl dslot[i] = 0; 98125cf1a30Sjl values[i] = 0; 98225cf1a30Sjl } 98325cf1a30Sjl } 98425cf1a30Sjl 98525cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_SYND, 98625cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, synd, NULL); 98725cf1a30Sjl 98825cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, 98925cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, dslot, NULL); 99025cf1a30Sjl 99125cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_DRAM, 99225cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, values, NULL); 99325cf1a30Sjl 99425cf1a30Sjl device_path[0] = 0; 99525cf1a30Sjl p = &device_path[0]; 9960cc8ae86Sav sid[0] = 0; 9970cc8ae86Sav s = &sid[0]; 9980cc8ae86Sav ret = 0; 99925cf1a30Sjl 100025cf1a30Sjl for (i = 0; i < nflts; i++) { 10010cc8ae86Sav int bank; 100225cf1a30Sjl 100325cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 10040cc8ae86Sav bank = flt_stat->mf_flt_maddr.ma_bank; 10050cc8ae86Sav ret = mc_set_mem_unum(p + strlen(p), blen, 1006*aeb241b2Sav flt_stat->mf_flt_maddr.ma_phys_bd, bank, 1007*aeb241b2Sav flt_stat->mf_type, flt_stat->mf_dimm_slot); 10080cc8ae86Sav 10090cc8ae86Sav if (ret != 0) { 10100cc8ae86Sav cmn_err(CE_WARN, 10110cc8ae86Sav "mc_ereport_post: Failed to determine the unum " 10120cc8ae86Sav "for board=%d bank=%d type=0x%x slot=0x%x", 10130cc8ae86Sav flt_stat->mf_flt_maddr.ma_bd, bank, 10140cc8ae86Sav flt_stat->mf_type, flt_stat->mf_dimm_slot); 10150cc8ae86Sav continue; 101625cf1a30Sjl } 10170cc8ae86Sav n = strlen(device_path); 101825cf1a30Sjl blen = MAXPATHLEN - n; 101925cf1a30Sjl p = &device_path[n]; 102025cf1a30Sjl if (i < (nflts - 1)) { 102125cf1a30Sjl snprintf(p, blen, " "); 10220cc8ae86Sav blen--; 10230cc8ae86Sav p++; 10240cc8ae86Sav } 10250cc8ae86Sav 10260cc8ae86Sav if (ret == 0) { 10270cc8ae86Sav ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s), 1028*aeb241b2Sav blen, flt_stat->mf_flt_maddr.ma_phys_bd, bank, 10290cc8ae86Sav flt_stat->mf_type, flt_stat->mf_dimm_slot); 10300cc8ae86Sav 103125cf1a30Sjl } 103225cf1a30Sjl } 103325cf1a30Sjl 103425cf1a30Sjl (void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, 10350cc8ae86Sav NULL, device_path, (ret == 0) ? sid : NULL, 10360cc8ae86Sav (ret == 0) ? offset : (uint64_t)-1); 103725cf1a30Sjl 103825cf1a30Sjl fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, 103925cf1a30Sjl resource, NULL); 104025cf1a30Sjl 104125cf1a30Sjl if (panicstr) { 104225cf1a30Sjl errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); 104325cf1a30Sjl } else { 104425cf1a30Sjl (void) fm_ereport_post(ereport, EVCH_TRYHARD); 104525cf1a30Sjl fm_nvlist_destroy(ereport, FM_NVA_FREE); 104625cf1a30Sjl fm_nvlist_destroy(detector, FM_NVA_FREE); 104725cf1a30Sjl fm_nvlist_destroy(resource, FM_NVA_FREE); 104825cf1a30Sjl } 104925cf1a30Sjl } 105025cf1a30Sjl 10510cc8ae86Sav 105225cf1a30Sjl static void 105325cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt) 105425cf1a30Sjl { 105525cf1a30Sjl int rv; 105625cf1a30Sjl uint64_t pa = (uint64_t)(-1); 10570cc8ae86Sav int i; 105825cf1a30Sjl 105925cf1a30Sjl MC_LOG("mc_err_drain: %s\n", 106025cf1a30Sjl mc_aflt->mflt_erpt_class); 106125cf1a30Sjl /* 106225cf1a30Sjl * we come here only when we have: 106325cf1a30Sjl * In mirror mode: CMPE, MUE, SUE 106425cf1a30Sjl * In normal mode: UE, Permanent CE 106525cf1a30Sjl */ 10660cc8ae86Sav for (i = 0; i < mc_aflt->mflt_nflts; i++) { 10670cc8ae86Sav rv = mcaddr_to_pa(mc_aflt->mflt_mcp, 10680cc8ae86Sav &(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa); 1069738dd194Shyw 1070738dd194Shyw /* Ensure the pa is valid (not in isolated memory block) */ 1071738dd194Shyw if (rv == 0 && pa_is_valid(mc_aflt->mflt_mcp, pa)) 10720cc8ae86Sav mc_aflt->mflt_stat[i]->mf_flt_paddr = pa; 10730cc8ae86Sav else 10740cc8ae86Sav mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1; 10750cc8ae86Sav } 10760cc8ae86Sav 1077738dd194Shyw MC_LOG("mc_err_drain:pa = %lx\n", pa); 107825cf1a30Sjl 1079738dd194Shyw switch (page_retire_check(pa, NULL)) { 1080738dd194Shyw case 0: 1081738dd194Shyw case EAGAIN: 1082738dd194Shyw MC_LOG("Page retired or pending\n"); 1083738dd194Shyw return; 1084738dd194Shyw case EIO: 1085738dd194Shyw /* 1086738dd194Shyw * Do page retirement except for the PCE case. 1087738dd194Shyw * This is taken care by the OPL DE 1088738dd194Shyw */ 1089738dd194Shyw if (mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) { 1090738dd194Shyw MC_LOG("offline page at pa %lx error %x\n", pa, 1091738dd194Shyw mc_aflt->mflt_pr); 1092738dd194Shyw (void) page_retire(pa, mc_aflt->mflt_pr); 109325cf1a30Sjl } 1094738dd194Shyw break; 1095738dd194Shyw case EINVAL: 1096738dd194Shyw default: 1097738dd194Shyw /* 1098738dd194Shyw * Some memory do not have page structure so 1099738dd194Shyw * we keep going in case of EINVAL. 1100738dd194Shyw */ 1101738dd194Shyw break; 110225cf1a30Sjl } 110325cf1a30Sjl 11040cc8ae86Sav for (i = 0; i < mc_aflt->mflt_nflts; i++) { 11050cc8ae86Sav mc_aflt_t mc_aflt0; 11060cc8ae86Sav if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) { 11070cc8ae86Sav mc_aflt0 = *mc_aflt; 11080cc8ae86Sav mc_aflt0.mflt_nflts = 1; 11090cc8ae86Sav mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i]; 11100cc8ae86Sav mc_ereport_post(&mc_aflt0); 11110cc8ae86Sav } 11120cc8ae86Sav } 11130cc8ae86Sav } 111425cf1a30Sjl 111525cf1a30Sjl /* 111625cf1a30Sjl * The restart address is actually defined in unit of PA[37:6] 111725cf1a30Sjl * the mac patrol will convert that to dimm offset. If the 111825cf1a30Sjl * address is not in the bank, it will continue to search for 111925cf1a30Sjl * the next PA that is within the bank. 112025cf1a30Sjl * 112125cf1a30Sjl * Also the mac patrol scans the dimms based on PA, not 112225cf1a30Sjl * dimm offset. 112325cf1a30Sjl */ 112425cf1a30Sjl static int 1125738dd194Shyw restart_patrol(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr_info) 112625cf1a30Sjl { 112725cf1a30Sjl uint64_t pa; 112825cf1a30Sjl int rv; 112925cf1a30Sjl 1130738dd194Shyw if (rsaddr_info == NULL || (rsaddr_info->mi_valid == 0)) { 113125cf1a30Sjl MAC_PTRL_START(mcp, bank); 113225cf1a30Sjl return (0); 113325cf1a30Sjl } 113425cf1a30Sjl 1135738dd194Shyw rv = mcaddr_to_pa(mcp, &rsaddr_info->mi_restartaddr, &pa); 113625cf1a30Sjl if (rv != 0) { 113725cf1a30Sjl MC_LOG("cannot convert mcaddr to pa. use auto restart\n"); 113825cf1a30Sjl MAC_PTRL_START(mcp, bank); 113925cf1a30Sjl return (0); 114025cf1a30Sjl } 114125cf1a30Sjl 1142738dd194Shyw if (!mc_rangecheck_pa(mcp, pa)) { 114325cf1a30Sjl /* pa is not on this board, just retry */ 114425cf1a30Sjl cmn_err(CE_WARN, "restart_patrol: invalid address %lx " 114525cf1a30Sjl "on board %d\n", pa, mcp->mc_board_num); 114625cf1a30Sjl MAC_PTRL_START(mcp, bank); 114725cf1a30Sjl return (0); 114825cf1a30Sjl } 114925cf1a30Sjl 115025cf1a30Sjl MC_LOG("restart_patrol: pa = %lx\n", pa); 115125cf1a30Sjl 1152738dd194Shyw if (!rsaddr_info->mi_injectrestart) { 1153738dd194Shyw /* 1154738dd194Shyw * For non-errorinjection restart we need to 1155738dd194Shyw * determine if the current restart pa/page is 1156738dd194Shyw * a "good" page. A "good" page is a page that 1157738dd194Shyw * has not been page retired. If the current 1158738dd194Shyw * page that contains the pa is "good", we will 1159738dd194Shyw * do a HW auto restart and let HW patrol continue 1160738dd194Shyw * where it last stopped. Most desired scenario. 1161738dd194Shyw * 1162738dd194Shyw * If the current page is not "good", we will advance 1163738dd194Shyw * to the next page to find the next "good" page and 1164738dd194Shyw * restart the patrol from there. 1165738dd194Shyw */ 1166738dd194Shyw int wrapcount = 0; 1167738dd194Shyw uint64_t origpa = pa; 1168738dd194Shyw while (wrapcount < 2) { 1169738dd194Shyw if (!pa_is_valid(mcp, pa)) { 1170738dd194Shyw /* 1171738dd194Shyw * Not in physinstall - advance to the 1172738dd194Shyw * next memory isolation blocksize 1173738dd194Shyw */ 117425cf1a30Sjl MC_LOG("Invalid PA\n"); 1175738dd194Shyw pa = roundup(pa + 1, mc_isolation_bsize); 1176738dd194Shyw } else { 1177738dd194Shyw int rv; 1178738dd194Shyw if ((rv = page_retire_check(pa, NULL)) != 0 && 1179738dd194Shyw rv != EAGAIN) { 1180738dd194Shyw /* 1181738dd194Shyw * The page is "good" (not retired), we will 1182738dd194Shyw * use automatic HW restart algorithm if 1183738dd194Shyw * this is the original current starting page 1184738dd194Shyw */ 1185738dd194Shyw if (pa == origpa) { 1186738dd194Shyw MC_LOG("Page has no error. Auto restart\n"); 1187738dd194Shyw MAC_PTRL_START(mcp, bank); 1188738dd194Shyw return (0); 1189738dd194Shyw } else { 1190738dd194Shyw /* found a subsequent good page */ 1191738dd194Shyw break; 1192738dd194Shyw } 119325cf1a30Sjl } 1194738dd194Shyw 1195cfb9e062Shyw /* 1196738dd194Shyw * Skip to the next page 1197cfb9e062Shyw */ 1198cfb9e062Shyw pa = roundup(pa + 1, PAGESIZE); 1199cfb9e062Shyw MC_LOG("Skipping bad page to %lx\n", pa); 1200738dd194Shyw } 1201738dd194Shyw 1202738dd194Shyw /* Check to see if we hit the end of the memory range */ 1203738dd194Shyw if (pa >= (mcp->mc_start_address + mcp->mc_size)) { 120425cf1a30Sjl MC_LOG("Wrap around\n"); 120525cf1a30Sjl pa = mcp->mc_start_address; 1206738dd194Shyw wrapcount++; 1207738dd194Shyw } 1208738dd194Shyw } 1209738dd194Shyw 1210738dd194Shyw if (wrapcount > 1) { 1211738dd194Shyw MC_LOG("Failed to find a good page. Just restart\n"); 1212738dd194Shyw MAC_PTRL_START(mcp, bank); 1213738dd194Shyw return (0); 121425cf1a30Sjl } 121525cf1a30Sjl } 121625cf1a30Sjl 1217738dd194Shyw /* 1218738dd194Shyw * We reached here either: 1219738dd194Shyw * 1. We are doing an error injection restart that specify 1220738dd194Shyw * the exact pa/page to restart. OR 1221738dd194Shyw * 2. We found a subsequent good page different from the 1222738dd194Shyw * original restart pa/page. 1223738dd194Shyw * Restart MAC patrol: PA[37:6] 1224738dd194Shyw */ 122525cf1a30Sjl MC_LOG("restart at pa = %lx\n", pa); 122625cf1a30Sjl ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa)); 122725cf1a30Sjl MAC_PTRL_START_ADD(mcp, bank); 122825cf1a30Sjl 122925cf1a30Sjl return (0); 123025cf1a30Sjl } 123125cf1a30Sjl 123225cf1a30Sjl /* 123325cf1a30Sjl * Rewriting is used for two purposes. 123425cf1a30Sjl * - to correct the error in memory. 123525cf1a30Sjl * - to determine whether the error is permanent or intermittent. 123625cf1a30Sjl * It's done by writing the address in MAC_BANKm_REWRITE_ADD 123725cf1a30Sjl * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that, 123825cf1a30Sjl * REW_END (and REW_CE/REW_UE if some error detected) is set when 123925cf1a30Sjl * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM. 124025cf1a30Sjl * 124125cf1a30Sjl * Note that rewrite operation doesn't change RAW_UE to Marked UE. 124225cf1a30Sjl * Therefore, we use it only CE case. 124325cf1a30Sjl */ 124425cf1a30Sjl static uint32_t 124525cf1a30Sjl do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr) 124625cf1a30Sjl { 124725cf1a30Sjl uint32_t cntl; 124825cf1a30Sjl int count = 0; 124925cf1a30Sjl 125025cf1a30Sjl /* first wait to make sure PTRL_STATUS is 0 */ 12510cc8ae86Sav while (count++ < mc_max_rewrite_loop) { 125225cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 125325cf1a30Sjl if (!(cntl & MAC_CNTL_PTRL_STATUS)) 125425cf1a30Sjl break; 12550cc8ae86Sav drv_usecwait(mc_rewrite_delay); 125625cf1a30Sjl } 12570cc8ae86Sav if (count >= mc_max_rewrite_loop) 125825cf1a30Sjl goto bad; 125925cf1a30Sjl 126025cf1a30Sjl count = 0; 126125cf1a30Sjl 126225cf1a30Sjl ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr); 126325cf1a30Sjl MAC_REW_REQ(mcp, bank); 126425cf1a30Sjl 126525cf1a30Sjl do { 126625cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 12670cc8ae86Sav if (count++ >= mc_max_rewrite_loop) { 126825cf1a30Sjl goto bad; 12690cc8ae86Sav } else { 12700cc8ae86Sav drv_usecwait(mc_rewrite_delay); 12710cc8ae86Sav } 127225cf1a30Sjl /* 127325cf1a30Sjl * If there are other MEMORY or PCI activities, this 127425cf1a30Sjl * will be BUSY, else it should be set immediately 127525cf1a30Sjl */ 127625cf1a30Sjl } while (!(cntl & MAC_CNTL_REW_END)); 127725cf1a30Sjl 127825cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 127925cf1a30Sjl return (cntl); 128025cf1a30Sjl bad: 128125cf1a30Sjl /* This is bad. Just reset the circuit */ 128225cf1a30Sjl cmn_err(CE_WARN, "mc-opl rewrite timeout on /LSB%d/B%d\n", 128325cf1a30Sjl mcp->mc_board_num, bank); 128425cf1a30Sjl cntl = MAC_CNTL_REW_END; 128525cf1a30Sjl MAC_CMD(mcp, bank, MAC_CNTL_PTRL_RESET); 128625cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 128725cf1a30Sjl return (cntl); 128825cf1a30Sjl } 128925cf1a30Sjl void 129025cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp) 129125cf1a30Sjl { 12920cc8ae86Sav int count; 12930cc8ae86Sav int n = 0; 129425cf1a30Sjl scf_log_t *p; 129525cf1a30Sjl int bank; 129625cf1a30Sjl 12970cc8ae86Sav for (bank = 0; bank < BANKNUM_PER_SB; bank++) { 12980cc8ae86Sav while ((p = mcp->mc_scf_log[bank]) != NULL && 12990cc8ae86Sav (n < mc_max_errlog_processed)) { 13000cc8ae86Sav ASSERT(bank == p->sl_bank); 13010cc8ae86Sav count = 0; 130225cf1a30Sjl while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank)) 130325cf1a30Sjl & MAC_STATIC_ERR_VLD)) { 13040cc8ae86Sav if (count++ >= (mc_max_scf_loop)) { 130525cf1a30Sjl break; 130625cf1a30Sjl } 13070cc8ae86Sav drv_usecwait(mc_scf_delay); 130825cf1a30Sjl } 130925cf1a30Sjl 13100cc8ae86Sav if (count < mc_max_scf_loop) { 131125cf1a30Sjl ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank), 131225cf1a30Sjl p->sl_err_log); 131325cf1a30Sjl 131425cf1a30Sjl ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank), 131525cf1a30Sjl p->sl_err_add|MAC_STATIC_ERR_VLD); 131625cf1a30Sjl mcp->mc_scf_retry[bank] = 0; 131725cf1a30Sjl } else { 131825cf1a30Sjl /* if we try too many times, just drop the req */ 13190cc8ae86Sav if (mcp->mc_scf_retry[bank]++ <= mc_max_scf_retry) { 132025cf1a30Sjl return; 132125cf1a30Sjl } else { 13220cc8ae86Sav if ((++mc_pce_dropped & 0xff) == 0) { 13230cc8ae86Sav cmn_err(CE_WARN, 13240cc8ae86Sav "Cannot report Permanent CE to SCF\n"); 13250cc8ae86Sav } 132625cf1a30Sjl } 132725cf1a30Sjl } 13280cc8ae86Sav n++; 13290cc8ae86Sav mcp->mc_scf_log[bank] = p->sl_next; 13300cc8ae86Sav mcp->mc_scf_total[bank]--; 13310cc8ae86Sav ASSERT(mcp->mc_scf_total[bank] >= 0); 133225cf1a30Sjl kmem_free(p, sizeof (scf_log_t)); 13330cc8ae86Sav } 133425cf1a30Sjl } 133525cf1a30Sjl } 133625cf1a30Sjl void 133725cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank) 133825cf1a30Sjl { 133925cf1a30Sjl scf_log_t *p; 134025cf1a30Sjl 13410cc8ae86Sav if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) { 13420cc8ae86Sav if ((++mc_pce_dropped & 0xff) == 0) { 13430cc8ae86Sav cmn_err(CE_WARN, "Too many Permanent CE requests.\n"); 13440cc8ae86Sav } 134525cf1a30Sjl return; 134625cf1a30Sjl } 134725cf1a30Sjl p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP); 134825cf1a30Sjl p->sl_next = 0; 134925cf1a30Sjl p->sl_err_add = flt_stat->mf_err_add; 135025cf1a30Sjl p->sl_err_log = flt_stat->mf_err_log; 135125cf1a30Sjl p->sl_bank = bank; 135225cf1a30Sjl 13530cc8ae86Sav if (mcp->mc_scf_log[bank] == NULL) { 135425cf1a30Sjl /* 135525cf1a30Sjl * we rely on mc_scf_log to detect NULL queue. 135625cf1a30Sjl * mc_scf_log_tail is irrelevant is such case. 135725cf1a30Sjl */ 13580cc8ae86Sav mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p; 135925cf1a30Sjl } else { 13600cc8ae86Sav mcp->mc_scf_log_tail[bank]->sl_next = p; 13610cc8ae86Sav mcp->mc_scf_log_tail[bank] = p; 136225cf1a30Sjl } 13630cc8ae86Sav mcp->mc_scf_total[bank]++; 136425cf1a30Sjl } 136525cf1a30Sjl /* 136625cf1a30Sjl * This routine determines what kind of CE happens, intermittent 136725cf1a30Sjl * or permanent as follows. (See 4.7.3 in Columbus2 PRM.) 136825cf1a30Sjl * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register. 136925cf1a30Sjl * - If CE is still detected on the same address even after doing 137025cf1a30Sjl * rewrite operation twice, it is determined as permanent error. 137125cf1a30Sjl * - If error is not detected anymore, it is determined as intermittent 137225cf1a30Sjl * error. 137325cf1a30Sjl * - If UE is detected due to rewrite operation, it should be treated 137425cf1a30Sjl * as UE. 137525cf1a30Sjl */ 137625cf1a30Sjl 137725cf1a30Sjl /* ARGSUSED */ 137825cf1a30Sjl static void 137925cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error) 138025cf1a30Sjl { 138125cf1a30Sjl uint32_t cntl; 138225cf1a30Sjl int i; 138325cf1a30Sjl 138425cf1a30Sjl flt_stat->mf_type = FLT_TYPE_PERMANENT_CE; 138525cf1a30Sjl /* 138625cf1a30Sjl * rewrite request 1st time reads and correct error data 138725cf1a30Sjl * and write to DIMM. 2nd rewrite request must be issued 138825cf1a30Sjl * after REW_CE/UE/END is 0. When the 2nd request is completed, 138925cf1a30Sjl * if REW_CE = 1, then it is permanent CE. 139025cf1a30Sjl */ 139125cf1a30Sjl for (i = 0; i < 2; i++) { 139225cf1a30Sjl cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add); 139325cf1a30Sjl /* 139425cf1a30Sjl * If the error becomes UE or CMPE 139525cf1a30Sjl * we return to the caller immediately. 139625cf1a30Sjl */ 139725cf1a30Sjl if (cntl & MAC_CNTL_REW_UE) { 139825cf1a30Sjl if (ptrl_error) 139925cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE; 140025cf1a30Sjl else 140125cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_UE; 140225cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 140325cf1a30Sjl return; 140425cf1a30Sjl } 140525cf1a30Sjl if (cntl & MAC_CNTL_REW_CMPE) { 140625cf1a30Sjl if (ptrl_error) 140725cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE; 140825cf1a30Sjl else 140925cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE; 141025cf1a30Sjl flt_stat->mf_type = FLT_TYPE_CMPE; 141125cf1a30Sjl return; 141225cf1a30Sjl } 141325cf1a30Sjl } 141425cf1a30Sjl if (!(cntl & MAC_CNTL_REW_CE)) { 141525cf1a30Sjl flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE; 141625cf1a30Sjl } 141725cf1a30Sjl 141825cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 141925cf1a30Sjl /* report PERMANENT_CE to SP via SCF */ 142025cf1a30Sjl if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) { 142125cf1a30Sjl mc_queue_scf_log(mcp, flt_stat, bank); 142225cf1a30Sjl } 142325cf1a30Sjl } 142425cf1a30Sjl } 142525cf1a30Sjl 142625cf1a30Sjl #define IS_CMPE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\ 142725cf1a30Sjl MAC_CNTL_MI_CMPE)) 142825cf1a30Sjl #define IS_UE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE)) 142925cf1a30Sjl #define IS_CE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE)) 143025cf1a30Sjl #define IS_OK(cntl, f) (!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \ 143125cf1a30Sjl MAC_CNTL_MI_ERRS))) 143225cf1a30Sjl 143325cf1a30Sjl 143425cf1a30Sjl static int 143525cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error) 143625cf1a30Sjl { 143725cf1a30Sjl if (ptrl_error) { 143825cf1a30Sjl return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE); 143925cf1a30Sjl } else { 144025cf1a30Sjl return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE); 144125cf1a30Sjl } 144225cf1a30Sjl } 144325cf1a30Sjl 144425cf1a30Sjl void 144525cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value) 144625cf1a30Sjl { 144737afe445Shyw int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank; 144837afe445Shyw 144937afe445Shyw if (mcp->mc_speedup_period[ebank] > 0) 14500cc8ae86Sav value |= mc_max_speed; 14510cc8ae86Sav else 14520cc8ae86Sav value |= mcp->mc_speed; 145325cf1a30Sjl ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value); 145425cf1a30Sjl } 145525cf1a30Sjl 145625cf1a30Sjl static void 145725cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 145825cf1a30Sjl { 145925cf1a30Sjl flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 146025cf1a30Sjl MAC_CNTL_PTRL_ERRS; 146125cf1a30Sjl flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank)); 146225cf1a30Sjl flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank)); 146325cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 1464*aeb241b2Sav flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num; 146525cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 146625cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 146725cf1a30Sjl } 146825cf1a30Sjl 146925cf1a30Sjl static void 147025cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 147125cf1a30Sjl { 147225cf1a30Sjl uint32_t status, old_status; 147325cf1a30Sjl 147425cf1a30Sjl status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 147525cf1a30Sjl MAC_CNTL_MI_ERRS; 147625cf1a30Sjl old_status = 0; 147725cf1a30Sjl 147825cf1a30Sjl /* we keep reading until the status is stable */ 147925cf1a30Sjl while (old_status != status) { 148025cf1a30Sjl old_status = status; 148125cf1a30Sjl flt_stat->mf_err_add = 148225cf1a30Sjl LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank)); 148325cf1a30Sjl flt_stat->mf_err_log = 148425cf1a30Sjl LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank)); 148525cf1a30Sjl status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 148625cf1a30Sjl MAC_CNTL_MI_ERRS; 148725cf1a30Sjl if (status == old_status) { 148825cf1a30Sjl break; 148925cf1a30Sjl } 149025cf1a30Sjl } 149125cf1a30Sjl 149225cf1a30Sjl flt_stat->mf_cntl = status; 149325cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 1494*aeb241b2Sav flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num; 149525cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 149625cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 149725cf1a30Sjl } 149825cf1a30Sjl 149925cf1a30Sjl 150025cf1a30Sjl /* 150125cf1a30Sjl * Error philosophy for mirror mode: 150225cf1a30Sjl * 150325cf1a30Sjl * PTRL (The error address for both banks are same, since ptrl stops if it 150425cf1a30Sjl * detects error.) 150525cf1a30Sjl * - Compaire error Report CMPE. 150625cf1a30Sjl * 150725cf1a30Sjl * - UE-UE Report MUE. No rewrite. 150825cf1a30Sjl * 150925cf1a30Sjl * - UE-* UE-(CE/OK). Rewrite to scrub UE. Report SUE. 151025cf1a30Sjl * 151125cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 151225cf1a30Sjl * If CE is permanent, inform SCF. Once for each 151325cf1a30Sjl * Dimm. If CE becomes UE or CMPE, go back to above. 151425cf1a30Sjl * 151525cf1a30Sjl * 151625cf1a30Sjl * MI (The error addresses for each bank are the same or different.) 151725cf1a30Sjl * - Compair error If addresses are the same. Just CMPE. 151825cf1a30Sjl * If addresses are different (this could happen 151925cf1a30Sjl * as a result of scrubbing. Report each seperately. 152025cf1a30Sjl * Only report error info on each side. 152125cf1a30Sjl * 152225cf1a30Sjl * - UE-UE Addresses are the same. Report MUE. 152325cf1a30Sjl * Addresses are different. Report SUE on each bank. 152425cf1a30Sjl * Rewrite to clear UE. 152525cf1a30Sjl * 152625cf1a30Sjl * - UE-* UE-(CE/OK) 152725cf1a30Sjl * Rewrite to clear UE. Report SUE for the bank. 152825cf1a30Sjl * 152925cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 153025cf1a30Sjl * If CE becomes UE or CMPE, go back to above. 153125cf1a30Sjl * 153225cf1a30Sjl */ 153325cf1a30Sjl 153425cf1a30Sjl static int 153525cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat) 153625cf1a30Sjl { 153725cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 153825cf1a30Sjl int i; 153925cf1a30Sjl int rv = 0; 154025cf1a30Sjl 154125cf1a30Sjl MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n", 154225cf1a30Sjl flt_stat[0].mf_cntl, flt_stat[1].mf_cntl); 154325cf1a30Sjl 154425cf1a30Sjl if (ptrl_error) { 154525cf1a30Sjl if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) 154625cf1a30Sjl & MAC_CNTL_PTRL_ERRS) == 0) 154725cf1a30Sjl return (0); 154825cf1a30Sjl } else { 154925cf1a30Sjl if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) 155025cf1a30Sjl & MAC_CNTL_MI_ERRS) == 0) 155125cf1a30Sjl return (0); 155225cf1a30Sjl } 155325cf1a30Sjl 155425cf1a30Sjl /* 155525cf1a30Sjl * First we take care of the case of CE 155625cf1a30Sjl * because they can become UE or CMPE 155725cf1a30Sjl */ 155825cf1a30Sjl for (i = 0; i < 2; i++) { 155925cf1a30Sjl if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) { 156025cf1a30Sjl MC_LOG("CE detected on bank %d\n", 156125cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank); 156225cf1a30Sjl mc_scrub_ce(mcp, flt_stat[i].mf_flt_maddr.ma_bank, 156325cf1a30Sjl &flt_stat[i], ptrl_error); 156425cf1a30Sjl rv = 1; 156525cf1a30Sjl } 156625cf1a30Sjl } 156725cf1a30Sjl 156825cf1a30Sjl /* The above scrubbing can turn CE into UE or CMPE */ 156925cf1a30Sjl 157025cf1a30Sjl /* 157125cf1a30Sjl * Now we distinguish two cases: same address or not 157225cf1a30Sjl * the same address. It might seem more intuitive to 157325cf1a30Sjl * distinguish PTRL v.s. MI error but it is more 157425cf1a30Sjl * complicated that way. 157525cf1a30Sjl */ 157625cf1a30Sjl 157725cf1a30Sjl if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) { 157825cf1a30Sjl 157925cf1a30Sjl if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) || 158025cf1a30Sjl IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) { 158125cf1a30Sjl flt_stat[0].mf_type = FLT_TYPE_CMPE; 158225cf1a30Sjl flt_stat[1].mf_type = FLT_TYPE_CMPE; 158325cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 158425cf1a30Sjl MC_LOG("cmpe error detected\n"); 158525cf1a30Sjl mc_aflt->mflt_nflts = 2; 158625cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 158725cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 158825cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 158925cf1a30Sjl mc_err_drain(mc_aflt); 159025cf1a30Sjl return (1); 159125cf1a30Sjl } 159225cf1a30Sjl 159325cf1a30Sjl if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) && 159425cf1a30Sjl IS_UE(flt_stat[1].mf_cntl, ptrl_error)) { 159525cf1a30Sjl /* Both side are UE's */ 159625cf1a30Sjl 159725cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[0]); 159825cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[1]); 159925cf1a30Sjl MC_LOG("MUE detected\n"); 16000cc8ae86Sav flt_stat[0].mf_type = FLT_TYPE_MUE; 16010cc8ae86Sav flt_stat[1].mf_type = FLT_TYPE_MUE; 160225cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_MUE; 160325cf1a30Sjl mc_aflt->mflt_nflts = 2; 160425cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 160525cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 160625cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 160725cf1a30Sjl mc_err_drain(mc_aflt); 160825cf1a30Sjl return (1); 160925cf1a30Sjl } 161025cf1a30Sjl 161125cf1a30Sjl /* Now the only case is UE/CE, UE/OK, or don't care */ 161225cf1a30Sjl for (i = 0; i < 2; i++) { 161325cf1a30Sjl if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 16140cc8ae86Sav 16150cc8ae86Sav /* rewrite can clear the one side UE error */ 16160cc8ae86Sav 161725cf1a30Sjl if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) { 161825cf1a30Sjl (void) do_rewrite(mcp, 161925cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank, 162025cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_dimm_addr); 162125cf1a30Sjl } 162225cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_UE; 162325cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[i]); 162425cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_SUE; 162525cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 162625cf1a30Sjl mc_aflt->mflt_nflts = 1; 162725cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 162825cf1a30Sjl mc_err_drain(mc_aflt); 162925cf1a30Sjl /* Once we hit a UE/CE or UE/OK case, done */ 163025cf1a30Sjl return (1); 163125cf1a30Sjl } 163225cf1a30Sjl } 163325cf1a30Sjl 163425cf1a30Sjl } else { 163525cf1a30Sjl /* 163625cf1a30Sjl * addresses are different. That means errors 163725cf1a30Sjl * on the 2 banks are not related at all. 163825cf1a30Sjl */ 163925cf1a30Sjl for (i = 0; i < 2; i++) { 164025cf1a30Sjl if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) { 164125cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_CMPE; 164225cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 164325cf1a30Sjl MC_LOG("cmpe error detected\n"); 164425cf1a30Sjl mc_aflt->mflt_nflts = 1; 164525cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 164625cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 164725cf1a30Sjl mc_err_drain(mc_aflt); 164825cf1a30Sjl /* no more report on this bank */ 164925cf1a30Sjl flt_stat[i].mf_cntl = 0; 165025cf1a30Sjl rv = 1; 165125cf1a30Sjl } 165225cf1a30Sjl } 165325cf1a30Sjl 16540cc8ae86Sav /* rewrite can clear the one side UE error */ 16550cc8ae86Sav 165625cf1a30Sjl for (i = 0; i < 2; i++) { 165725cf1a30Sjl if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 165825cf1a30Sjl (void) do_rewrite(mcp, 165925cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank, 166025cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_dimm_addr); 166125cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_UE; 166225cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[i]); 166325cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_SUE; 166425cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 166525cf1a30Sjl mc_aflt->mflt_nflts = 1; 166625cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 166725cf1a30Sjl mc_err_drain(mc_aflt); 166825cf1a30Sjl rv = 1; 166925cf1a30Sjl } 167025cf1a30Sjl } 167125cf1a30Sjl } 167225cf1a30Sjl return (rv); 167325cf1a30Sjl } 167425cf1a30Sjl static void 1675738dd194Shyw mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr) 167625cf1a30Sjl { 167725cf1a30Sjl mc_aflt_t mc_aflt; 167825cf1a30Sjl mc_flt_stat_t flt_stat[2], mi_flt_stat[2]; 16790cc8ae86Sav int i; 16800cc8ae86Sav int mi_valid; 168125cf1a30Sjl 1682738dd194Shyw ASSERT(rsaddr); 1683738dd194Shyw 168425cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 168525cf1a30Sjl bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t)); 168625cf1a30Sjl bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t)); 168725cf1a30Sjl 168825cf1a30Sjl mc_aflt.mflt_mcp = mcp; 168925cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 169025cf1a30Sjl 169125cf1a30Sjl /* Now read all the registers into flt_stat */ 169225cf1a30Sjl 16930cc8ae86Sav for (i = 0; i < 2; i++) { 16940cc8ae86Sav MC_LOG("Reading registers of bank %d\n", bank); 16950cc8ae86Sav /* patrol registers */ 16960cc8ae86Sav mc_read_ptrl_reg(mcp, bank, &flt_stat[i]); 169725cf1a30Sjl 1698738dd194Shyw /* 1699738dd194Shyw * In mirror mode, it is possible that only one bank 1700738dd194Shyw * may report the error. We need to check for it to 1701738dd194Shyw * ensure we pick the right addr value for patrol restart. 1702738dd194Shyw * Note that if both banks reported errors, we pick the 1703738dd194Shyw * 2nd one. Both banks should reported the same error address. 1704738dd194Shyw */ 1705738dd194Shyw if (flt_stat[i].mf_cntl & MAC_CNTL_PTRL_ERRS) 1706738dd194Shyw rsaddr->mi_restartaddr = flt_stat[i].mf_flt_maddr; 170725cf1a30Sjl 17080cc8ae86Sav MC_LOG("ptrl registers cntl %x add %x log %x\n", 17090cc8ae86Sav flt_stat[i].mf_cntl, 17100cc8ae86Sav flt_stat[i].mf_err_add, 17110cc8ae86Sav flt_stat[i].mf_err_log); 171225cf1a30Sjl 17130cc8ae86Sav /* MI registers */ 17140cc8ae86Sav mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]); 171525cf1a30Sjl 17160cc8ae86Sav MC_LOG("MI registers cntl %x add %x log %x\n", 17170cc8ae86Sav mi_flt_stat[i].mf_cntl, 17180cc8ae86Sav mi_flt_stat[i].mf_err_add, 17190cc8ae86Sav mi_flt_stat[i].mf_err_log); 172025cf1a30Sjl 17210cc8ae86Sav bank = bank^1; 17220cc8ae86Sav } 172325cf1a30Sjl 172425cf1a30Sjl /* clear errors once we read all the registers */ 17250cc8ae86Sav MAC_CLEAR_ERRS(mcp, bank, 172625cf1a30Sjl (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 172725cf1a30Sjl 17280cc8ae86Sav MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 17290cc8ae86Sav 17300cc8ae86Sav /* Process MI errors first */ 173125cf1a30Sjl 17320cc8ae86Sav /* if not error mode, cntl1 is 0 */ 17330cc8ae86Sav if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 17340cc8ae86Sav (mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 17350cc8ae86Sav mi_flt_stat[0].mf_cntl = 0; 17360cc8ae86Sav 17370cc8ae86Sav if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 17380cc8ae86Sav (mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 17390cc8ae86Sav mi_flt_stat[1].mf_cntl = 0; 174025cf1a30Sjl 17410cc8ae86Sav mc_aflt.mflt_is_ptrl = 0; 17420cc8ae86Sav mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]); 17430cc8ae86Sav 17440cc8ae86Sav if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 17450cc8ae86Sav MAC_CNTL_PTRL_ERR_SHIFT) == 17460cc8ae86Sav ((mi_flt_stat[0].mf_cntl & MAC_CNTL_MI_ERRS) >> 17470cc8ae86Sav MAC_CNTL_MI_ERR_SHIFT)) && 17480cc8ae86Sav (flt_stat[0].mf_err_add == mi_flt_stat[0].mf_err_add) && 17490cc8ae86Sav (((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 17500cc8ae86Sav MAC_CNTL_PTRL_ERR_SHIFT) == 17510cc8ae86Sav ((mi_flt_stat[1].mf_cntl & MAC_CNTL_MI_ERRS) >> 17520cc8ae86Sav MAC_CNTL_MI_ERR_SHIFT)) && 17530cc8ae86Sav (flt_stat[1].mf_err_add == mi_flt_stat[1].mf_err_add)) { 17540cc8ae86Sav #ifdef DEBUG 17550cc8ae86Sav MC_LOG("discarding PTRL error because " 17560cc8ae86Sav "it is the same as MI\n"); 17570cc8ae86Sav #endif 1758738dd194Shyw rsaddr->mi_valid = mi_valid; 17590cc8ae86Sav return; 17600cc8ae86Sav } 176125cf1a30Sjl /* if not error mode, cntl1 is 0 */ 176225cf1a30Sjl if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 176325cf1a30Sjl (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 176425cf1a30Sjl flt_stat[0].mf_cntl = 0; 176525cf1a30Sjl 176625cf1a30Sjl if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 176725cf1a30Sjl (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 176825cf1a30Sjl flt_stat[1].mf_cntl = 0; 176925cf1a30Sjl 177025cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 1771738dd194Shyw rsaddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]); 177225cf1a30Sjl } 177325cf1a30Sjl static int 177425cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt, 177525cf1a30Sjl mc_flt_stat_t *flt_stat) 177625cf1a30Sjl { 177725cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 177825cf1a30Sjl int rv = 0; 177925cf1a30Sjl 178025cf1a30Sjl mc_aflt->mflt_erpt_class = NULL; 178125cf1a30Sjl if (IS_UE(flt_stat->mf_cntl, ptrl_error)) { 178225cf1a30Sjl MC_LOG("UE deteceted\n"); 178325cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 178425cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 178525cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 178625cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 178725cf1a30Sjl rv = 1; 178825cf1a30Sjl } else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) { 178925cf1a30Sjl MC_LOG("CE deteceted\n"); 179025cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 179125cf1a30Sjl 179225cf1a30Sjl /* Error type can change after scrubing */ 179325cf1a30Sjl mc_scrub_ce(mcp, bank, flt_stat, ptrl_error); 179425cf1a30Sjl 179525cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 179625cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CE; 179725cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 179825cf1a30Sjl } else if (flt_stat->mf_type == FLT_TYPE_UE) { 179925cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 180025cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 180125cf1a30Sjl } 180225cf1a30Sjl rv = 1; 180325cf1a30Sjl } 180425cf1a30Sjl MC_LOG("mc_process_error: fault type %x erpt %s\n", 180525cf1a30Sjl flt_stat->mf_type, 180625cf1a30Sjl mc_aflt->mflt_erpt_class); 180725cf1a30Sjl if (mc_aflt->mflt_erpt_class) { 180825cf1a30Sjl mc_aflt->mflt_stat[0] = flt_stat; 180925cf1a30Sjl mc_aflt->mflt_nflts = 1; 181025cf1a30Sjl mc_err_drain(mc_aflt); 181125cf1a30Sjl } 181225cf1a30Sjl return (rv); 181325cf1a30Sjl } 181425cf1a30Sjl 181525cf1a30Sjl static void 1816738dd194Shyw mc_error_handler(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr) 181725cf1a30Sjl { 181825cf1a30Sjl mc_aflt_t mc_aflt; 181925cf1a30Sjl mc_flt_stat_t flt_stat, mi_flt_stat; 18200cc8ae86Sav int mi_valid; 182125cf1a30Sjl 182225cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 182325cf1a30Sjl bzero(&flt_stat, sizeof (mc_flt_stat_t)); 182425cf1a30Sjl bzero(&mi_flt_stat, sizeof (mc_flt_stat_t)); 182525cf1a30Sjl 182625cf1a30Sjl mc_aflt.mflt_mcp = mcp; 182725cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 182825cf1a30Sjl 182925cf1a30Sjl /* patrol registers */ 183025cf1a30Sjl mc_read_ptrl_reg(mcp, bank, &flt_stat); 183125cf1a30Sjl 1832738dd194Shyw ASSERT(rsaddr); 1833738dd194Shyw rsaddr->mi_restartaddr = flt_stat.mf_flt_maddr; 183425cf1a30Sjl 183525cf1a30Sjl MC_LOG("ptrl registers cntl %x add %x log %x\n", 183625cf1a30Sjl flt_stat.mf_cntl, 183725cf1a30Sjl flt_stat.mf_err_add, 183825cf1a30Sjl flt_stat.mf_err_log); 183925cf1a30Sjl 184025cf1a30Sjl /* MI registers */ 184125cf1a30Sjl mc_read_mi_reg(mcp, bank, &mi_flt_stat); 184225cf1a30Sjl 18430cc8ae86Sav 184425cf1a30Sjl MC_LOG("MI registers cntl %x add %x log %x\n", 184525cf1a30Sjl mi_flt_stat.mf_cntl, 184625cf1a30Sjl mi_flt_stat.mf_err_add, 184725cf1a30Sjl mi_flt_stat.mf_err_log); 184825cf1a30Sjl 184925cf1a30Sjl /* clear errors once we read all the registers */ 185025cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 185125cf1a30Sjl 18520cc8ae86Sav mc_aflt.mflt_is_ptrl = 0; 18530cc8ae86Sav if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) && 18540cc8ae86Sav ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 18550cc8ae86Sav ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 18560cc8ae86Sav mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat); 18570cc8ae86Sav } 18580cc8ae86Sav 18590cc8ae86Sav if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >> 18600cc8ae86Sav MAC_CNTL_PTRL_ERR_SHIFT) == 18610cc8ae86Sav ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) >> 18620cc8ae86Sav MAC_CNTL_MI_ERR_SHIFT)) && 18630cc8ae86Sav (flt_stat.mf_err_add == mi_flt_stat.mf_err_add)) { 18640cc8ae86Sav #ifdef DEBUG 18650cc8ae86Sav MC_LOG("discarding PTRL error because " 18660cc8ae86Sav "it is the same as MI\n"); 18670cc8ae86Sav #endif 1868738dd194Shyw rsaddr->mi_valid = mi_valid; 18690cc8ae86Sav return; 18700cc8ae86Sav } 18710cc8ae86Sav 187225cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 187325cf1a30Sjl if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) && 187425cf1a30Sjl ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 187525cf1a30Sjl ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 1876738dd194Shyw rsaddr->mi_valid = mc_process_error(mcp, bank, 187725cf1a30Sjl &mc_aflt, &flt_stat); 187825cf1a30Sjl } 187925cf1a30Sjl } 188025cf1a30Sjl /* 188125cf1a30Sjl * memory patrol error handling algorithm: 188225cf1a30Sjl * timeout() is used to do periodic polling 188325cf1a30Sjl * This is the flow chart. 188425cf1a30Sjl * timeout -> 188525cf1a30Sjl * mc_check_errors() 188625cf1a30Sjl * if memory bank is installed, read the status register 188725cf1a30Sjl * if any error bit is set, 188825cf1a30Sjl * -> mc_error_handler() 188925cf1a30Sjl * -> read all error regsiters 189025cf1a30Sjl * -> mc_process_error() 189125cf1a30Sjl * determine error type 189225cf1a30Sjl * rewrite to clear error or scrub to determine CE type 189325cf1a30Sjl * inform SCF on permanent CE 189425cf1a30Sjl * -> mc_err_drain 189525cf1a30Sjl * page offline processing 189625cf1a30Sjl * -> mc_ereport_post() 189725cf1a30Sjl */ 189825cf1a30Sjl 189925cf1a30Sjl static void 190025cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp) 190125cf1a30Sjl { 1902738dd194Shyw mc_rsaddr_info_t rsaddr_info; 190325cf1a30Sjl int i, error_count = 0; 190425cf1a30Sjl uint32_t stat, cntl; 19050cc8ae86Sav int running; 1906cfb9e062Shyw int wrapped; 190737afe445Shyw int ebk; 190825cf1a30Sjl 190925cf1a30Sjl /* 191025cf1a30Sjl * scan errors. 191125cf1a30Sjl */ 19120cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) 19130cc8ae86Sav return; 19140cc8ae86Sav 191525cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 191625cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 191725cf1a30Sjl stat = ldphysio(MAC_PTRL_STAT(mcp, i)); 191825cf1a30Sjl cntl = ldphysio(MAC_PTRL_CNTL(mcp, i)); 19190cc8ae86Sav running = cntl & MAC_CNTL_PTRL_START; 1920cfb9e062Shyw wrapped = cntl & MAC_CNTL_PTRL_ADD_MAX; 19210cc8ae86Sav 192237afe445Shyw /* Compute the effective bank idx */ 192337afe445Shyw ebk = (IS_MIRROR(mcp, i)) ? MIRROR_IDX(i) : i; 192437afe445Shyw 1925cfb9e062Shyw if (mc_debug_show_all || stat) { 1926cfb9e062Shyw MC_LOG("/LSB%d/B%d stat %x cntl %x\n", 1927cfb9e062Shyw mcp->mc_board_num, i, 1928cfb9e062Shyw stat, cntl); 1929cfb9e062Shyw } 1930cfb9e062Shyw 1931cfb9e062Shyw /* 1932cfb9e062Shyw * Update stats and reset flag if the HW patrol 1933cfb9e062Shyw * wrapped around in its scan. 1934cfb9e062Shyw */ 1935cfb9e062Shyw if (wrapped) { 193625cf1a30Sjl MAC_CLEAR_MAX(mcp, i); 193737afe445Shyw mcp->mc_period[ebk]++; 193837afe445Shyw if (IS_MIRROR(mcp, i)) 193937afe445Shyw MC_LOG("mirror mc period %ld on " 194037afe445Shyw "/LSB%d/B%d\n", mcp->mc_period[ebk], 194137afe445Shyw mcp->mc_board_num, i); 194237afe445Shyw else { 194337afe445Shyw MC_LOG("mc period %ld on " 194437afe445Shyw "/LSB%d/B%d\n", mcp->mc_period[ebk], 194537afe445Shyw mcp->mc_board_num, i); 194637afe445Shyw } 194725cf1a30Sjl } 1948cfb9e062Shyw 1949cfb9e062Shyw if (running) { 1950cfb9e062Shyw /* 1951cfb9e062Shyw * Mac patrol HW is still running. 1952cfb9e062Shyw * Normally when an error is detected, 1953cfb9e062Shyw * the HW patrol will stop so that we 1954cfb9e062Shyw * can collect error data for reporting. 1955cfb9e062Shyw * Certain errors (MI errors) detected may not 1956cfb9e062Shyw * cause the HW patrol to stop which is a 1957cfb9e062Shyw * problem since we cannot read error data while 1958cfb9e062Shyw * the HW patrol is running. SW is not allowed 1959cfb9e062Shyw * to stop the HW patrol while it is running 1960cfb9e062Shyw * as it may cause HW inconsistency. This is 1961cfb9e062Shyw * described in a HW errata. 1962cfb9e062Shyw * In situations where we detected errors 1963cfb9e062Shyw * that may not cause the HW patrol to stop. 1964cfb9e062Shyw * We speed up the HW patrol scanning in 1965cfb9e062Shyw * the hope that it will find the 'real' PTRL 1966cfb9e062Shyw * errors associated with the previous errors 1967cfb9e062Shyw * causing the HW to finally stop so that we 1968cfb9e062Shyw * can do the reporting. 1969cfb9e062Shyw */ 1970cfb9e062Shyw /* 1971cfb9e062Shyw * Check to see if we did speed up 1972cfb9e062Shyw * the HW patrol due to previous errors 1973cfb9e062Shyw * detected that did not cause the patrol 1974cfb9e062Shyw * to stop. We only do it if HW patrol scan 1975cfb9e062Shyw * wrapped (counted as completing a 'period'). 1976cfb9e062Shyw */ 197737afe445Shyw if (mcp->mc_speedup_period[ebk] > 0) { 1978cfb9e062Shyw if (wrapped && 197937afe445Shyw (--mcp->mc_speedup_period[ebk] == 0)) { 1980cfb9e062Shyw /* 1981cfb9e062Shyw * We did try to speed up. 1982cfb9e062Shyw * The speed up period has expired 1983cfb9e062Shyw * and the HW patrol is still running. 1984cfb9e062Shyw * The errors must be intermittent. 1985cfb9e062Shyw * We have no choice but to ignore 1986cfb9e062Shyw * them, reset the scan speed to normal 198737afe445Shyw * and clear the MI error bits. For 198837afe445Shyw * mirror mode, we need to clear errors 198937afe445Shyw * on both banks. 1990cfb9e062Shyw */ 1991cfb9e062Shyw MC_LOG("Clearing MI errors\n"); 1992cfb9e062Shyw MAC_CLEAR_ERRS(mcp, i, 1993cfb9e062Shyw MAC_CNTL_MI_ERRS); 199437afe445Shyw 199537afe445Shyw if (IS_MIRROR(mcp, i)) { 199637afe445Shyw MC_LOG("Clearing Mirror MI errs\n"); 199737afe445Shyw MAC_CLEAR_ERRS(mcp, i^1, 199837afe445Shyw MAC_CNTL_MI_ERRS); 199937afe445Shyw } 2000cfb9e062Shyw } 2001cfb9e062Shyw } else if (stat & MAC_STAT_MI_ERRS) { 2002cfb9e062Shyw /* 2003cfb9e062Shyw * MI errors detected but we cannot 2004cfb9e062Shyw * report them since the HW patrol 2005cfb9e062Shyw * is still running. 2006cfb9e062Shyw * We will attempt to speed up the 2007cfb9e062Shyw * scanning and hopefully the HW 2008cfb9e062Shyw * can detect PRTL errors at the same 2009cfb9e062Shyw * location that cause the HW patrol 2010cfb9e062Shyw * to stop. 2011cfb9e062Shyw */ 201237afe445Shyw mcp->mc_speedup_period[ebk] = 2; 20130cc8ae86Sav MAC_CMD(mcp, i, 0); 20140cc8ae86Sav } 2015cfb9e062Shyw } else if (stat & (MAC_STAT_PTRL_ERRS | 2016cfb9e062Shyw MAC_STAT_MI_ERRS)) { 2017cfb9e062Shyw /* 2018cfb9e062Shyw * HW Patrol has stopped and we found errors. 2019cfb9e062Shyw * Proceed to collect and report error info. 2020cfb9e062Shyw */ 202137afe445Shyw mcp->mc_speedup_period[ebk] = 0; 2022738dd194Shyw rsaddr_info.mi_valid = 0; 2023738dd194Shyw rsaddr_info.mi_injectrestart = 0; 2024738dd194Shyw if (IS_MIRROR(mcp, i)) { 2025738dd194Shyw mc_error_handler_mir(mcp, i, &rsaddr_info); 2026738dd194Shyw } else { 2027738dd194Shyw mc_error_handler(mcp, i, &rsaddr_info); 2028738dd194Shyw } 2029cfb9e062Shyw 2030cfb9e062Shyw error_count++; 2031738dd194Shyw restart_patrol(mcp, i, &rsaddr_info); 203225cf1a30Sjl } else { 2033cfb9e062Shyw /* 2034cfb9e062Shyw * HW patrol scan has apparently stopped 2035cfb9e062Shyw * but no errors detected/flagged. 2036cfb9e062Shyw * Restart the HW patrol just to be sure. 203737afe445Shyw * In mirror mode, the odd bank might have 203837afe445Shyw * reported errors that caused the patrol to 203937afe445Shyw * stop. We'll defer the restart to the odd 204037afe445Shyw * bank in this case. 2041cfb9e062Shyw */ 204237afe445Shyw if (!IS_MIRROR(mcp, i) || (i & 0x1)) 204337afe445Shyw restart_patrol(mcp, i, NULL); 204425cf1a30Sjl } 204525cf1a30Sjl } 204625cf1a30Sjl } 204725cf1a30Sjl if (error_count > 0) 204825cf1a30Sjl mcp->mc_last_error += error_count; 204925cf1a30Sjl else 205025cf1a30Sjl mcp->mc_last_error = 0; 205125cf1a30Sjl } 205225cf1a30Sjl 20530cc8ae86Sav /* 20540cc8ae86Sav * mc_polling -- Check errors for only one instance, 20550cc8ae86Sav * but process errors for all instances to make sure we drain the errors 20560cc8ae86Sav * faster than they can be accumulated. 20570cc8ae86Sav * 20580cc8ae86Sav * Polling on each board should be done only once per each 20590cc8ae86Sav * mc_patrol_interval_sec. This is equivalent to setting mc_tick_left 20600cc8ae86Sav * to OPL_MAX_BOARDS and decrement by 1 on each timeout. 20610cc8ae86Sav * Once mc_tick_left becomes negative, the board becomes a candidate 20620cc8ae86Sav * for polling because it has waited for at least 20630cc8ae86Sav * mc_patrol_interval_sec's long. If mc_timeout_period is calculated 20640cc8ae86Sav * differently, this has to beupdated accordingly. 20650cc8ae86Sav */ 206625cf1a30Sjl 206725cf1a30Sjl static void 20680cc8ae86Sav mc_polling(void) 206925cf1a30Sjl { 20700cc8ae86Sav int i, scan_error; 20710cc8ae86Sav mc_opl_t *mcp; 207225cf1a30Sjl 207325cf1a30Sjl 20740cc8ae86Sav scan_error = 1; 20750cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 20760cc8ae86Sav mutex_enter(&mcmutex); 20770cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) { 20780cc8ae86Sav mutex_exit(&mcmutex); 20790cc8ae86Sav continue; 20800cc8ae86Sav } 20810cc8ae86Sav mutex_enter(&mcp->mc_lock); 20820cc8ae86Sav mutex_exit(&mcmutex); 2083738dd194Shyw if (!(mcp->mc_status & MC_POLL_RUNNING)) { 2084738dd194Shyw mutex_exit(&mcp->mc_lock); 2085738dd194Shyw continue; 2086738dd194Shyw } 20870cc8ae86Sav if (scan_error && mcp->mc_tick_left <= 0) { 20880cc8ae86Sav mc_check_errors_func((void *)mcp); 20890cc8ae86Sav mcp->mc_tick_left = OPL_MAX_BOARDS; 20900cc8ae86Sav scan_error = 0; 20910cc8ae86Sav } else { 20920cc8ae86Sav mcp->mc_tick_left--; 20930cc8ae86Sav } 20940cc8ae86Sav mc_process_scf_log(mcp); 20950cc8ae86Sav mutex_exit(&mcp->mc_lock); 209625cf1a30Sjl } 209725cf1a30Sjl } 209825cf1a30Sjl 209925cf1a30Sjl static void 210025cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr) 210125cf1a30Sjl { 210225cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 210325cf1a30Sjl maddr->ma_bank = bank; 210425cf1a30Sjl maddr->ma_dimm_addr = 0; 210525cf1a30Sjl } 210625cf1a30Sjl 210725cf1a30Sjl typedef struct mc_mem_range { 210825cf1a30Sjl uint64_t addr; 210925cf1a30Sjl uint64_t size; 211025cf1a30Sjl } mc_mem_range_t; 211125cf1a30Sjl 211225cf1a30Sjl static int 211325cf1a30Sjl get_base_address(mc_opl_t *mcp) 211425cf1a30Sjl { 211525cf1a30Sjl mc_mem_range_t *mem_range; 211625cf1a30Sjl int len; 211725cf1a30Sjl 211825cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 211925cf1a30Sjl "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) { 212025cf1a30Sjl return (DDI_FAILURE); 212125cf1a30Sjl } 212225cf1a30Sjl 212325cf1a30Sjl mcp->mc_start_address = mem_range->addr; 212425cf1a30Sjl mcp->mc_size = mem_range->size; 212525cf1a30Sjl 212625cf1a30Sjl kmem_free(mem_range, len); 212725cf1a30Sjl return (DDI_SUCCESS); 212825cf1a30Sjl } 212925cf1a30Sjl 213025cf1a30Sjl struct mc_addr_spec { 213125cf1a30Sjl uint32_t bank; 213225cf1a30Sjl uint32_t phys_hi; 213325cf1a30Sjl uint32_t phys_lo; 213425cf1a30Sjl }; 213525cf1a30Sjl 213625cf1a30Sjl #define REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo) 213725cf1a30Sjl 213825cf1a30Sjl static char *mc_tbl_name[] = { 213925cf1a30Sjl "cs0-mc-pa-trans-table", 214025cf1a30Sjl "cs1-mc-pa-trans-table" 214125cf1a30Sjl }; 214225cf1a30Sjl 2143738dd194Shyw /* 2144738dd194Shyw * This routine performs a rangecheck for a given PA 2145738dd194Shyw * to see if it belongs to the memory range for this board. 2146738dd194Shyw * Return 1 if it is valid (within the range) and 0 otherwise 2147738dd194Shyw */ 214825cf1a30Sjl static int 2149738dd194Shyw mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa) 215025cf1a30Sjl { 2151738dd194Shyw if ((pa < mcp->mc_start_address) || 2152738dd194Shyw (mcp->mc_start_address + mcp->mc_size <= pa)) 2153738dd194Shyw return (0); 2154738dd194Shyw else 2155738dd194Shyw return (1); 215625cf1a30Sjl } 215725cf1a30Sjl 215825cf1a30Sjl static void 215925cf1a30Sjl mc_memlist_delete(struct memlist *mlist) 216025cf1a30Sjl { 216125cf1a30Sjl struct memlist *ml; 216225cf1a30Sjl 216325cf1a30Sjl for (ml = mlist; ml; ml = mlist) { 216425cf1a30Sjl mlist = ml->next; 216525cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 216625cf1a30Sjl } 216725cf1a30Sjl } 216825cf1a30Sjl 216925cf1a30Sjl static struct memlist * 217025cf1a30Sjl mc_memlist_dup(struct memlist *mlist) 217125cf1a30Sjl { 217225cf1a30Sjl struct memlist *hl = NULL, *tl, **mlp; 217325cf1a30Sjl 217425cf1a30Sjl if (mlist == NULL) 217525cf1a30Sjl return (NULL); 217625cf1a30Sjl 217725cf1a30Sjl mlp = &hl; 217825cf1a30Sjl tl = *mlp; 217925cf1a30Sjl for (; mlist; mlist = mlist->next) { 218025cf1a30Sjl *mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP); 218125cf1a30Sjl (*mlp)->address = mlist->address; 218225cf1a30Sjl (*mlp)->size = mlist->size; 218325cf1a30Sjl (*mlp)->prev = tl; 218425cf1a30Sjl tl = *mlp; 218525cf1a30Sjl mlp = &((*mlp)->next); 218625cf1a30Sjl } 218725cf1a30Sjl *mlp = NULL; 218825cf1a30Sjl 218925cf1a30Sjl return (hl); 219025cf1a30Sjl } 219125cf1a30Sjl 219225cf1a30Sjl 219325cf1a30Sjl static struct memlist * 219425cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len) 219525cf1a30Sjl { 219625cf1a30Sjl uint64_t end; 219725cf1a30Sjl struct memlist *ml, *tl, *nlp; 219825cf1a30Sjl 219925cf1a30Sjl if (mlist == NULL) 220025cf1a30Sjl return (NULL); 220125cf1a30Sjl 220225cf1a30Sjl end = base + len; 220325cf1a30Sjl if ((end <= mlist->address) || (base == end)) 220425cf1a30Sjl return (mlist); 220525cf1a30Sjl 220625cf1a30Sjl for (tl = ml = mlist; ml; tl = ml, ml = nlp) { 220725cf1a30Sjl uint64_t mend; 220825cf1a30Sjl 220925cf1a30Sjl nlp = ml->next; 221025cf1a30Sjl 221125cf1a30Sjl if (end <= ml->address) 221225cf1a30Sjl break; 221325cf1a30Sjl 221425cf1a30Sjl mend = ml->address + ml->size; 221525cf1a30Sjl if (base < mend) { 221625cf1a30Sjl if (base <= ml->address) { 221725cf1a30Sjl ml->address = end; 221825cf1a30Sjl if (end >= mend) 221925cf1a30Sjl ml->size = 0ull; 222025cf1a30Sjl else 222125cf1a30Sjl ml->size = mend - ml->address; 222225cf1a30Sjl } else { 222325cf1a30Sjl ml->size = base - ml->address; 222425cf1a30Sjl if (end < mend) { 222525cf1a30Sjl struct memlist *nl; 222625cf1a30Sjl /* 222725cf1a30Sjl * splitting an memlist entry. 222825cf1a30Sjl */ 222925cf1a30Sjl nl = kmem_alloc(sizeof (struct memlist), 223025cf1a30Sjl KM_SLEEP); 223125cf1a30Sjl nl->address = end; 223225cf1a30Sjl nl->size = mend - nl->address; 223325cf1a30Sjl if ((nl->next = nlp) != NULL) 223425cf1a30Sjl nlp->prev = nl; 223525cf1a30Sjl nl->prev = ml; 223625cf1a30Sjl ml->next = nl; 223725cf1a30Sjl nlp = nl; 223825cf1a30Sjl } 223925cf1a30Sjl } 224025cf1a30Sjl if (ml->size == 0ull) { 224125cf1a30Sjl if (ml == mlist) { 224225cf1a30Sjl if ((mlist = nlp) != NULL) 224325cf1a30Sjl nlp->prev = NULL; 224425cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 224525cf1a30Sjl if (mlist == NULL) 224625cf1a30Sjl break; 224725cf1a30Sjl ml = nlp; 224825cf1a30Sjl } else { 224925cf1a30Sjl if ((tl->next = nlp) != NULL) 225025cf1a30Sjl nlp->prev = tl; 225125cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 225225cf1a30Sjl ml = tl; 225325cf1a30Sjl } 225425cf1a30Sjl } 225525cf1a30Sjl } 225625cf1a30Sjl } 225725cf1a30Sjl 225825cf1a30Sjl return (mlist); 225925cf1a30Sjl } 226025cf1a30Sjl 226125cf1a30Sjl static void 226225cf1a30Sjl mc_get_mlist(mc_opl_t *mcp) 226325cf1a30Sjl { 226425cf1a30Sjl struct memlist *mlist; 226525cf1a30Sjl 226625cf1a30Sjl memlist_read_lock(); 226725cf1a30Sjl mlist = mc_memlist_dup(phys_install); 226825cf1a30Sjl memlist_read_unlock(); 226925cf1a30Sjl 227025cf1a30Sjl if (mlist) { 227125cf1a30Sjl mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address); 227225cf1a30Sjl } 227325cf1a30Sjl 227425cf1a30Sjl if (mlist) { 227525cf1a30Sjl uint64_t startpa, endpa; 227625cf1a30Sjl 227725cf1a30Sjl startpa = mcp->mc_start_address + mcp->mc_size; 227825cf1a30Sjl endpa = ptob(physmax + 1); 227925cf1a30Sjl if (endpa > startpa) { 228025cf1a30Sjl mlist = mc_memlist_del_span(mlist, 228125cf1a30Sjl startpa, endpa - startpa); 228225cf1a30Sjl } 228325cf1a30Sjl } 228425cf1a30Sjl 228525cf1a30Sjl if (mlist) { 228625cf1a30Sjl mcp->mlist = mlist; 228725cf1a30Sjl } 228825cf1a30Sjl } 228925cf1a30Sjl 229025cf1a30Sjl int 229125cf1a30Sjl mc_board_add(mc_opl_t *mcp) 229225cf1a30Sjl { 229325cf1a30Sjl struct mc_addr_spec *macaddr; 22940cc8ae86Sav cs_status_t *cs_status; 22950cc8ae86Sav int len, len1, i, bk, cc; 2296738dd194Shyw mc_rsaddr_info_t rsaddr; 229725cf1a30Sjl uint32_t mirr; 22980cc8ae86Sav int nbanks = 0; 22990cc8ae86Sav uint64_t nbytes = 0; 230025cf1a30Sjl 230125cf1a30Sjl /* 230225cf1a30Sjl * Get configurations from "pseudo-mc" node which includes: 230325cf1a30Sjl * board# : LSB number 230425cf1a30Sjl * mac-addr : physical base address of MAC registers 230525cf1a30Sjl * csX-mac-pa-trans-table: translation table from DIMM address 230625cf1a30Sjl * to physical address or vice versa. 230725cf1a30Sjl */ 230825cf1a30Sjl mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip, 230925cf1a30Sjl DDI_PROP_DONTPASS, "board#", -1); 231025cf1a30Sjl 23110cc8ae86Sav if (mcp->mc_board_num == -1) { 23120cc8ae86Sav return (DDI_FAILURE); 23130cc8ae86Sav } 23140cc8ae86Sav 231525cf1a30Sjl /* 231625cf1a30Sjl * Get start address in this CAB. It can be gotten from 231725cf1a30Sjl * "sb-mem-ranges" property. 231825cf1a30Sjl */ 231925cf1a30Sjl 232025cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 232125cf1a30Sjl return (DDI_FAILURE); 232225cf1a30Sjl } 232325cf1a30Sjl /* get mac-pa trans tables */ 232425cf1a30Sjl for (i = 0; i < MC_TT_CS; i++) { 232525cf1a30Sjl len = MC_TT_ENTRIES; 232625cf1a30Sjl cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip, 232725cf1a30Sjl DDI_PROP_DONTPASS, mc_tbl_name[i], 232825cf1a30Sjl (caddr_t)mcp->mc_trans_table[i], &len); 232925cf1a30Sjl 233025cf1a30Sjl if (cc != DDI_SUCCESS) { 233125cf1a30Sjl bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES); 233225cf1a30Sjl } 233325cf1a30Sjl } 233425cf1a30Sjl mcp->mlist = NULL; 233525cf1a30Sjl 233625cf1a30Sjl mc_get_mlist(mcp); 233725cf1a30Sjl 233825cf1a30Sjl /* initialize bank informations */ 233925cf1a30Sjl cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 234025cf1a30Sjl "mc-addr", (caddr_t)&macaddr, &len); 234125cf1a30Sjl if (cc != DDI_SUCCESS) { 234225cf1a30Sjl cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc); 234325cf1a30Sjl return (DDI_FAILURE); 234425cf1a30Sjl } 234525cf1a30Sjl 23460cc8ae86Sav cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 23470cc8ae86Sav "cs-status", (caddr_t)&cs_status, &len1); 234825cf1a30Sjl 23490cc8ae86Sav if (cc != DDI_SUCCESS) { 23500cc8ae86Sav if (len > 0) 23510cc8ae86Sav kmem_free(macaddr, len); 23520cc8ae86Sav cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc); 23530cc8ae86Sav return (DDI_FAILURE); 23540cc8ae86Sav } 2355*aeb241b2Sav /* get the physical board number for a given logical board number */ 2356*aeb241b2Sav mcp->mc_phys_board_num = mc_opl_get_physical_board(mcp->mc_board_num); 2357*aeb241b2Sav 2358*aeb241b2Sav if (mcp->mc_phys_board_num < 0) { 2359*aeb241b2Sav if (len > 0) 2360*aeb241b2Sav kmem_free(macaddr, len); 2361*aeb241b2Sav cmn_err(CE_WARN, "Unable to obtain the physical board number"); 2362*aeb241b2Sav return (DDI_FAILURE); 2363*aeb241b2Sav } 236425cf1a30Sjl 23650cc8ae86Sav mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL); 23660cc8ae86Sav 23670cc8ae86Sav for (i = 0; i < len1 / sizeof (cs_status_t); i++) { 23680cc8ae86Sav nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) | 23690cc8ae86Sav ((uint64_t)cs_status[i].cs_avail_low); 23700cc8ae86Sav } 23710cc8ae86Sav if (len1 > 0) 23720cc8ae86Sav kmem_free(cs_status, len1); 23730cc8ae86Sav nbanks = len / sizeof (struct mc_addr_spec); 23740cc8ae86Sav 23750cc8ae86Sav if (nbanks > 0) 23760cc8ae86Sav nbytes /= nbanks; 23770cc8ae86Sav else { 23780cc8ae86Sav /* No need to free macaddr because len must be 0 */ 23790cc8ae86Sav mcp->mc_status |= MC_MEMORYLESS; 23800cc8ae86Sav return (DDI_SUCCESS); 23810cc8ae86Sav } 23820cc8ae86Sav 23830cc8ae86Sav for (i = 0; i < BANKNUM_PER_SB; i++) { 23840cc8ae86Sav mcp->mc_scf_retry[i] = 0; 23850cc8ae86Sav mcp->mc_period[i] = 0; 23860cc8ae86Sav mcp->mc_speedup_period[i] = 0; 23870cc8ae86Sav } 23880cc8ae86Sav 23890cc8ae86Sav /* 23900cc8ae86Sav * Get the memory size here. Let it be B (bytes). 23910cc8ae86Sav * Let T be the time in u.s. to scan 64 bytes. 23920cc8ae86Sav * If we want to complete 1 round of scanning in P seconds. 23930cc8ae86Sav * 23940cc8ae86Sav * B * T * 10^(-6) = P 23950cc8ae86Sav * --------------- 23960cc8ae86Sav * 64 23970cc8ae86Sav * 23980cc8ae86Sav * T = P * 64 * 10^6 23990cc8ae86Sav * ------------- 24000cc8ae86Sav * B 24010cc8ae86Sav * 24020cc8ae86Sav * = P * 64 * 10^6 24030cc8ae86Sav * ------------- 24040cc8ae86Sav * B 24050cc8ae86Sav * 24060cc8ae86Sav * The timing bits are set in PTRL_CNTL[28:26] where 24070cc8ae86Sav * 24080cc8ae86Sav * 0 - 1 m.s 24090cc8ae86Sav * 1 - 512 u.s. 24100cc8ae86Sav * 10 - 256 u.s. 24110cc8ae86Sav * 11 - 128 u.s. 24120cc8ae86Sav * 100 - 64 u.s. 24130cc8ae86Sav * 101 - 32 u.s. 24140cc8ae86Sav * 110 - 0 u.s. 24150cc8ae86Sav * 111 - reserved. 24160cc8ae86Sav * 24170cc8ae86Sav * 24180cc8ae86Sav * a[0] = 110, a[1] = 101, ... a[6] = 0 24190cc8ae86Sav * 24200cc8ae86Sav * cs-status property is int x 7 24210cc8ae86Sav * 0 - cs# 24220cc8ae86Sav * 1 - cs-status 24230cc8ae86Sav * 2 - cs-avail.hi 24240cc8ae86Sav * 3 - cs-avail.lo 24250cc8ae86Sav * 4 - dimm-capa.hi 24260cc8ae86Sav * 5 - dimm-capa.lo 24270cc8ae86Sav * 6 - #of dimms 24280cc8ae86Sav */ 24290cc8ae86Sav 24300cc8ae86Sav if (nbytes > 0) { 24310cc8ae86Sav int i; 24320cc8ae86Sav uint64_t ms; 24330cc8ae86Sav ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes; 24340cc8ae86Sav mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds; 24350cc8ae86Sav for (i = 0; i < MC_MAX_SPEEDS - 1; i++) { 24360cc8ae86Sav if (ms < mc_scan_speeds[i + 1].mc_period) { 24370cc8ae86Sav mcp->mc_speed = mc_scan_speeds[i].mc_speeds; 24380cc8ae86Sav break; 24390cc8ae86Sav } 24400cc8ae86Sav } 24410cc8ae86Sav } else 24420cc8ae86Sav mcp->mc_speed = 0; 24430cc8ae86Sav 24440cc8ae86Sav 24450cc8ae86Sav for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) { 24460cc8ae86Sav struct mc_bank *bankp; 24470cc8ae86Sav uint32_t reg; 24480cc8ae86Sav 24490cc8ae86Sav /* 24500cc8ae86Sav * setup bank 24510cc8ae86Sav */ 24520cc8ae86Sav bk = macaddr[i].bank; 24530cc8ae86Sav bankp = &(mcp->mc_bank[bk]); 24540cc8ae86Sav bankp->mcb_status = BANK_INSTALLED; 24550cc8ae86Sav bankp->mcb_reg_base = REGS_PA(macaddr, i); 24560cc8ae86Sav 24570cc8ae86Sav reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk)); 24580cc8ae86Sav bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS); 245925cf1a30Sjl 246025cf1a30Sjl /* 246125cf1a30Sjl * check if mirror mode 246225cf1a30Sjl */ 246325cf1a30Sjl mirr = LD_MAC_REG(MAC_MIRR(mcp, bk)); 246425cf1a30Sjl 246525cf1a30Sjl if (mirr & MAC_MIRR_MIRROR_MODE) { 246625cf1a30Sjl MC_LOG("Mirror -> /LSB%d/B%d\n", 246725cf1a30Sjl mcp->mc_board_num, bk); 246825cf1a30Sjl bankp->mcb_status |= BANK_MIRROR_MODE; 246925cf1a30Sjl /* 247025cf1a30Sjl * The following bit is only used for 247125cf1a30Sjl * error injection. We should clear it 247225cf1a30Sjl */ 247325cf1a30Sjl if (mirr & MAC_MIRR_BANK_EXCLUSIVE) 247425cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bk), 247525cf1a30Sjl 0); 247625cf1a30Sjl } 247725cf1a30Sjl 247825cf1a30Sjl /* 247925cf1a30Sjl * restart if not mirror mode or the other bank 248025cf1a30Sjl * of the mirror is not running 248125cf1a30Sjl */ 248225cf1a30Sjl if (!(mirr & MAC_MIRR_MIRROR_MODE) || 248325cf1a30Sjl !(mcp->mc_bank[bk^1].mcb_status & 248425cf1a30Sjl BANK_PTRL_RUNNING)) { 248525cf1a30Sjl MC_LOG("Starting up /LSB%d/B%d\n", 248625cf1a30Sjl mcp->mc_board_num, bk); 2487738dd194Shyw get_ptrl_start_address(mcp, bk, &rsaddr.mi_restartaddr); 2488738dd194Shyw rsaddr.mi_valid = 0; 2489738dd194Shyw rsaddr.mi_injectrestart = 0; 2490738dd194Shyw restart_patrol(mcp, bk, &rsaddr); 249125cf1a30Sjl } else { 249225cf1a30Sjl MC_LOG("Not starting up /LSB%d/B%d\n", 249325cf1a30Sjl mcp->mc_board_num, bk); 249425cf1a30Sjl } 249525cf1a30Sjl bankp->mcb_status |= BANK_PTRL_RUNNING; 249625cf1a30Sjl } 24970cc8ae86Sav if (len > 0) 24980cc8ae86Sav kmem_free(macaddr, len); 24990cc8ae86Sav 25000cc8ae86Sav mcp->mc_dimm_list = mc_get_dimm_list(mcp); 250125cf1a30Sjl 250225cf1a30Sjl /* 250325cf1a30Sjl * set interval in HZ. 250425cf1a30Sjl */ 250525cf1a30Sjl mcp->mc_last_error = 0; 250625cf1a30Sjl 250725cf1a30Sjl /* restart memory patrol checking */ 250825cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 250925cf1a30Sjl 251025cf1a30Sjl return (DDI_SUCCESS); 251125cf1a30Sjl } 251225cf1a30Sjl 251325cf1a30Sjl int 251425cf1a30Sjl mc_board_del(mc_opl_t *mcp) 251525cf1a30Sjl { 251625cf1a30Sjl int i; 251725cf1a30Sjl scf_log_t *p; 251825cf1a30Sjl 251925cf1a30Sjl /* 252025cf1a30Sjl * cleanup mac state 252125cf1a30Sjl */ 252225cf1a30Sjl mutex_enter(&mcp->mc_lock); 25230cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 25240cc8ae86Sav mutex_exit(&mcp->mc_lock); 25250cc8ae86Sav mutex_destroy(&mcp->mc_lock); 25260cc8ae86Sav return (DDI_SUCCESS); 25270cc8ae86Sav } 252825cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 252925cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 253025cf1a30Sjl mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED; 253125cf1a30Sjl } 253225cf1a30Sjl } 253325cf1a30Sjl 253425cf1a30Sjl /* stop memory patrol checking */ 2535738dd194Shyw mcp->mc_status &= ~MC_POLL_RUNNING; 253625cf1a30Sjl 253725cf1a30Sjl /* just throw away all the scf logs */ 25380cc8ae86Sav for (i = 0; i < BANKNUM_PER_SB; i++) { 25390cc8ae86Sav while ((p = mcp->mc_scf_log[i]) != NULL) { 25400cc8ae86Sav mcp->mc_scf_log[i] = p->sl_next; 25410cc8ae86Sav mcp->mc_scf_total[i]--; 254225cf1a30Sjl kmem_free(p, sizeof (scf_log_t)); 25430cc8ae86Sav } 254425cf1a30Sjl } 254525cf1a30Sjl 254625cf1a30Sjl if (mcp->mlist) 254725cf1a30Sjl mc_memlist_delete(mcp->mlist); 254825cf1a30Sjl 25490cc8ae86Sav if (mcp->mc_dimm_list) 25500cc8ae86Sav mc_free_dimm_list(mcp->mc_dimm_list); 25510cc8ae86Sav 255225cf1a30Sjl mutex_exit(&mcp->mc_lock); 255325cf1a30Sjl 255425cf1a30Sjl mutex_destroy(&mcp->mc_lock); 255525cf1a30Sjl return (DDI_SUCCESS); 255625cf1a30Sjl } 255725cf1a30Sjl 255825cf1a30Sjl int 255925cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag) 256025cf1a30Sjl { 256125cf1a30Sjl /* stop memory patrol checking */ 256225cf1a30Sjl mutex_enter(&mcp->mc_lock); 25630cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 25640cc8ae86Sav mutex_exit(&mcp->mc_lock); 25650cc8ae86Sav return (DDI_SUCCESS); 25660cc8ae86Sav } 25670cc8ae86Sav 2568738dd194Shyw mcp->mc_status &= ~MC_POLL_RUNNING; 2569738dd194Shyw 257025cf1a30Sjl mcp->mc_status |= flag; 257125cf1a30Sjl mutex_exit(&mcp->mc_lock); 257225cf1a30Sjl 257325cf1a30Sjl return (DDI_SUCCESS); 257425cf1a30Sjl } 257525cf1a30Sjl 257625cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */ 257725cf1a30Sjl 257825cf1a30Sjl int 257925cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag) 258025cf1a30Sjl { 258125cf1a30Sjl int i; 258225cf1a30Sjl uint64_t basepa; 258325cf1a30Sjl 258425cf1a30Sjl mutex_enter(&mcp->mc_lock); 25850cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 25860cc8ae86Sav mutex_exit(&mcp->mc_lock); 25870cc8ae86Sav return (DDI_SUCCESS); 25880cc8ae86Sav } 258925cf1a30Sjl basepa = mcp->mc_start_address; 259025cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 259125cf1a30Sjl mutex_exit(&mcp->mc_lock); 259225cf1a30Sjl return (DDI_FAILURE); 259325cf1a30Sjl } 259425cf1a30Sjl 259525cf1a30Sjl if (basepa != mcp->mc_start_address) { 259625cf1a30Sjl if (mcp->mlist) 259725cf1a30Sjl mc_memlist_delete(mcp->mlist); 259825cf1a30Sjl mcp->mlist = NULL; 259925cf1a30Sjl mc_get_mlist(mcp); 260025cf1a30Sjl } 260125cf1a30Sjl 260225cf1a30Sjl mcp->mc_status &= ~flag; 260325cf1a30Sjl 260425cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 260525cf1a30Sjl mutex_exit(&mcp->mc_lock); 260625cf1a30Sjl return (DDI_SUCCESS); 260725cf1a30Sjl } 260825cf1a30Sjl 260925cf1a30Sjl if (!(mcp->mc_status & MC_POLL_RUNNING)) { 261025cf1a30Sjl /* restart memory patrol checking */ 261125cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 261225cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 261325cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 261425cf1a30Sjl restart_patrol(mcp, i, NULL); 261525cf1a30Sjl } 261625cf1a30Sjl } 261725cf1a30Sjl } 261825cf1a30Sjl mutex_exit(&mcp->mc_lock); 261925cf1a30Sjl 262025cf1a30Sjl return (DDI_SUCCESS); 262125cf1a30Sjl } 262225cf1a30Sjl 262325cf1a30Sjl static mc_opl_t * 262425cf1a30Sjl mc_pa_to_mcp(uint64_t pa) 262525cf1a30Sjl { 26260cc8ae86Sav mc_opl_t *mcp; 26270cc8ae86Sav int i; 26280cc8ae86Sav 262925cf1a30Sjl ASSERT(MUTEX_HELD(&mcmutex)); 26300cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 26310cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 26320cc8ae86Sav continue; 263325cf1a30Sjl /* if mac patrol is suspended, we cannot rely on it */ 26340cc8ae86Sav if (!(mcp->mc_status & MC_POLL_RUNNING) || 26350cc8ae86Sav (mcp->mc_status & MC_SOFT_SUSPENDED)) 263625cf1a30Sjl continue; 2637738dd194Shyw if (mc_rangecheck_pa(mcp, pa)) { 26380cc8ae86Sav return (mcp); 263925cf1a30Sjl } 264025cf1a30Sjl } 264125cf1a30Sjl return (NULL); 264225cf1a30Sjl } 264325cf1a30Sjl 264425cf1a30Sjl /* 264525cf1a30Sjl * Get Physical Board number from Logical one. 264625cf1a30Sjl */ 264725cf1a30Sjl static int 264825cf1a30Sjl mc_opl_get_physical_board(int sb) 264925cf1a30Sjl { 265025cf1a30Sjl if (&opl_get_physical_board) { 265125cf1a30Sjl return (opl_get_physical_board(sb)); 265225cf1a30Sjl } 265325cf1a30Sjl 265425cf1a30Sjl cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n"); 265525cf1a30Sjl return (-1); 265625cf1a30Sjl } 265725cf1a30Sjl 265825cf1a30Sjl /* ARGSUSED */ 265925cf1a30Sjl int 266025cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen, 266125cf1a30Sjl int *lenp) 266225cf1a30Sjl { 26630cc8ae86Sav int i; 2664*aeb241b2Sav int j; 266525cf1a30Sjl int sb; 26660cc8ae86Sav int bank; 2667*aeb241b2Sav int cs; 26680cc8ae86Sav mc_opl_t *mcp; 26690cc8ae86Sav char memb_num; 267025cf1a30Sjl 267125cf1a30Sjl mutex_enter(&mcmutex); 267225cf1a30Sjl 267325cf1a30Sjl if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) || 267425cf1a30Sjl (!pa_is_valid(mcp, flt_addr))) { 267525cf1a30Sjl mutex_exit(&mcmutex); 267625cf1a30Sjl if (snprintf(buf, buflen, "UNKNOWN") >= buflen) { 267725cf1a30Sjl return (ENOSPC); 267825cf1a30Sjl } else { 267925cf1a30Sjl if (lenp) 268025cf1a30Sjl *lenp = strlen(buf); 268125cf1a30Sjl } 268225cf1a30Sjl return (0); 268325cf1a30Sjl } 268425cf1a30Sjl 268525cf1a30Sjl bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address); 2686*aeb241b2Sav sb = mcp->mc_phys_board_num; 2687*aeb241b2Sav cs = pa_to_cs(mcp, flt_addr - mcp->mc_start_address); 268825cf1a30Sjl 268925cf1a30Sjl if (sb == -1) { 269025cf1a30Sjl mutex_exit(&mcmutex); 269125cf1a30Sjl return (ENXIO); 269225cf1a30Sjl } 269325cf1a30Sjl 26940cc8ae86Sav if (plat_model == MODEL_DC) { 26950cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 2696*aeb241b2Sav j = (cs == 0) ? i : i + 2; 2697*aeb241b2Sav snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s", 26980cc8ae86Sav model_names[plat_model].unit_name, sb, 2699*aeb241b2Sav mc_dc_dimm_unum_table[j], 2700*aeb241b2Sav mc_dc_dimm_unum_table[j + 1]); 270125cf1a30Sjl } else { 27020cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 2703*aeb241b2Sav j = (cs == 0) ? i : i + 2; 27040cc8ae86Sav memb_num = mc_ff_dimm_unum_table[i][0]; 2705*aeb241b2Sav snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s", 27060cc8ae86Sav model_names[plat_model].unit_name, 27070cc8ae86Sav model_names[plat_model].mem_name, memb_num, 2708*aeb241b2Sav &mc_ff_dimm_unum_table[j][1], 2709*aeb241b2Sav &mc_ff_dimm_unum_table[j + 1][1]); 27100cc8ae86Sav } 27110cc8ae86Sav if (lenp) { 27120cc8ae86Sav *lenp = strlen(buf); 271325cf1a30Sjl } 271425cf1a30Sjl mutex_exit(&mcmutex); 271525cf1a30Sjl return (0); 271625cf1a30Sjl } 271725cf1a30Sjl 271825cf1a30Sjl int 27190cc8ae86Sav opl_mc_suspend(void) 272025cf1a30Sjl { 272125cf1a30Sjl mc_opl_t *mcp; 27220cc8ae86Sav int i; 272325cf1a30Sjl 272425cf1a30Sjl mutex_enter(&mcmutex); 27250cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 27260cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 27270cc8ae86Sav continue; 27280cc8ae86Sav mc_suspend(mcp, MC_SOFT_SUSPENDED); 272925cf1a30Sjl } 273025cf1a30Sjl mutex_exit(&mcmutex); 27310cc8ae86Sav 273225cf1a30Sjl return (0); 273325cf1a30Sjl } 273425cf1a30Sjl 273525cf1a30Sjl int 27360cc8ae86Sav opl_mc_resume(void) 273725cf1a30Sjl { 273825cf1a30Sjl mc_opl_t *mcp; 27390cc8ae86Sav int i; 274025cf1a30Sjl 274125cf1a30Sjl mutex_enter(&mcmutex); 27420cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 27430cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 27440cc8ae86Sav continue; 27450cc8ae86Sav mc_resume(mcp, MC_SOFT_SUSPENDED); 274625cf1a30Sjl } 274725cf1a30Sjl mutex_exit(&mcmutex); 27480cc8ae86Sav 274925cf1a30Sjl return (0); 275025cf1a30Sjl } 275125cf1a30Sjl static void 275225cf1a30Sjl insert_mcp(mc_opl_t *mcp) 275325cf1a30Sjl { 275425cf1a30Sjl mutex_enter(&mcmutex); 27550cc8ae86Sav if (mc_instances[mcp->mc_board_num] != NULL) { 27560cc8ae86Sav MC_LOG("mc-opl instance for board# %d already exists\n", 27570cc8ae86Sav mcp->mc_board_num); 27580cc8ae86Sav } 27590cc8ae86Sav mc_instances[mcp->mc_board_num] = mcp; 276025cf1a30Sjl mutex_exit(&mcmutex); 276125cf1a30Sjl } 276225cf1a30Sjl 276325cf1a30Sjl static void 276425cf1a30Sjl delete_mcp(mc_opl_t *mcp) 276525cf1a30Sjl { 27660cc8ae86Sav mutex_enter(&mcmutex); 27670cc8ae86Sav mc_instances[mcp->mc_board_num] = 0; 27680cc8ae86Sav mutex_exit(&mcmutex); 276925cf1a30Sjl } 277025cf1a30Sjl 277125cf1a30Sjl /* Error injection interface */ 277225cf1a30Sjl 2773cfb9e062Shyw static void 2774cfb9e062Shyw mc_lock_va(uint64_t pa, caddr_t new_va) 2775cfb9e062Shyw { 2776cfb9e062Shyw tte_t tte; 2777cfb9e062Shyw 2778738dd194Shyw vtag_flushpage(new_va, (uint64_t)ksfmmup); 2779cfb9e062Shyw sfmmu_memtte(&tte, pa >> PAGESHIFT, 2780cfb9e062Shyw PROC_DATA|HAT_NOSYNC, TTE8K); 2781cfb9e062Shyw tte.tte_intlo |= TTE_LCK_INT; 2782cfb9e062Shyw sfmmu_dtlb_ld_kva(new_va, &tte); 2783cfb9e062Shyw } 2784cfb9e062Shyw 2785cfb9e062Shyw static void 2786cfb9e062Shyw mc_unlock_va(caddr_t va) 2787cfb9e062Shyw { 2788cfb9e062Shyw vtag_flushpage(va, (uint64_t)ksfmmup); 2789cfb9e062Shyw } 2790cfb9e062Shyw 279125cf1a30Sjl /* ARGSUSED */ 279225cf1a30Sjl int 279325cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags) 279425cf1a30Sjl { 279525cf1a30Sjl mc_opl_t *mcp; 279625cf1a30Sjl int bank; 279725cf1a30Sjl uint32_t dimm_addr; 279825cf1a30Sjl uint32_t cntl; 2799738dd194Shyw mc_rsaddr_info_t rsaddr; 280025cf1a30Sjl uint32_t data, stat; 280125cf1a30Sjl int both_sides = 0; 280225cf1a30Sjl uint64_t pa0; 2803cfb9e062Shyw int extra_injection_needed = 0; 280425cf1a30Sjl extern void cpu_flush_ecache(void); 280525cf1a30Sjl 280625cf1a30Sjl MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags); 280725cf1a30Sjl 280825cf1a30Sjl mutex_enter(&mcmutex); 280925cf1a30Sjl if ((mcp = mc_pa_to_mcp(pa)) == NULL) { 281025cf1a30Sjl mutex_exit(&mcmutex); 281125cf1a30Sjl MC_LOG("mc_inject_error: invalid pa\n"); 281225cf1a30Sjl return (ENOTSUP); 281325cf1a30Sjl } 281425cf1a30Sjl 281525cf1a30Sjl mutex_enter(&mcp->mc_lock); 281625cf1a30Sjl mutex_exit(&mcmutex); 281725cf1a30Sjl 281825cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 281925cf1a30Sjl mutex_exit(&mcp->mc_lock); 282025cf1a30Sjl MC_LOG("mc-opl has been suspended. No error injection.\n"); 282125cf1a30Sjl return (EBUSY); 282225cf1a30Sjl } 282325cf1a30Sjl 282425cf1a30Sjl /* convert pa to offset within the board */ 282525cf1a30Sjl MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address); 282625cf1a30Sjl 282725cf1a30Sjl if (!pa_is_valid(mcp, pa)) { 282825cf1a30Sjl mutex_exit(&mcp->mc_lock); 282925cf1a30Sjl return (EINVAL); 283025cf1a30Sjl } 283125cf1a30Sjl 283225cf1a30Sjl pa0 = pa - mcp->mc_start_address; 283325cf1a30Sjl 283425cf1a30Sjl bank = pa_to_bank(mcp, pa0); 283525cf1a30Sjl 283625cf1a30Sjl if (flags & MC_INJECT_FLAG_OTHER) 283725cf1a30Sjl bank = bank ^ 1; 283825cf1a30Sjl 283925cf1a30Sjl if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) { 284025cf1a30Sjl mutex_exit(&mcp->mc_lock); 284125cf1a30Sjl MC_LOG("Not mirror mode\n"); 284225cf1a30Sjl return (EINVAL); 284325cf1a30Sjl } 284425cf1a30Sjl 284525cf1a30Sjl dimm_addr = pa_to_dimm(mcp, pa0); 284625cf1a30Sjl 2847cfb9e062Shyw MC_LOG("injecting error to /LSB%d/B%d/%x\n", 284825cf1a30Sjl mcp->mc_board_num, bank, dimm_addr); 284925cf1a30Sjl 285025cf1a30Sjl 285125cf1a30Sjl switch (error_type) { 285225cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 285325cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 285425cf1a30Sjl case MC_INJECT_MUE: 285525cf1a30Sjl both_sides = 1; 285625cf1a30Sjl } 285725cf1a30Sjl 285825cf1a30Sjl if (flags & MC_INJECT_FLAG_RESET) 285925cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0); 286025cf1a30Sjl 286125cf1a30Sjl ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK); 286225cf1a30Sjl 286325cf1a30Sjl if (both_sides) { 286425cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0); 286525cf1a30Sjl ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), 286625cf1a30Sjl dimm_addr & MAC_EG_ADD_MASK); 286725cf1a30Sjl } 286825cf1a30Sjl 286925cf1a30Sjl switch (error_type) { 287025cf1a30Sjl case MC_INJECT_SUE: 2871cfb9e062Shyw extra_injection_needed = 1; 2872cfb9e062Shyw /*FALLTHROUGH*/ 2873cfb9e062Shyw case MC_INJECT_UE: 287425cf1a30Sjl case MC_INJECT_MUE: 287525cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 287625cf1a30Sjl cntl = MAC_EG_ADD_FIX 287725cf1a30Sjl |MAC_EG_FORCE_READ00|MAC_EG_FORCE_READ16 28780cc8ae86Sav |MAC_EG_RDERR_ONCE; 287925cf1a30Sjl } else { 288025cf1a30Sjl cntl = MAC_EG_ADD_FIX|MAC_EG_FORCE_DERR00 288125cf1a30Sjl |MAC_EG_FORCE_DERR16|MAC_EG_DERR_ONCE; 288225cf1a30Sjl } 288325cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 288425cf1a30Sjl break; 288525cf1a30Sjl case MC_INJECT_INTERMITTENT_CE: 288625cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 288725cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 288825cf1a30Sjl cntl = MAC_EG_ADD_FIX 288925cf1a30Sjl |MAC_EG_FORCE_READ00 28900cc8ae86Sav |MAC_EG_RDERR_ONCE; 289125cf1a30Sjl } else { 289225cf1a30Sjl cntl = MAC_EG_ADD_FIX 289325cf1a30Sjl |MAC_EG_FORCE_DERR16 289425cf1a30Sjl |MAC_EG_DERR_ONCE; 289525cf1a30Sjl } 2896cfb9e062Shyw extra_injection_needed = 1; 289725cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 289825cf1a30Sjl break; 289925cf1a30Sjl case MC_INJECT_PERMANENT_CE: 290025cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 290125cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 290225cf1a30Sjl cntl = MAC_EG_ADD_FIX 290325cf1a30Sjl |MAC_EG_FORCE_READ00 29040cc8ae86Sav |MAC_EG_RDERR_ALWAYS; 290525cf1a30Sjl } else { 290625cf1a30Sjl cntl = MAC_EG_ADD_FIX 290725cf1a30Sjl |MAC_EG_FORCE_DERR16 290825cf1a30Sjl |MAC_EG_DERR_ALWAYS; 290925cf1a30Sjl } 291025cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 291125cf1a30Sjl break; 291225cf1a30Sjl case MC_INJECT_CMPE: 291325cf1a30Sjl data = 0xabcdefab; 291425cf1a30Sjl stphys(pa, data); 291525cf1a30Sjl cpu_flush_ecache(); 291625cf1a30Sjl MC_LOG("CMPE: writing data %x to %lx\n", data, pa); 291725cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE); 291825cf1a30Sjl stphys(pa, data ^ 0xffffffff); 2919738dd194Shyw membar_sync(); 292025cf1a30Sjl cpu_flush_ecache(); 292125cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), 0); 292225cf1a30Sjl MC_LOG("CMPE: write new data %xto %lx\n", data, pa); 292325cf1a30Sjl cntl = 0; 292425cf1a30Sjl break; 292525cf1a30Sjl case MC_INJECT_NOP: 292625cf1a30Sjl cntl = 0; 292725cf1a30Sjl break; 292825cf1a30Sjl default: 292925cf1a30Sjl MC_LOG("mc_inject_error: invalid option\n"); 293025cf1a30Sjl cntl = 0; 293125cf1a30Sjl } 293225cf1a30Sjl 293325cf1a30Sjl if (cntl) { 293425cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK); 293525cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 293625cf1a30Sjl 293725cf1a30Sjl if (both_sides) { 293825cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 293925cf1a30Sjl MAC_EG_SETUP_MASK); 294025cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 294125cf1a30Sjl } 294225cf1a30Sjl } 294325cf1a30Sjl 294425cf1a30Sjl /* 294525cf1a30Sjl * For all injection cases except compare error, we 294625cf1a30Sjl * must write to the PA to trigger the error. 294725cf1a30Sjl */ 294825cf1a30Sjl 294925cf1a30Sjl if (flags & MC_INJECT_FLAG_ST) { 295025cf1a30Sjl data = 0xf0e0d0c0; 295125cf1a30Sjl MC_LOG("Writing %x to %lx\n", data, pa); 295225cf1a30Sjl stphys(pa, data); 295325cf1a30Sjl cpu_flush_ecache(); 295425cf1a30Sjl } 295525cf1a30Sjl 295625cf1a30Sjl 295725cf1a30Sjl if (flags & MC_INJECT_FLAG_LD) { 2958cfb9e062Shyw if (flags & MC_INJECT_FLAG_PREFETCH) { 2959cfb9e062Shyw /* 2960cfb9e062Shyw * Use strong prefetch operation to 2961cfb9e062Shyw * inject MI errors. 2962cfb9e062Shyw */ 2963cfb9e062Shyw page_t *pp; 2964cfb9e062Shyw extern void mc_prefetch(caddr_t); 2965cfb9e062Shyw 2966cfb9e062Shyw MC_LOG("prefetch\n"); 2967cfb9e062Shyw 2968cfb9e062Shyw pp = page_numtopp_nolock(pa >> PAGESHIFT); 2969cfb9e062Shyw if (pp != NULL) { 2970cfb9e062Shyw caddr_t va, va1; 2971cfb9e062Shyw 2972cfb9e062Shyw va = ppmapin(pp, PROT_READ|PROT_WRITE, 2973cfb9e062Shyw (caddr_t)-1); 2974cfb9e062Shyw kpreempt_disable(); 2975cfb9e062Shyw mc_lock_va((uint64_t)pa, va); 2976cfb9e062Shyw va1 = va + (pa & (PAGESIZE - 1)); 2977cfb9e062Shyw mc_prefetch(va1); 2978cfb9e062Shyw mc_unlock_va(va); 2979cfb9e062Shyw kpreempt_enable(); 2980cfb9e062Shyw ppmapout(va); 2981cfb9e062Shyw 2982cfb9e062Shyw /* 2983cfb9e062Shyw * For MI errors, we need one extra 2984cfb9e062Shyw * injection for HW patrol to stop. 2985cfb9e062Shyw */ 2986cfb9e062Shyw extra_injection_needed = 1; 298725cf1a30Sjl } else { 2988cfb9e062Shyw cmn_err(CE_WARN, "Cannot find page structure" 2989cfb9e062Shyw " for PA %lx\n", pa); 299025cf1a30Sjl } 299125cf1a30Sjl } else { 299225cf1a30Sjl MC_LOG("Reading from %lx\n", pa); 299325cf1a30Sjl data = ldphys(pa); 299425cf1a30Sjl MC_LOG("data = %x\n", data); 299525cf1a30Sjl } 2996cfb9e062Shyw 2997cfb9e062Shyw if (extra_injection_needed) { 2998cfb9e062Shyw /* 2999cfb9e062Shyw * These are the injection cases where the 3000cfb9e062Shyw * requested injected errors will not cause the HW 3001cfb9e062Shyw * patrol to stop. For these cases, we need to inject 3002cfb9e062Shyw * an extra 'real' PTRL error to force the 3003cfb9e062Shyw * HW patrol to stop so that we can report the 3004cfb9e062Shyw * errors injected. Note that we cannot read 3005cfb9e062Shyw * and report error status while the HW patrol 3006cfb9e062Shyw * is running. 3007cfb9e062Shyw */ 3008cfb9e062Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 3009cfb9e062Shyw cntl & MAC_EG_SETUP_MASK); 3010cfb9e062Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 3011cfb9e062Shyw 3012cfb9e062Shyw if (both_sides) { 3013cfb9e062Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 3014cfb9e062Shyw MAC_EG_SETUP_MASK); 3015cfb9e062Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 3016cfb9e062Shyw } 3017cfb9e062Shyw data = 0xf0e0d0c0; 3018cfb9e062Shyw MC_LOG("Writing %x to %lx\n", data, pa); 3019cfb9e062Shyw stphys(pa, data); 3020cfb9e062Shyw cpu_flush_ecache(); 3021cfb9e062Shyw } 302225cf1a30Sjl } 302325cf1a30Sjl 302425cf1a30Sjl if (flags & MC_INJECT_FLAG_RESTART) { 302525cf1a30Sjl MC_LOG("Restart patrol\n"); 3026738dd194Shyw rsaddr.mi_restartaddr.ma_bd = mcp->mc_board_num; 3027738dd194Shyw rsaddr.mi_restartaddr.ma_bank = bank; 3028738dd194Shyw rsaddr.mi_restartaddr.ma_dimm_addr = dimm_addr; 3029738dd194Shyw rsaddr.mi_valid = 1; 3030738dd194Shyw rsaddr.mi_injectrestart = 1; 3031738dd194Shyw restart_patrol(mcp, bank, &rsaddr); 303225cf1a30Sjl } 303325cf1a30Sjl 303425cf1a30Sjl if (flags & MC_INJECT_FLAG_POLL) { 30350cc8ae86Sav int running; 303637afe445Shyw int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank; 303725cf1a30Sjl 303825cf1a30Sjl MC_LOG("Poll patrol error\n"); 303925cf1a30Sjl stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank)); 304025cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 30410cc8ae86Sav running = cntl & MAC_CNTL_PTRL_START; 304225cf1a30Sjl 3043cfb9e062Shyw if (!running && 3044cfb9e062Shyw (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS))) { 3045cfb9e062Shyw /* 3046cfb9e062Shyw * HW patrol stopped and we have errors to 3047cfb9e062Shyw * report. Do it. 3048cfb9e062Shyw */ 304937afe445Shyw mcp->mc_speedup_period[ebank] = 0; 3050738dd194Shyw rsaddr.mi_valid = 0; 3051738dd194Shyw rsaddr.mi_injectrestart = 0; 3052738dd194Shyw if (IS_MIRROR(mcp, bank)) { 3053738dd194Shyw mc_error_handler_mir(mcp, bank, &rsaddr); 3054738dd194Shyw } else { 3055738dd194Shyw mc_error_handler(mcp, bank, &rsaddr); 3056738dd194Shyw } 3057cfb9e062Shyw 3058738dd194Shyw restart_patrol(mcp, bank, &rsaddr); 3059cfb9e062Shyw } else { 3060cfb9e062Shyw /* 3061cfb9e062Shyw * We are expecting to report injected 3062cfb9e062Shyw * errors but the HW patrol is still running. 3063cfb9e062Shyw * Speed up the scanning 3064cfb9e062Shyw */ 306537afe445Shyw mcp->mc_speedup_period[ebank] = 2; 3066cfb9e062Shyw MAC_CMD(mcp, bank, 0); 306725cf1a30Sjl restart_patrol(mcp, bank, NULL); 3068cfb9e062Shyw } 306925cf1a30Sjl } 307025cf1a30Sjl 307125cf1a30Sjl mutex_exit(&mcp->mc_lock); 307225cf1a30Sjl return (0); 307325cf1a30Sjl } 3074cfb9e062Shyw 307525cf1a30Sjl void 307625cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data) 307725cf1a30Sjl { 307825cf1a30Sjl MC_LOG("0x%x -> pa(%lx)\n", data, pa); 307925cf1a30Sjl stphysio(pa, data); 30800cc8ae86Sav 30810cc8ae86Sav /* force the above write to be processed by mac patrol */ 3082cfb9e062Shyw data = ldphysio(pa); 3083cfb9e062Shyw MC_LOG("pa(%lx) = 0x%x\n", pa, data); 308425cf1a30Sjl } 308525cf1a30Sjl 308625cf1a30Sjl uint32_t 308725cf1a30Sjl mc_ldphysio(uint64_t pa) 308825cf1a30Sjl { 308925cf1a30Sjl uint32_t rv; 309025cf1a30Sjl 309125cf1a30Sjl rv = ldphysio(pa); 309225cf1a30Sjl MC_LOG("pa(%lx) = 0x%x\n", pa, rv); 309325cf1a30Sjl return (rv); 309425cf1a30Sjl } 30950cc8ae86Sav 30960cc8ae86Sav #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 30970cc8ae86Sav 30980cc8ae86Sav /* 30990cc8ae86Sav * parse_unum_memory -- extract the board number and the DIMM name from 31000cc8ae86Sav * the unum. 31010cc8ae86Sav * 31020cc8ae86Sav * Return 0 for success and non-zero for a failure. 31030cc8ae86Sav */ 31040cc8ae86Sav int 31050cc8ae86Sav parse_unum_memory(char *unum, int *board, char *dname) 31060cc8ae86Sav { 31070cc8ae86Sav char *c; 31080cc8ae86Sav char x, y, z; 31090cc8ae86Sav 31100cc8ae86Sav if ((c = strstr(unum, "CMU")) != NULL) { 31110cc8ae86Sav /* DC Model */ 31120cc8ae86Sav c += 3; 31130cc8ae86Sav *board = (uint8_t)stoi(&c); 31140cc8ae86Sav if ((c = strstr(c, "MEM")) == NULL) { 31150cc8ae86Sav return (1); 31160cc8ae86Sav } 31170cc8ae86Sav c += 3; 31180cc8ae86Sav if (strlen(c) < 3) { 31190cc8ae86Sav return (2); 31200cc8ae86Sav } 31210cc8ae86Sav if ((!isdigit(c[0])) || (!(isdigit(c[1]))) || 31220cc8ae86Sav ((c[2] != 'A') && (c[2] != 'B'))) { 31230cc8ae86Sav return (3); 31240cc8ae86Sav } 31250cc8ae86Sav x = c[0]; 31260cc8ae86Sav y = c[1]; 31270cc8ae86Sav z = c[2]; 31280cc8ae86Sav } else if ((c = strstr(unum, "MBU_")) != NULL) { 31290cc8ae86Sav /* FF1/FF2 Model */ 31300cc8ae86Sav c += 4; 31310cc8ae86Sav if ((c[0] != 'A') && (c[0] != 'B')) { 31320cc8ae86Sav return (4); 31330cc8ae86Sav } 31340cc8ae86Sav if ((c = strstr(c, "MEMB")) == NULL) { 31350cc8ae86Sav return (5); 31360cc8ae86Sav } 31370cc8ae86Sav c += 4; 31380cc8ae86Sav 31390cc8ae86Sav x = c[0]; 31400cc8ae86Sav *board = ((uint8_t)stoi(&c)) / 4; 31410cc8ae86Sav if ((c = strstr(c, "MEM")) == NULL) { 31420cc8ae86Sav return (6); 31430cc8ae86Sav } 31440cc8ae86Sav c += 3; 31450cc8ae86Sav if (strlen(c) < 2) { 31460cc8ae86Sav return (7); 31470cc8ae86Sav } 31480cc8ae86Sav if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) { 31490cc8ae86Sav return (8); 31500cc8ae86Sav } 31510cc8ae86Sav y = c[0]; 31520cc8ae86Sav z = c[1]; 31530cc8ae86Sav } else { 31540cc8ae86Sav return (9); 31550cc8ae86Sav } 31560cc8ae86Sav if (*board < 0) { 31570cc8ae86Sav return (10); 31580cc8ae86Sav } 31590cc8ae86Sav dname[0] = x; 31600cc8ae86Sav dname[1] = y; 31610cc8ae86Sav dname[2] = z; 31620cc8ae86Sav dname[3] = '\0'; 31630cc8ae86Sav return (0); 31640cc8ae86Sav } 31650cc8ae86Sav 31660cc8ae86Sav /* 31670cc8ae86Sav * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and 31680cc8ae86Sav * the DIMM name. 31690cc8ae86Sav */ 31700cc8ae86Sav int 31710cc8ae86Sav mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 31720cc8ae86Sav int buflen, int *lenp) 31730cc8ae86Sav { 31740cc8ae86Sav int ret = ENODEV; 31750cc8ae86Sav mc_dimm_info_t *d = NULL; 31760cc8ae86Sav 31770cc8ae86Sav if ((d = mcp->mc_dimm_list) == NULL) 31780cc8ae86Sav return (ENOTSUP); 31790cc8ae86Sav 31800cc8ae86Sav for (; d != NULL; d = d->md_next) { 31810cc8ae86Sav if (strcmp(d->md_dimmname, dname) == 0) { 31820cc8ae86Sav break; 31830cc8ae86Sav } 31840cc8ae86Sav } 31850cc8ae86Sav if (d != NULL) { 31860cc8ae86Sav *lenp = strlen(d->md_serial) + strlen(d->md_partnum); 31870cc8ae86Sav if (buflen <= *lenp) { 31880cc8ae86Sav cmn_err(CE_WARN, "mc_get_mem_sid_dimm: " 31890cc8ae86Sav "buflen is smaller than %d\n", *lenp); 31900cc8ae86Sav ret = ENOSPC; 31910cc8ae86Sav } else { 31920cc8ae86Sav snprintf(buf, buflen, "%s:%s", 31930cc8ae86Sav d->md_serial, d->md_partnum); 31940cc8ae86Sav ret = 0; 31950cc8ae86Sav } 31960cc8ae86Sav } 31970cc8ae86Sav MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n", 31980cc8ae86Sav ret, dname, (ret == 0) ? buf : ""); 31990cc8ae86Sav return (ret); 32000cc8ae86Sav } 32010cc8ae86Sav 32020cc8ae86Sav int 3203*aeb241b2Sav mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int sb, 32040cc8ae86Sav int bank, uint32_t mf_type, uint32_t d_slot) 32050cc8ae86Sav { 32060cc8ae86Sav int lenp = buflen; 32070cc8ae86Sav int id; 32080cc8ae86Sav int ret; 32090cc8ae86Sav char *dimmnm; 32100cc8ae86Sav 32110cc8ae86Sav if (mf_type == FLT_TYPE_PERMANENT_CE) { 32120cc8ae86Sav if (plat_model == MODEL_DC) { 32130cc8ae86Sav id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 3214*aeb241b2Sav dimmnm = mc_dc_dimm_unum_table[id]; 32150cc8ae86Sav } else { 32160cc8ae86Sav id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 3217*aeb241b2Sav dimmnm = mc_ff_dimm_unum_table[id]; 32180cc8ae86Sav } 32190cc8ae86Sav if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen, 32200cc8ae86Sav &lenp)) != 0) { 32210cc8ae86Sav return (ret); 32220cc8ae86Sav } 32230cc8ae86Sav } else { 32240cc8ae86Sav return (1); 32250cc8ae86Sav } 32260cc8ae86Sav 32270cc8ae86Sav return (0); 32280cc8ae86Sav } 32290cc8ae86Sav 32300cc8ae86Sav /* 32310cc8ae86Sav * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum. 32320cc8ae86Sav */ 32330cc8ae86Sav int 32340cc8ae86Sav mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp) 32350cc8ae86Sav { 32360cc8ae86Sav int i; 32370cc8ae86Sav int ret = ENODEV; 32380cc8ae86Sav int board; 32390cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 32400cc8ae86Sav mc_opl_t *mcp; 32410cc8ae86Sav 32420cc8ae86Sav MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen); 32430cc8ae86Sav if ((ret = parse_unum_memory(unum, &board, dname)) != 0) { 32440cc8ae86Sav MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 32450cc8ae86Sav unum, ret); 32460cc8ae86Sav return (EINVAL); 32470cc8ae86Sav } 32480cc8ae86Sav 32490cc8ae86Sav if (board < 0) { 32500cc8ae86Sav MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n", 32510cc8ae86Sav board, dname); 32520cc8ae86Sav return (EINVAL); 32530cc8ae86Sav } 32540cc8ae86Sav 32550cc8ae86Sav mutex_enter(&mcmutex); 32560cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 32570cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 32580cc8ae86Sav continue; 32590cc8ae86Sav mutex_enter(&mcp->mc_lock); 3260*aeb241b2Sav if (mcp->mc_phys_board_num != board) { 3261*aeb241b2Sav mutex_exit(&mcp->mc_lock); 3262*aeb241b2Sav continue; 3263*aeb241b2Sav } 3264*aeb241b2Sav ret = mc_get_mem_sid_dimm(mcp, dname, buf, buflen, lenp); 3265*aeb241b2Sav if (ret == 0) { 32660cc8ae86Sav mutex_exit(&mcp->mc_lock); 32670cc8ae86Sav break; 32680cc8ae86Sav } 32690cc8ae86Sav mutex_exit(&mcp->mc_lock); 32700cc8ae86Sav } 32710cc8ae86Sav mutex_exit(&mcmutex); 32720cc8ae86Sav return (ret); 32730cc8ae86Sav } 32740cc8ae86Sav 32750cc8ae86Sav /* 32760cc8ae86Sav * mc_get_mem_offset -- get the offset in a DIMM for a given physical address. 32770cc8ae86Sav */ 32780cc8ae86Sav int 32790cc8ae86Sav mc_get_mem_offset(uint64_t paddr, uint64_t *offp) 32800cc8ae86Sav { 32810cc8ae86Sav int i; 32820cc8ae86Sav int ret = ENODEV; 32830cc8ae86Sav mc_addr_t maddr; 32840cc8ae86Sav mc_opl_t *mcp; 32850cc8ae86Sav 32860cc8ae86Sav mutex_enter(&mcmutex); 3287c964b0e6Sraghuram for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) { 32880cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 32890cc8ae86Sav continue; 32900cc8ae86Sav mutex_enter(&mcp->mc_lock); 32910cc8ae86Sav if (!pa_is_valid(mcp, paddr)) { 32920cc8ae86Sav mutex_exit(&mcp->mc_lock); 32930cc8ae86Sav continue; 32940cc8ae86Sav } 32950cc8ae86Sav if (pa_to_maddr(mcp, paddr, &maddr) == 0) { 32960cc8ae86Sav *offp = maddr.ma_dimm_addr; 32970cc8ae86Sav ret = 0; 32980cc8ae86Sav } 32990cc8ae86Sav mutex_exit(&mcp->mc_lock); 33000cc8ae86Sav } 33010cc8ae86Sav mutex_exit(&mcmutex); 33020cc8ae86Sav MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n", 33030cc8ae86Sav ret, paddr, *offp); 33040cc8ae86Sav return (ret); 33050cc8ae86Sav } 33060cc8ae86Sav 33070cc8ae86Sav /* 33080cc8ae86Sav * dname_to_bankslot - Get the bank and slot number from the DIMM name. 33090cc8ae86Sav */ 33100cc8ae86Sav int 33110cc8ae86Sav dname_to_bankslot(char *dname, int *bank, int *slot) 33120cc8ae86Sav { 33130cc8ae86Sav int i; 33140cc8ae86Sav int tsz; 33150cc8ae86Sav char **tbl; 33160cc8ae86Sav 33170cc8ae86Sav if (plat_model == MODEL_DC) { /* DC */ 33180cc8ae86Sav tbl = mc_dc_dimm_unum_table; 33190cc8ae86Sav tsz = OPL_MAX_DIMMS; 33200cc8ae86Sav } else { 33210cc8ae86Sav tbl = mc_ff_dimm_unum_table; 33220cc8ae86Sav tsz = 2 * OPL_MAX_DIMMS; 33230cc8ae86Sav } 33240cc8ae86Sav 33250cc8ae86Sav for (i = 0; i < tsz; i++) { 33260cc8ae86Sav if (strcmp(dname, tbl[i]) == 0) { 33270cc8ae86Sav break; 33280cc8ae86Sav } 33290cc8ae86Sav } 33300cc8ae86Sav if (i == tsz) { 33310cc8ae86Sav return (1); 33320cc8ae86Sav } 33330cc8ae86Sav *bank = INDEX_TO_BANK(i); 33340cc8ae86Sav *slot = INDEX_TO_SLOT(i); 33350cc8ae86Sav return (0); 33360cc8ae86Sav } 33370cc8ae86Sav 33380cc8ae86Sav /* 33390cc8ae86Sav * mc_get_mem_addr -- get the physical address of a DIMM corresponding 33400cc8ae86Sav * to the unum and sid. 33410cc8ae86Sav */ 33420cc8ae86Sav int 33430cc8ae86Sav mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr) 33440cc8ae86Sav { 33450cc8ae86Sav int board; 33460cc8ae86Sav int bank; 33470cc8ae86Sav int slot; 33480cc8ae86Sav int i; 33490cc8ae86Sav int ret = ENODEV; 33500cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 33510cc8ae86Sav mc_addr_t maddr; 33520cc8ae86Sav mc_opl_t *mcp; 33530cc8ae86Sav 33540cc8ae86Sav MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n", 33550cc8ae86Sav unum, sid, offset); 33560cc8ae86Sav if (parse_unum_memory(unum, &board, dname) != 0) { 33570cc8ae86Sav MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 33580cc8ae86Sav unum, ret); 33590cc8ae86Sav return (EINVAL); 33600cc8ae86Sav } 33610cc8ae86Sav 33620cc8ae86Sav if (board < 0) { 33630cc8ae86Sav MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n", 33640cc8ae86Sav board, dname); 33650cc8ae86Sav return (EINVAL); 33660cc8ae86Sav } 33670cc8ae86Sav 33680cc8ae86Sav mutex_enter(&mcmutex); 33690cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 33700cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 33710cc8ae86Sav continue; 33720cc8ae86Sav mutex_enter(&mcp->mc_lock); 3373*aeb241b2Sav if (mcp->mc_phys_board_num != board) { 33740cc8ae86Sav mutex_exit(&mcp->mc_lock); 33750cc8ae86Sav continue; 33760cc8ae86Sav } 33770cc8ae86Sav 33780cc8ae86Sav ret = dname_to_bankslot(dname, &bank, &slot); 33790cc8ae86Sav MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot); 33800cc8ae86Sav if (ret != 0) { 33810cc8ae86Sav MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n"); 33820cc8ae86Sav ret = ENODEV; 33830cc8ae86Sav } else { 3384*aeb241b2Sav maddr.ma_bd = mcp->mc_board_num; 33850cc8ae86Sav maddr.ma_bank = bank; 33860cc8ae86Sav maddr.ma_dimm_addr = offset; 33870cc8ae86Sav ret = mcaddr_to_pa(mcp, &maddr, paddr); 33880cc8ae86Sav if (ret != 0) { 33890cc8ae86Sav MC_LOG("mc_get_mem_addr: " 33900cc8ae86Sav "mcaddr_to_pa failed\n"); 33910cc8ae86Sav ret = ENODEV; 33920cc8ae86Sav } 3393*aeb241b2Sav mutex_exit(&mcp->mc_lock); 3394*aeb241b2Sav break; 33950cc8ae86Sav } 33960cc8ae86Sav mutex_exit(&mcp->mc_lock); 33970cc8ae86Sav } 33980cc8ae86Sav mutex_exit(&mcmutex); 33990cc8ae86Sav MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr); 34000cc8ae86Sav return (ret); 34010cc8ae86Sav } 34020cc8ae86Sav 34030cc8ae86Sav static void 34040cc8ae86Sav mc_free_dimm_list(mc_dimm_info_t *d) 34050cc8ae86Sav { 34060cc8ae86Sav mc_dimm_info_t *next; 34070cc8ae86Sav 34080cc8ae86Sav while (d != NULL) { 34090cc8ae86Sav next = d->md_next; 34100cc8ae86Sav kmem_free(d, sizeof (mc_dimm_info_t)); 34110cc8ae86Sav d = next; 34120cc8ae86Sav } 34130cc8ae86Sav } 34140cc8ae86Sav 34150cc8ae86Sav /* 34160cc8ae86Sav * mc_get_dimm_list -- get the list of dimms with serial-id info 34170cc8ae86Sav * from the SP. 34180cc8ae86Sav */ 34190cc8ae86Sav mc_dimm_info_t * 34200cc8ae86Sav mc_get_dimm_list(mc_opl_t *mcp) 34210cc8ae86Sav { 34220cc8ae86Sav uint32_t bufsz; 34230cc8ae86Sav uint32_t maxbufsz; 34240cc8ae86Sav int ret; 34250cc8ae86Sav int sexp; 34260cc8ae86Sav board_dimm_info_t *bd_dimmp; 34270cc8ae86Sav mc_dimm_info_t *dimm_list = NULL; 34280cc8ae86Sav 34290cc8ae86Sav maxbufsz = bufsz = sizeof (board_dimm_info_t) + 34300cc8ae86Sav ((MCOPL_MAX_DIMMNAME + MCOPL_MAX_SERIAL + 34310cc8ae86Sav MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS); 34320cc8ae86Sav 34330cc8ae86Sav bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP); 34340cc8ae86Sav ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz); 34350cc8ae86Sav 34360cc8ae86Sav MC_LOG("mc_get_dimm_list: scf_service_getinfo returned=%d\n", ret); 34370cc8ae86Sav if (ret == 0) { 34380cc8ae86Sav sexp = sizeof (board_dimm_info_t) + 34390cc8ae86Sav ((bd_dimmp->bd_dnamesz + bd_dimmp->bd_serialsz + 34400cc8ae86Sav bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms); 34410cc8ae86Sav 34420cc8ae86Sav if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) && 34430cc8ae86Sav (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) && 34440cc8ae86Sav (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) && 34450cc8ae86Sav (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) && 34460cc8ae86Sav (sexp <= bufsz)) { 34470cc8ae86Sav 34480cc8ae86Sav #ifdef DEBUG 34490cc8ae86Sav if (oplmc_debug) 34500cc8ae86Sav mc_dump_dimm_info(bd_dimmp); 34510cc8ae86Sav #endif 34520cc8ae86Sav dimm_list = mc_prepare_dimmlist(bd_dimmp); 34530cc8ae86Sav 34540cc8ae86Sav } else { 34550cc8ae86Sav cmn_err(CE_WARN, "DIMM info version mismatch\n"); 34560cc8ae86Sav } 34570cc8ae86Sav } 34580cc8ae86Sav kmem_free(bd_dimmp, maxbufsz); 34590cc8ae86Sav MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list); 34600cc8ae86Sav return (dimm_list); 34610cc8ae86Sav } 34620cc8ae86Sav 34630cc8ae86Sav /* 34640cc8ae86Sav * mc_prepare_dimmlist - Prepare the dimm list from the infomation 34650cc8ae86Sav * recieved from the SP. 34660cc8ae86Sav */ 34670cc8ae86Sav mc_dimm_info_t * 34680cc8ae86Sav mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp) 34690cc8ae86Sav { 34700cc8ae86Sav char *dimm_name; 34710cc8ae86Sav char *serial; 34720cc8ae86Sav char *part; 34730cc8ae86Sav int dimm; 34740cc8ae86Sav int dnamesz = bd_dimmp->bd_dnamesz; 34750cc8ae86Sav int sersz = bd_dimmp->bd_serialsz; 34760cc8ae86Sav int partsz = bd_dimmp->bd_partnumsz; 34770cc8ae86Sav mc_dimm_info_t *dimm_list = NULL; 34780cc8ae86Sav mc_dimm_info_t *d; 34790cc8ae86Sav 34800cc8ae86Sav dimm_name = (char *)(bd_dimmp + 1); 34810cc8ae86Sav for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 34820cc8ae86Sav 34830cc8ae86Sav d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t), 34840cc8ae86Sav KM_SLEEP); 34850cc8ae86Sav snprintf(d->md_dimmname, dnamesz + 1, "%s", dimm_name); 34860cc8ae86Sav serial = dimm_name + dnamesz; 34870cc8ae86Sav snprintf(d->md_serial, sersz + 1, "%s", serial); 34880cc8ae86Sav part = serial + sersz; 34890cc8ae86Sav snprintf(d->md_partnum, partsz + 1, "%s", part); 34900cc8ae86Sav 34910cc8ae86Sav d->md_next = dimm_list; 34920cc8ae86Sav dimm_list = d; 34930cc8ae86Sav dimm_name = part + partsz; 34940cc8ae86Sav } 34950cc8ae86Sav return (dimm_list); 34960cc8ae86Sav } 34970cc8ae86Sav 34980cc8ae86Sav #ifdef DEBUG 34990cc8ae86Sav void 35000cc8ae86Sav mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz) 35010cc8ae86Sav { 35020cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 35030cc8ae86Sav char serial[MCOPL_MAX_SERIAL + 1]; 35040cc8ae86Sav char part[ MCOPL_MAX_PARTNUM + 1]; 35050cc8ae86Sav char *b; 35060cc8ae86Sav 35070cc8ae86Sav b = buf; 35080cc8ae86Sav snprintf(dname, dnamesz + 1, "%s", b); 35090cc8ae86Sav b += dnamesz; 35100cc8ae86Sav snprintf(serial, serialsz + 1, "%s", b); 35110cc8ae86Sav b += serialsz; 35120cc8ae86Sav snprintf(part, partnumsz + 1, "%s", b); 35130cc8ae86Sav printf("DIMM=%s Serial=%s PartNum=%s\n", dname, serial, part); 35140cc8ae86Sav } 35150cc8ae86Sav 35160cc8ae86Sav void 35170cc8ae86Sav mc_dump_dimm_info(board_dimm_info_t *bd_dimmp) 35180cc8ae86Sav { 35190cc8ae86Sav int dimm; 35200cc8ae86Sav int dnamesz = bd_dimmp->bd_dnamesz; 35210cc8ae86Sav int sersz = bd_dimmp->bd_serialsz; 35220cc8ae86Sav int partsz = bd_dimmp->bd_partnumsz; 35230cc8ae86Sav char *buf; 35240cc8ae86Sav 35250cc8ae86Sav printf("Version=%d Board=%02d DIMMs=%d NameSize=%d " 35260cc8ae86Sav "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version, 35270cc8ae86Sav bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz, 35280cc8ae86Sav bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz); 35290cc8ae86Sav printf("======================================================\n"); 35300cc8ae86Sav 35310cc8ae86Sav buf = (char *)(bd_dimmp + 1); 35320cc8ae86Sav for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 35330cc8ae86Sav mc_dump_dimm(buf, dnamesz, sersz, partsz); 35340cc8ae86Sav buf += dnamesz + sersz + partsz; 35350cc8ae86Sav } 35360cc8ae86Sav printf("======================================================\n"); 35370cc8ae86Sav } 35380cc8ae86Sav 35390cc8ae86Sav 35400cc8ae86Sav /* ARGSUSED */ 35410cc8ae86Sav static int 35420cc8ae86Sav mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 35430cc8ae86Sav int *rvalp) 35440cc8ae86Sav { 35450cc8ae86Sav caddr_t buf; 35460cc8ae86Sav uint64_t pa; 35470cc8ae86Sav int rv = 0; 35480cc8ae86Sav int i; 35490cc8ae86Sav uint32_t flags; 35500cc8ae86Sav static uint32_t offset = 0; 35510cc8ae86Sav 35520cc8ae86Sav 35530cc8ae86Sav flags = (cmd >> 4) & 0xfffffff; 35540cc8ae86Sav 35550cc8ae86Sav cmd &= 0xf; 35560cc8ae86Sav 35570cc8ae86Sav MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags); 35580cc8ae86Sav 35590cc8ae86Sav if (arg != NULL) { 35600cc8ae86Sav if (ddi_copyin((const void *)arg, (void *)&pa, 35610cc8ae86Sav sizeof (uint64_t), 0) < 0) { 35620cc8ae86Sav rv = EFAULT; 35630cc8ae86Sav return (rv); 35640cc8ae86Sav } 35650cc8ae86Sav buf = NULL; 35660cc8ae86Sav } else { 35670cc8ae86Sav buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP); 35680cc8ae86Sav 35690cc8ae86Sav pa = va_to_pa(buf); 35700cc8ae86Sav pa += offset; 35710cc8ae86Sav 35720cc8ae86Sav offset += 64; 35730cc8ae86Sav if (offset >= PAGESIZE) 35740cc8ae86Sav offset = 0; 35750cc8ae86Sav } 35760cc8ae86Sav 35770cc8ae86Sav switch (cmd) { 35780cc8ae86Sav case MCI_CE: 35790cc8ae86Sav mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa, 35800cc8ae86Sav flags); 35810cc8ae86Sav break; 35820cc8ae86Sav case MCI_PERM_CE: 35830cc8ae86Sav mc_inject_error(MC_INJECT_PERMANENT_CE, pa, 35840cc8ae86Sav flags); 35850cc8ae86Sav break; 35860cc8ae86Sav case MCI_UE: 35870cc8ae86Sav mc_inject_error(MC_INJECT_UE, pa, 35880cc8ae86Sav flags); 35890cc8ae86Sav break; 35900cc8ae86Sav case MCI_M_CE: 35910cc8ae86Sav mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa, 35920cc8ae86Sav flags); 35930cc8ae86Sav break; 35940cc8ae86Sav case MCI_M_PCE: 35950cc8ae86Sav mc_inject_error(MC_INJECT_PERMANENT_MCE, pa, 35960cc8ae86Sav flags); 35970cc8ae86Sav break; 35980cc8ae86Sav case MCI_M_UE: 35990cc8ae86Sav mc_inject_error(MC_INJECT_MUE, pa, 36000cc8ae86Sav flags); 36010cc8ae86Sav break; 36020cc8ae86Sav case MCI_CMP: 36030cc8ae86Sav mc_inject_error(MC_INJECT_CMPE, pa, 36040cc8ae86Sav flags); 36050cc8ae86Sav break; 36060cc8ae86Sav case MCI_NOP: 36070cc8ae86Sav mc_inject_error(MC_INJECT_NOP, pa, flags); 36080cc8ae86Sav break; 36090cc8ae86Sav case MCI_SHOW_ALL: 36100cc8ae86Sav mc_debug_show_all = 1; 36110cc8ae86Sav break; 36120cc8ae86Sav case MCI_SHOW_NONE: 36130cc8ae86Sav mc_debug_show_all = 0; 36140cc8ae86Sav break; 36150cc8ae86Sav case MCI_ALLOC: 36160cc8ae86Sav /* 36170cc8ae86Sav * just allocate some kernel memory and never free it 36180cc8ae86Sav * 512 MB seems to be the maximum size supported. 36190cc8ae86Sav */ 36200cc8ae86Sav cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512); 36210cc8ae86Sav for (i = 0; i < flags; i++) { 36220cc8ae86Sav buf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP); 36230cc8ae86Sav cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n", 36240cc8ae86Sav (u_longlong_t)buf, (u_longlong_t)va_to_pa(buf)); 36250cc8ae86Sav } 36260cc8ae86Sav break; 36270cc8ae86Sav case MCI_SUSPEND: 36280cc8ae86Sav (void) opl_mc_suspend(); 36290cc8ae86Sav break; 36300cc8ae86Sav case MCI_RESUME: 36310cc8ae86Sav (void) opl_mc_resume(); 36320cc8ae86Sav break; 36330cc8ae86Sav default: 36340cc8ae86Sav rv = ENXIO; 36350cc8ae86Sav } 36360cc8ae86Sav return (rv); 36370cc8ae86Sav } 36380cc8ae86Sav 36390cc8ae86Sav #endif /* DEBUG */ 3640