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 /* 2268ac2337Sjl * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 231e2e7a75Shuah * Use is subject to license terms. 241e2e7a75Shuah */ 2525cf1a30Sjl /* 26*601c2e1eSdhain * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2007 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> 43d8a0cca9Swh #include <sys/sunndi.h> 4425cf1a30Sjl #include <sys/ddifm.h> 4525cf1a30Sjl #include <sys/fm/protocol.h> 4625cf1a30Sjl #include <sys/fm/util.h> 4725cf1a30Sjl #include <sys/kmem.h> 4825cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h> 4925cf1a30Sjl #include <sys/memlist.h> 5025cf1a30Sjl #include <sys/param.h> 510cc8ae86Sav #include <sys/disp.h> 5225cf1a30Sjl #include <vm/page.h> 5325cf1a30Sjl #include <sys/mc-opl.h> 540cc8ae86Sav #include <sys/opl.h> 550cc8ae86Sav #include <sys/opl_dimm.h> 560cc8ae86Sav #include <sys/scfd/scfostoescf.h> 57cfb9e062Shyw #include <sys/cpu_module.h> 58cfb9e062Shyw #include <vm/seg_kmem.h> 59cfb9e062Shyw #include <sys/vmem.h> 60cfb9e062Shyw #include <vm/hat_sfmmu.h> 61cfb9e062Shyw #include <sys/vmsystm.h> 62738dd194Shyw #include <sys/membar.h> 6325cf1a30Sjl 6425cf1a30Sjl /* 6525cf1a30Sjl * Function prototypes 6625cf1a30Sjl */ 6725cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *); 6825cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *); 6925cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 7025cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t); 7125cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t); 7225cf1a30Sjl 730cc8ae86Sav static int mc_poll_init(void); 740cc8ae86Sav static void mc_poll_fini(void); 7525cf1a30Sjl static int mc_board_add(mc_opl_t *mcp); 7625cf1a30Sjl static int mc_board_del(mc_opl_t *mcp); 7725cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag); 7825cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag); 790cc8ae86Sav int opl_mc_suspend(void); 800cc8ae86Sav int opl_mc_resume(void); 8125cf1a30Sjl 8225cf1a30Sjl static void insert_mcp(mc_opl_t *mcp); 8325cf1a30Sjl static void delete_mcp(mc_opl_t *mcp); 8425cf1a30Sjl 8525cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr); 8625cf1a30Sjl 87738dd194Shyw static int mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa); 8825cf1a30Sjl 8925cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *); 900cc8ae86Sav int mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr); 910cc8ae86Sav int mc_get_mem_offset(uint64_t paddr, uint64_t *offp); 920cc8ae86Sav int mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp); 930cc8ae86Sav int mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 940cc8ae86Sav int buflen, int *lenp); 950cc8ae86Sav mc_dimm_info_t *mc_get_dimm_list(mc_opl_t *mcp); 960cc8ae86Sav mc_dimm_info_t *mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp); 970cc8ae86Sav int mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int lsb, int bank, 980cc8ae86Sav uint32_t mf_type, uint32_t d_slot); 990cc8ae86Sav static void mc_free_dimm_list(mc_dimm_info_t *d); 10025cf1a30Sjl static void mc_get_mlist(mc_opl_t *); 1010cc8ae86Sav static void mc_polling(void); 1020cc8ae86Sav static int mc_opl_get_physical_board(int); 1030cc8ae86Sav 104*601c2e1eSdhain static void mc_clear_rewrite(mc_opl_t *mcp, int i); 105*601c2e1eSdhain static void mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state); 106*601c2e1eSdhain 1070cc8ae86Sav #ifdef DEBUG 1080cc8ae86Sav static int mc_ioctl_debug(dev_t, int, intptr_t, int, cred_t *, int *); 1090cc8ae86Sav void mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz); 1100cc8ae86Sav void mc_dump_dimm_info(board_dimm_info_t *bd_dimmp); 1110cc8ae86Sav #endif 11225cf1a30Sjl 11325cf1a30Sjl #pragma weak opl_get_physical_board 11425cf1a30Sjl extern int opl_get_physical_board(int); 1150cc8ae86Sav extern int plat_max_boards(void); 11625cf1a30Sjl 11725cf1a30Sjl /* 11825cf1a30Sjl * Configuration data structures 11925cf1a30Sjl */ 12025cf1a30Sjl static struct cb_ops mc_cb_ops = { 12125cf1a30Sjl mc_open, /* open */ 12225cf1a30Sjl mc_close, /* close */ 12325cf1a30Sjl nulldev, /* strategy */ 12425cf1a30Sjl nulldev, /* print */ 12525cf1a30Sjl nodev, /* dump */ 12625cf1a30Sjl nulldev, /* read */ 12725cf1a30Sjl nulldev, /* write */ 12825cf1a30Sjl mc_ioctl, /* ioctl */ 12925cf1a30Sjl nodev, /* devmap */ 13025cf1a30Sjl nodev, /* mmap */ 13125cf1a30Sjl nodev, /* segmap */ 13225cf1a30Sjl nochpoll, /* poll */ 13325cf1a30Sjl ddi_prop_op, /* cb_prop_op */ 13425cf1a30Sjl 0, /* streamtab */ 13525cf1a30Sjl D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 13625cf1a30Sjl CB_REV, /* rev */ 13725cf1a30Sjl nodev, /* cb_aread */ 13825cf1a30Sjl nodev /* cb_awrite */ 13925cf1a30Sjl }; 14025cf1a30Sjl 14125cf1a30Sjl static struct dev_ops mc_ops = { 14225cf1a30Sjl DEVO_REV, /* rev */ 14325cf1a30Sjl 0, /* refcnt */ 14425cf1a30Sjl ddi_getinfo_1to1, /* getinfo */ 14525cf1a30Sjl nulldev, /* identify */ 14625cf1a30Sjl nulldev, /* probe */ 14725cf1a30Sjl mc_attach, /* attach */ 14825cf1a30Sjl mc_detach, /* detach */ 14925cf1a30Sjl nulldev, /* reset */ 15025cf1a30Sjl &mc_cb_ops, /* cb_ops */ 15125cf1a30Sjl (struct bus_ops *)0, /* bus_ops */ 15225cf1a30Sjl nulldev /* power */ 15325cf1a30Sjl }; 15425cf1a30Sjl 15525cf1a30Sjl /* 15625cf1a30Sjl * Driver globals 15725cf1a30Sjl */ 15825cf1a30Sjl 1590cc8ae86Sav static enum { 1600cc8ae86Sav MODEL_FF1 = 0, 1610cc8ae86Sav MODEL_FF2 = 1, 1620cc8ae86Sav MODEL_DC = 2 1630cc8ae86Sav } plat_model = MODEL_DC; /* The default behaviour is DC */ 1640cc8ae86Sav 1650cc8ae86Sav static struct plat_model_names { 1660cc8ae86Sav const char *unit_name; 1670cc8ae86Sav const char *mem_name; 1680cc8ae86Sav } model_names[] = { 1690cc8ae86Sav { "MBU_A", "MEMB" }, 1700cc8ae86Sav { "MBU_B", "MEMB" }, 1710cc8ae86Sav { "CMU", "" } 1720cc8ae86Sav }; 17325cf1a30Sjl 1740cc8ae86Sav /* 1750cc8ae86Sav * The DIMM Names for DC platform. 1760cc8ae86Sav * The index into this table is made up of (bank, dslot), 1770cc8ae86Sav * Where dslot occupies bits 0-1 and bank occupies 2-4. 1780cc8ae86Sav */ 1790cc8ae86Sav static char *mc_dc_dimm_unum_table[OPL_MAX_DIMMS] = { 1800cc8ae86Sav /* --------CMUnn----------- */ 1810cc8ae86Sav /* --CS0-----|--CS1------ */ 1820cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 183c964b0e6Sraghuram "03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */ 184c964b0e6Sraghuram "13A", "12A", "13B", "12B", /* Bank 1 (MAC 0 bank 1) */ 185c964b0e6Sraghuram "23A", "22A", "23B", "22B", /* Bank 2 (MAC 1 bank 0) */ 186c964b0e6Sraghuram "33A", "32A", "33B", "32B", /* Bank 3 (MAC 1 bank 1) */ 187c964b0e6Sraghuram "01A", "00A", "01B", "00B", /* Bank 4 (MAC 2 bank 0) */ 188c964b0e6Sraghuram "11A", "10A", "11B", "10B", /* Bank 5 (MAC 2 bank 1) */ 189c964b0e6Sraghuram "21A", "20A", "21B", "20B", /* Bank 6 (MAC 3 bank 0) */ 190c964b0e6Sraghuram "31A", "30A", "31B", "30B" /* Bank 7 (MAC 3 bank 1) */ 1910cc8ae86Sav }; 1920cc8ae86Sav 1930cc8ae86Sav /* 1940cc8ae86Sav * The DIMM Names for FF1/FF2 platforms. 1950cc8ae86Sav * The index into this table is made up of (board, bank, dslot), 1960cc8ae86Sav * Where dslot occupies bits 0-1, bank occupies 2-4 and 1970cc8ae86Sav * board occupies the bit 5. 1980cc8ae86Sav */ 1990cc8ae86Sav static char *mc_ff_dimm_unum_table[2 * OPL_MAX_DIMMS] = { 2000cc8ae86Sav /* --------CMU0---------- */ 2010cc8ae86Sav /* --CS0-----|--CS1------ */ 2020cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 203c964b0e6Sraghuram "03A", "02A", "03B", "02B", /* Bank 0 (MAC 0 bank 0) */ 204c964b0e6Sraghuram "01A", "00A", "01B", "00B", /* Bank 1 (MAC 0 bank 1) */ 205c964b0e6Sraghuram "13A", "12A", "13B", "12B", /* Bank 2 (MAC 1 bank 0) */ 206c964b0e6Sraghuram "11A", "10A", "11B", "10B", /* Bank 3 (MAC 1 bank 1) */ 207c964b0e6Sraghuram "23A", "22A", "23B", "22B", /* Bank 4 (MAC 2 bank 0) */ 208c964b0e6Sraghuram "21A", "20A", "21B", "20B", /* Bank 5 (MAC 2 bank 1) */ 209c964b0e6Sraghuram "33A", "32A", "33B", "32B", /* Bank 6 (MAC 3 bank 0) */ 210c964b0e6Sraghuram "31A", "30A", "31B", "30B", /* Bank 7 (MAC 3 bank 1) */ 2110cc8ae86Sav /* --------CMU1---------- */ 2120cc8ae86Sav /* --CS0-----|--CS1------ */ 2130cc8ae86Sav /* -H-|--L-- | -H- | -L-- */ 214c964b0e6Sraghuram "43A", "42A", "43B", "42B", /* Bank 0 (MAC 0 bank 0) */ 215c964b0e6Sraghuram "41A", "40A", "41B", "40B", /* Bank 1 (MAC 0 bank 1) */ 216c964b0e6Sraghuram "53A", "52A", "53B", "52B", /* Bank 2 (MAC 1 bank 0) */ 217c964b0e6Sraghuram "51A", "50A", "51B", "50B", /* Bank 3 (MAC 1 bank 1) */ 218c964b0e6Sraghuram "63A", "62A", "63B", "62B", /* Bank 4 (MAC 2 bank 0) */ 219c964b0e6Sraghuram "61A", "60A", "61B", "60B", /* Bank 5 (MAC 2 bank 1) */ 220c964b0e6Sraghuram "73A", "72A", "73B", "72B", /* Bank 6 (MAC 3 bank 0) */ 221c964b0e6Sraghuram "71A", "70A", "71B", "70B" /* Bank 7 (MAC 3 bank 1) */ 2220cc8ae86Sav }; 2230cc8ae86Sav 2240cc8ae86Sav #define BD_BK_SLOT_TO_INDEX(bd, bk, s) \ 2250cc8ae86Sav (((bd & 0x01) << 5) | ((bk & 0x07) << 2) | (s & 0x03)) 2260cc8ae86Sav 2270cc8ae86Sav #define INDEX_TO_BANK(i) (((i) & 0x1C) >> 2) 2280cc8ae86Sav #define INDEX_TO_SLOT(i) ((i) & 0x03) 2290cc8ae86Sav 230aeb241b2Sav #define SLOT_TO_CS(slot) ((slot & 0x3) >> 1) 231aeb241b2Sav 2320cc8ae86Sav /* Isolation unit size is 64 MB */ 2330cc8ae86Sav #define MC_ISOLATION_BSIZE (64 * 1024 * 1024) 2340cc8ae86Sav 2350cc8ae86Sav #define MC_MAX_SPEEDS 7 2360cc8ae86Sav 2370cc8ae86Sav typedef struct { 2380cc8ae86Sav uint32_t mc_speeds; 2390cc8ae86Sav uint32_t mc_period; 2400cc8ae86Sav } mc_scan_speed_t; 2410cc8ae86Sav 2420cc8ae86Sav #define MC_CNTL_SPEED_SHIFT 26 2430cc8ae86Sav 24437afe445Shyw /* 24537afe445Shyw * In mirror mode, we normalized the bank idx to "even" since 24637afe445Shyw * the HW treats them as one unit w.r.t programming. 24737afe445Shyw * This bank index will be the "effective" bank index. 24837afe445Shyw * All mirrored bank state info on mc_period, mc_speedup_period 24937afe445Shyw * will be stored in the even bank structure to avoid code duplication. 25037afe445Shyw */ 25137afe445Shyw #define MIRROR_IDX(bankidx) (bankidx & ~1) 25237afe445Shyw 2530cc8ae86Sav static mc_scan_speed_t mc_scan_speeds[MC_MAX_SPEEDS] = { 2540cc8ae86Sav {0x6 << MC_CNTL_SPEED_SHIFT, 0}, 2550cc8ae86Sav {0x5 << MC_CNTL_SPEED_SHIFT, 32}, 2560cc8ae86Sav {0x4 << MC_CNTL_SPEED_SHIFT, 64}, 2570cc8ae86Sav {0x3 << MC_CNTL_SPEED_SHIFT, 128}, 2580cc8ae86Sav {0x2 << MC_CNTL_SPEED_SHIFT, 256}, 2590cc8ae86Sav {0x1 << MC_CNTL_SPEED_SHIFT, 512}, 2600cc8ae86Sav {0x0 << MC_CNTL_SPEED_SHIFT, 1024} 2610cc8ae86Sav }; 2620cc8ae86Sav 2630cc8ae86Sav static uint32_t mc_max_speed = (0x6 << 26); 2640cc8ae86Sav 2650cc8ae86Sav int mc_isolation_bsize = MC_ISOLATION_BSIZE; 2660cc8ae86Sav int mc_patrol_interval_sec = MC_PATROL_INTERVAL_SEC; 2670cc8ae86Sav int mc_max_scf_retry = 16; 2680cc8ae86Sav int mc_max_scf_logs = 64; 2690cc8ae86Sav int mc_max_errlog_processed = BANKNUM_PER_SB*2; 2700cc8ae86Sav int mc_scan_period = 12 * 60 * 60; /* 12 hours period */ 2710cc8ae86Sav int mc_max_rewrite_loop = 100; 2720cc8ae86Sav int mc_rewrite_delay = 10; 2730cc8ae86Sav /* 2740cc8ae86Sav * it takes SCF about 300 m.s. to process a requst. We can bail out 2750cc8ae86Sav * if it is busy. It does not pay to wait for it too long. 2760cc8ae86Sav */ 2770cc8ae86Sav int mc_max_scf_loop = 2; 2780cc8ae86Sav int mc_scf_delay = 100; 2790cc8ae86Sav int mc_pce_dropped = 0; 2800cc8ae86Sav int mc_poll_priority = MINCLSYSPRI; 281*601c2e1eSdhain int mc_max_rewrite_retry = 6 * 60; 28225cf1a30Sjl 2830cc8ae86Sav 2840cc8ae86Sav /* 2851039f409Sav * Mutex hierarchy in mc-opl 2860cc8ae86Sav * If both mcmutex and mc_lock must be held, 2870cc8ae86Sav * mcmutex must be acquired first, and then mc_lock. 2880cc8ae86Sav */ 2890cc8ae86Sav 2900cc8ae86Sav static kmutex_t mcmutex; 2910cc8ae86Sav mc_opl_t *mc_instances[OPL_MAX_BOARDS]; 2920cc8ae86Sav 2930cc8ae86Sav static kmutex_t mc_polling_lock; 2940cc8ae86Sav static kcondvar_t mc_polling_cv; 2950cc8ae86Sav static kcondvar_t mc_poll_exit_cv; 2960cc8ae86Sav static int mc_poll_cmd = 0; 2970cc8ae86Sav static int mc_pollthr_running = 0; 2980cc8ae86Sav int mc_timeout_period = 0; /* this is in m.s. */ 29925cf1a30Sjl void *mc_statep; 30025cf1a30Sjl 30125cf1a30Sjl #ifdef DEBUG 3022742aa22Shyw int oplmc_debug = 0; 30325cf1a30Sjl #endif 30425cf1a30Sjl 3050cc8ae86Sav static int mc_debug_show_all = 0; 30625cf1a30Sjl 30725cf1a30Sjl extern struct mod_ops mod_driverops; 30825cf1a30Sjl 30925cf1a30Sjl static struct modldrv modldrv = { 31025cf1a30Sjl &mod_driverops, /* module type, this one is a driver */ 3110cc8ae86Sav "OPL Memory-controller %I%", /* module name */ 31225cf1a30Sjl &mc_ops, /* driver ops */ 31325cf1a30Sjl }; 31425cf1a30Sjl 31525cf1a30Sjl static struct modlinkage modlinkage = { 31625cf1a30Sjl MODREV_1, /* rev */ 31725cf1a30Sjl (void *)&modldrv, 31825cf1a30Sjl NULL 31925cf1a30Sjl }; 32025cf1a30Sjl 32125cf1a30Sjl #pragma weak opl_get_mem_unum 3220cc8ae86Sav #pragma weak opl_get_mem_sid 3230cc8ae86Sav #pragma weak opl_get_mem_offset 3240cc8ae86Sav #pragma weak opl_get_mem_addr 3250cc8ae86Sav 32625cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *); 3270cc8ae86Sav extern int (*opl_get_mem_sid)(char *unum, char *buf, int buflen, int *lenp); 3280cc8ae86Sav extern int (*opl_get_mem_offset)(uint64_t paddr, uint64_t *offp); 3290cc8ae86Sav extern int (*opl_get_mem_addr)(char *unum, char *sid, uint64_t offset, 3300cc8ae86Sav uint64_t *paddr); 3310cc8ae86Sav 33225cf1a30Sjl 33325cf1a30Sjl /* 33425cf1a30Sjl * pseudo-mc node portid format 33525cf1a30Sjl * 33625cf1a30Sjl * [10] = 0 33725cf1a30Sjl * [9] = 1 33825cf1a30Sjl * [8] = LSB_ID[4] = 0 33925cf1a30Sjl * [7:4] = LSB_ID[3:0] 34025cf1a30Sjl * [3:0] = 0 34125cf1a30Sjl * 34225cf1a30Sjl */ 34325cf1a30Sjl 34425cf1a30Sjl /* 34525cf1a30Sjl * These are the module initialization routines. 34625cf1a30Sjl */ 34725cf1a30Sjl int 34825cf1a30Sjl _init(void) 34925cf1a30Sjl { 3500cc8ae86Sav int error; 3510cc8ae86Sav int plen; 3520cc8ae86Sav char model[20]; 3530cc8ae86Sav pnode_t node; 35425cf1a30Sjl 35525cf1a30Sjl 35625cf1a30Sjl if ((error = ddi_soft_state_init(&mc_statep, 35725cf1a30Sjl sizeof (mc_opl_t), 1)) != 0) 35825cf1a30Sjl return (error); 35925cf1a30Sjl 3600cc8ae86Sav if ((error = mc_poll_init()) != 0) { 3610cc8ae86Sav ddi_soft_state_fini(&mc_statep); 3620cc8ae86Sav return (error); 3630cc8ae86Sav } 3640cc8ae86Sav 36525cf1a30Sjl mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL); 36625cf1a30Sjl if (&opl_get_mem_unum) 36725cf1a30Sjl opl_get_mem_unum = mc_get_mem_unum; 3680cc8ae86Sav if (&opl_get_mem_sid) 3690cc8ae86Sav opl_get_mem_sid = mc_get_mem_sid; 3700cc8ae86Sav if (&opl_get_mem_offset) 3710cc8ae86Sav opl_get_mem_offset = mc_get_mem_offset; 3720cc8ae86Sav if (&opl_get_mem_addr) 3730cc8ae86Sav opl_get_mem_addr = mc_get_mem_addr; 3740cc8ae86Sav 3750cc8ae86Sav node = prom_rootnode(); 3760cc8ae86Sav plen = prom_getproplen(node, "model"); 3770cc8ae86Sav 3780cc8ae86Sav if (plen > 0 && plen < sizeof (model)) { 3790cc8ae86Sav (void) prom_getprop(node, "model", model); 3800cc8ae86Sav model[plen] = '\0'; 3810cc8ae86Sav if (strcmp(model, "FF1") == 0) 3820cc8ae86Sav plat_model = MODEL_FF1; 3830cc8ae86Sav else if (strcmp(model, "FF2") == 0) 3840cc8ae86Sav plat_model = MODEL_FF2; 3850cc8ae86Sav else if (strncmp(model, "DC", 2) == 0) 3860cc8ae86Sav plat_model = MODEL_DC; 3870cc8ae86Sav } 38825cf1a30Sjl 38925cf1a30Sjl error = mod_install(&modlinkage); 39025cf1a30Sjl if (error != 0) { 39125cf1a30Sjl if (&opl_get_mem_unum) 39225cf1a30Sjl opl_get_mem_unum = NULL; 3930cc8ae86Sav if (&opl_get_mem_sid) 3940cc8ae86Sav opl_get_mem_sid = NULL; 3950cc8ae86Sav if (&opl_get_mem_offset) 3960cc8ae86Sav opl_get_mem_offset = NULL; 3970cc8ae86Sav if (&opl_get_mem_addr) 3980cc8ae86Sav opl_get_mem_addr = NULL; 39925cf1a30Sjl mutex_destroy(&mcmutex); 4000cc8ae86Sav mc_poll_fini(); 40125cf1a30Sjl ddi_soft_state_fini(&mc_statep); 40225cf1a30Sjl } 40325cf1a30Sjl return (error); 40425cf1a30Sjl } 40525cf1a30Sjl 40625cf1a30Sjl int 40725cf1a30Sjl _fini(void) 40825cf1a30Sjl { 40925cf1a30Sjl int error; 41025cf1a30Sjl 41125cf1a30Sjl if ((error = mod_remove(&modlinkage)) != 0) 41225cf1a30Sjl return (error); 41325cf1a30Sjl 41425cf1a30Sjl if (&opl_get_mem_unum) 41525cf1a30Sjl opl_get_mem_unum = NULL; 4160cc8ae86Sav if (&opl_get_mem_sid) 4170cc8ae86Sav opl_get_mem_sid = NULL; 4180cc8ae86Sav if (&opl_get_mem_offset) 4190cc8ae86Sav opl_get_mem_offset = NULL; 4200cc8ae86Sav if (&opl_get_mem_addr) 4210cc8ae86Sav opl_get_mem_addr = NULL; 42225cf1a30Sjl 4230cc8ae86Sav mutex_destroy(&mcmutex); 4240cc8ae86Sav mc_poll_fini(); 42525cf1a30Sjl ddi_soft_state_fini(&mc_statep); 42625cf1a30Sjl 42725cf1a30Sjl return (0); 42825cf1a30Sjl } 42925cf1a30Sjl 43025cf1a30Sjl int 43125cf1a30Sjl _info(struct modinfo *modinfop) 43225cf1a30Sjl { 43325cf1a30Sjl return (mod_info(&modlinkage, modinfop)); 43425cf1a30Sjl } 43525cf1a30Sjl 4360cc8ae86Sav static void 4370cc8ae86Sav mc_polling_thread() 4380cc8ae86Sav { 4390cc8ae86Sav mutex_enter(&mc_polling_lock); 4400cc8ae86Sav mc_pollthr_running = 1; 4410cc8ae86Sav while (!(mc_poll_cmd & MC_POLL_EXIT)) { 4420cc8ae86Sav mc_polling(); 4430cc8ae86Sav cv_timedwait(&mc_polling_cv, &mc_polling_lock, 4440cc8ae86Sav ddi_get_lbolt() + mc_timeout_period); 4450cc8ae86Sav } 4460cc8ae86Sav mc_pollthr_running = 0; 4470cc8ae86Sav 4480cc8ae86Sav /* 4490cc8ae86Sav * signal if any one is waiting for this thread to exit. 4500cc8ae86Sav */ 4510cc8ae86Sav cv_signal(&mc_poll_exit_cv); 4520cc8ae86Sav mutex_exit(&mc_polling_lock); 4530cc8ae86Sav thread_exit(); 4540cc8ae86Sav /* NOTREACHED */ 4550cc8ae86Sav } 4560cc8ae86Sav 4570cc8ae86Sav static int 4580cc8ae86Sav mc_poll_init() 4590cc8ae86Sav { 4600cc8ae86Sav mutex_init(&mc_polling_lock, NULL, MUTEX_DRIVER, NULL); 4610cc8ae86Sav cv_init(&mc_polling_cv, NULL, CV_DRIVER, NULL); 4620cc8ae86Sav cv_init(&mc_poll_exit_cv, NULL, CV_DRIVER, NULL); 4630cc8ae86Sav return (0); 4640cc8ae86Sav } 4650cc8ae86Sav 4660cc8ae86Sav static void 4670cc8ae86Sav mc_poll_fini() 4680cc8ae86Sav { 4690cc8ae86Sav mutex_enter(&mc_polling_lock); 4700cc8ae86Sav if (mc_pollthr_running) { 4710cc8ae86Sav mc_poll_cmd = MC_POLL_EXIT; 4720cc8ae86Sav cv_signal(&mc_polling_cv); 4730cc8ae86Sav while (mc_pollthr_running) { 4740cc8ae86Sav cv_wait(&mc_poll_exit_cv, &mc_polling_lock); 4750cc8ae86Sav } 4760cc8ae86Sav } 4770cc8ae86Sav mutex_exit(&mc_polling_lock); 4780cc8ae86Sav mutex_destroy(&mc_polling_lock); 4790cc8ae86Sav cv_destroy(&mc_polling_cv); 4800cc8ae86Sav cv_destroy(&mc_poll_exit_cv); 4810cc8ae86Sav } 4820cc8ae86Sav 48325cf1a30Sjl static int 48425cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 48525cf1a30Sjl { 48625cf1a30Sjl mc_opl_t *mcp; 48725cf1a30Sjl int instance; 4880cc8ae86Sav int rv; 48925cf1a30Sjl 49025cf1a30Sjl /* get the instance of this devi */ 49125cf1a30Sjl instance = ddi_get_instance(devi); 49225cf1a30Sjl 49325cf1a30Sjl switch (cmd) { 49425cf1a30Sjl case DDI_ATTACH: 49525cf1a30Sjl break; 49625cf1a30Sjl case DDI_RESUME: 49725cf1a30Sjl mcp = ddi_get_soft_state(mc_statep, instance); 4980cc8ae86Sav rv = mc_resume(mcp, MC_DRIVER_SUSPENDED); 4990cc8ae86Sav return (rv); 50025cf1a30Sjl default: 50125cf1a30Sjl return (DDI_FAILURE); 50225cf1a30Sjl } 50325cf1a30Sjl 50425cf1a30Sjl if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS) 50525cf1a30Sjl return (DDI_FAILURE); 50625cf1a30Sjl 50725cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 50825cf1a30Sjl goto bad; 50925cf1a30Sjl } 51025cf1a30Sjl 5110cc8ae86Sav if (mc_timeout_period == 0) { 5120cc8ae86Sav mc_patrol_interval_sec = (int)ddi_getprop(DDI_DEV_T_ANY, devi, 513d8a0cca9Swh DDI_PROP_DONTPASS, "mc-timeout-interval-sec", 514d8a0cca9Swh mc_patrol_interval_sec); 515d8a0cca9Swh mc_timeout_period = drv_usectohz(1000000 * 516d8a0cca9Swh mc_patrol_interval_sec / OPL_MAX_BOARDS); 5170cc8ae86Sav } 5180cc8ae86Sav 51925cf1a30Sjl /* set informations in mc state */ 52025cf1a30Sjl mcp->mc_dip = devi; 52125cf1a30Sjl 52225cf1a30Sjl if (mc_board_add(mcp)) 52325cf1a30Sjl goto bad; 52425cf1a30Sjl 52525cf1a30Sjl insert_mcp(mcp); 5260cc8ae86Sav 5270cc8ae86Sav /* 5280cc8ae86Sav * Start the polling thread if it is not running already. 5290cc8ae86Sav */ 5300cc8ae86Sav mutex_enter(&mc_polling_lock); 5310cc8ae86Sav if (!mc_pollthr_running) { 5320cc8ae86Sav (void) thread_create(NULL, 0, (void (*)())mc_polling_thread, 533d8a0cca9Swh NULL, 0, &p0, TS_RUN, mc_poll_priority); 5340cc8ae86Sav } 5350cc8ae86Sav mutex_exit(&mc_polling_lock); 53625cf1a30Sjl ddi_report_dev(devi); 53725cf1a30Sjl 53825cf1a30Sjl return (DDI_SUCCESS); 53925cf1a30Sjl 54025cf1a30Sjl bad: 54125cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 54225cf1a30Sjl return (DDI_FAILURE); 54325cf1a30Sjl } 54425cf1a30Sjl 54525cf1a30Sjl /* ARGSUSED */ 54625cf1a30Sjl static int 54725cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 54825cf1a30Sjl { 5490cc8ae86Sav int rv; 55025cf1a30Sjl int instance; 55125cf1a30Sjl mc_opl_t *mcp; 55225cf1a30Sjl 55325cf1a30Sjl /* get the instance of this devi */ 55425cf1a30Sjl instance = ddi_get_instance(devi); 55525cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 55625cf1a30Sjl return (DDI_FAILURE); 55725cf1a30Sjl } 55825cf1a30Sjl 55925cf1a30Sjl switch (cmd) { 56025cf1a30Sjl case DDI_SUSPEND: 5610cc8ae86Sav rv = mc_suspend(mcp, MC_DRIVER_SUSPENDED); 5620cc8ae86Sav return (rv); 56325cf1a30Sjl case DDI_DETACH: 56425cf1a30Sjl break; 56525cf1a30Sjl default: 56625cf1a30Sjl return (DDI_FAILURE); 56725cf1a30Sjl } 56825cf1a30Sjl 5690cc8ae86Sav delete_mcp(mcp); 57025cf1a30Sjl if (mc_board_del(mcp) != DDI_SUCCESS) { 57125cf1a30Sjl return (DDI_FAILURE); 57225cf1a30Sjl } 57325cf1a30Sjl 57425cf1a30Sjl /* free up the soft state */ 57525cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 57625cf1a30Sjl 57725cf1a30Sjl return (DDI_SUCCESS); 57825cf1a30Sjl } 57925cf1a30Sjl 58025cf1a30Sjl /* ARGSUSED */ 58125cf1a30Sjl static int 58225cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 58325cf1a30Sjl { 58425cf1a30Sjl return (0); 58525cf1a30Sjl } 58625cf1a30Sjl 58725cf1a30Sjl /* ARGSUSED */ 58825cf1a30Sjl static int 58925cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp) 59025cf1a30Sjl { 59125cf1a30Sjl return (0); 59225cf1a30Sjl } 59325cf1a30Sjl 59425cf1a30Sjl /* ARGSUSED */ 59525cf1a30Sjl static int 59625cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 59725cf1a30Sjl int *rvalp) 59825cf1a30Sjl { 5990cc8ae86Sav #ifdef DEBUG 6000cc8ae86Sav return (mc_ioctl_debug(dev, cmd, arg, mode, credp, rvalp)); 6010cc8ae86Sav #else 60225cf1a30Sjl return (ENXIO); 6030cc8ae86Sav #endif 60425cf1a30Sjl } 60525cf1a30Sjl 60625cf1a30Sjl /* 60725cf1a30Sjl * PA validity check: 608738dd194Shyw * This function return 1 if the PA is a valid PA 609738dd194Shyw * in the running Solaris instance i.e. in physinstall 610738dd194Shyw * Otherwise, return 0. 61125cf1a30Sjl */ 61225cf1a30Sjl 61325cf1a30Sjl /* ARGSUSED */ 61425cf1a30Sjl static int 61525cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr) 61625cf1a30Sjl { 61725cf1a30Sjl if (mcp->mlist == NULL) 61825cf1a30Sjl mc_get_mlist(mcp); 61925cf1a30Sjl 62025cf1a30Sjl if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) { 62125cf1a30Sjl return (1); 62225cf1a30Sjl } 62325cf1a30Sjl return (0); 62425cf1a30Sjl } 62525cf1a30Sjl 62625cf1a30Sjl /* 62725cf1a30Sjl * mac-pa translation routines. 62825cf1a30Sjl * 62925cf1a30Sjl * Input: mc driver state, (LSB#, Bank#, DIMM address) 63025cf1a30Sjl * Output: physical address 63125cf1a30Sjl * 63225cf1a30Sjl * Valid - return value: 0 63325cf1a30Sjl * Invalid - return value: -1 63425cf1a30Sjl */ 63525cf1a30Sjl static int 63625cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa) 63725cf1a30Sjl { 63825cf1a30Sjl int i; 63925cf1a30Sjl uint64_t pa_offset = 0; 64025cf1a30Sjl int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1; 64125cf1a30Sjl int bank = maddr->ma_bank; 64225cf1a30Sjl mc_addr_t maddr1; 64325cf1a30Sjl int bank0, bank1; 64425cf1a30Sjl 64525cf1a30Sjl MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank, 646d8a0cca9Swh maddr->ma_dimm_addr); 64725cf1a30Sjl 64825cf1a30Sjl /* loc validity check */ 64925cf1a30Sjl ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd); 65025cf1a30Sjl ASSERT(bank >= 0 && OPL_BANK_MAX > bank); 65125cf1a30Sjl 65225cf1a30Sjl /* Do translation */ 65325cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 65425cf1a30Sjl int pa_bit = 0; 65525cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 65625cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 65725cf1a30Sjl pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1; 65825cf1a30Sjl } else if (mc_bit == MP_NONE) { 65925cf1a30Sjl pa_bit = 0; 66025cf1a30Sjl } else if (mc_bit == MP_BANK_0) { 66125cf1a30Sjl pa_bit = bank & 1; 66225cf1a30Sjl } else if (mc_bit == MP_BANK_1) { 66325cf1a30Sjl pa_bit = (bank >> 1) & 1; 66425cf1a30Sjl } else if (mc_bit == MP_BANK_2) { 66525cf1a30Sjl pa_bit = (bank >> 2) & 1; 66625cf1a30Sjl } 66725cf1a30Sjl pa_offset |= ((uint64_t)pa_bit) << i; 66825cf1a30Sjl } 66925cf1a30Sjl *pa = mcp->mc_start_address + pa_offset; 67025cf1a30Sjl MC_LOG("pa = %lx\n", *pa); 67125cf1a30Sjl 67225cf1a30Sjl if (pa_to_maddr(mcp, *pa, &maddr1) == -1) { 6730cc8ae86Sav cmn_err(CE_WARN, "mcaddr_to_pa: /LSB%d/B%d/%x failed to " 6740cc8ae86Sav "convert PA %lx\n", maddr->ma_bd, bank, 6750cc8ae86Sav maddr->ma_dimm_addr, *pa); 67625cf1a30Sjl return (-1); 67725cf1a30Sjl } 67825cf1a30Sjl 6790cc8ae86Sav /* 6800cc8ae86Sav * In mirror mode, PA is always translated to the even bank. 6810cc8ae86Sav */ 68225cf1a30Sjl if (IS_MIRROR(mcp, maddr->ma_bank)) { 68325cf1a30Sjl bank0 = maddr->ma_bank & ~(1); 68425cf1a30Sjl bank1 = maddr1.ma_bank & ~(1); 68525cf1a30Sjl } else { 68625cf1a30Sjl bank0 = maddr->ma_bank; 68725cf1a30Sjl bank1 = maddr1.ma_bank; 68825cf1a30Sjl } 68925cf1a30Sjl /* 69025cf1a30Sjl * there is no need to check ma_bd because it is generated from 69125cf1a30Sjl * mcp. They are the same. 69225cf1a30Sjl */ 693d8a0cca9Swh if ((bank0 == bank1) && (maddr->ma_dimm_addr == 694d8a0cca9Swh maddr1.ma_dimm_addr)) { 69525cf1a30Sjl return (0); 69625cf1a30Sjl } else { 69725cf1a30Sjl cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, " 698d8a0cca9Swh "PA %lx, target /LSB%d/B%d/%x\n", maddr->ma_bd, bank, 699d8a0cca9Swh maddr->ma_dimm_addr, *pa, maddr1.ma_bd, maddr1.ma_bank, 700d8a0cca9Swh maddr1.ma_dimm_addr); 70125cf1a30Sjl return (-1); 70225cf1a30Sjl } 70325cf1a30Sjl } 70425cf1a30Sjl 70525cf1a30Sjl /* 70625cf1a30Sjl * PA to CS (used by pa_to_maddr). 70725cf1a30Sjl */ 70825cf1a30Sjl static int 70925cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset) 71025cf1a30Sjl { 71125cf1a30Sjl int i; 712738dd194Shyw int cs = 1; 71325cf1a30Sjl 71425cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 71525cf1a30Sjl /* MAC address bit<29> is arranged on the same PA bit */ 71625cf1a30Sjl /* on both table. So we may use any table. */ 71725cf1a30Sjl if (mcp->mc_trans_table[0][i] == CS_SHIFT) { 71825cf1a30Sjl cs = (pa_offset >> i) & 1; 71925cf1a30Sjl break; 72025cf1a30Sjl } 72125cf1a30Sjl } 72225cf1a30Sjl return (cs); 72325cf1a30Sjl } 72425cf1a30Sjl 72525cf1a30Sjl /* 72625cf1a30Sjl * PA to DIMM (used by pa_to_maddr). 72725cf1a30Sjl */ 72825cf1a30Sjl /* ARGSUSED */ 72925cf1a30Sjl static uint32_t 73025cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset) 73125cf1a30Sjl { 73225cf1a30Sjl int i; 73325cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 73425cf1a30Sjl uint32_t dimm_addr = 0; 73525cf1a30Sjl 73625cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 73725cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 73825cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 73925cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 74025cf1a30Sjl dimm_addr |= pa_bit_value << mc_bit; 74125cf1a30Sjl } 74225cf1a30Sjl } 743738dd194Shyw dimm_addr |= cs << CS_SHIFT; 74425cf1a30Sjl return (dimm_addr); 74525cf1a30Sjl } 74625cf1a30Sjl 74725cf1a30Sjl /* 74825cf1a30Sjl * PA to Bank (used by pa_to_maddr). 74925cf1a30Sjl */ 75025cf1a30Sjl static int 75125cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset) 75225cf1a30Sjl { 75325cf1a30Sjl int i; 75425cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 75525cf1a30Sjl int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT]; 75625cf1a30Sjl 75725cf1a30Sjl 75825cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 75925cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 76025cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 76125cf1a30Sjl switch (mc_bit) { 76225cf1a30Sjl case MP_BANK_0: 76325cf1a30Sjl bankno |= pa_bit_value; 76425cf1a30Sjl break; 76525cf1a30Sjl case MP_BANK_1: 76625cf1a30Sjl bankno |= pa_bit_value << 1; 76725cf1a30Sjl break; 76825cf1a30Sjl case MP_BANK_2: 76925cf1a30Sjl bankno |= pa_bit_value << 2; 77025cf1a30Sjl break; 77125cf1a30Sjl } 77225cf1a30Sjl } 77325cf1a30Sjl 77425cf1a30Sjl return (bankno); 77525cf1a30Sjl } 77625cf1a30Sjl 77725cf1a30Sjl /* 77825cf1a30Sjl * PA to MAC address translation 77925cf1a30Sjl * 78025cf1a30Sjl * Input: MAC driver state, physicall adress 78125cf1a30Sjl * Output: LSB#, Bank id, mac address 78225cf1a30Sjl * 78325cf1a30Sjl * Valid - return value: 0 78425cf1a30Sjl * Invalid - return value: -1 78525cf1a30Sjl */ 78625cf1a30Sjl 78725cf1a30Sjl int 78825cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr) 78925cf1a30Sjl { 79025cf1a30Sjl uint64_t pa_offset; 79125cf1a30Sjl 792738dd194Shyw if (!mc_rangecheck_pa(mcp, pa)) 79325cf1a30Sjl return (-1); 79425cf1a30Sjl 79525cf1a30Sjl /* Do translation */ 79625cf1a30Sjl pa_offset = pa - mcp->mc_start_address; 79725cf1a30Sjl 79825cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 799aeb241b2Sav maddr->ma_phys_bd = mcp->mc_phys_board_num; 80025cf1a30Sjl maddr->ma_bank = pa_to_bank(mcp, pa_offset); 80125cf1a30Sjl maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset); 802d8a0cca9Swh MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", pa_offset, maddr->ma_bd, 803d8a0cca9Swh maddr->ma_bank, maddr->ma_dimm_addr); 80425cf1a30Sjl return (0); 80525cf1a30Sjl } 80625cf1a30Sjl 8070cc8ae86Sav /* 8080cc8ae86Sav * UNUM format for DC is "/CMUnn/MEMxyZ", where 8090cc8ae86Sav * nn = 00..03 for DC1 and 00..07 for DC2 and 00..15 for DC3. 8100cc8ae86Sav * x = MAC 0..3 8110cc8ae86Sav * y = 0..3 (slot info). 8120cc8ae86Sav * Z = 'A' or 'B' 8130cc8ae86Sav * 8140cc8ae86Sav * UNUM format for FF1 is "/MBU_A/MEMBx/MEMyZ", where 8150cc8ae86Sav * x = 0..3 (MEMB number) 8160cc8ae86Sav * y = 0..3 (slot info). 8170cc8ae86Sav * Z = 'A' or 'B' 8180cc8ae86Sav * 8190cc8ae86Sav * UNUM format for FF2 is "/MBU_B/MEMBx/MEMyZ" 8200cc8ae86Sav * x = 0..7 (MEMB number) 8210cc8ae86Sav * y = 0..3 (slot info). 8220cc8ae86Sav * Z = 'A' or 'B' 8230cc8ae86Sav */ 8240cc8ae86Sav int 825aeb241b2Sav mc_set_mem_unum(char *buf, int buflen, int sb, int bank, 8260cc8ae86Sav uint32_t mf_type, uint32_t d_slot) 8270cc8ae86Sav { 8280cc8ae86Sav char *dimmnm; 8290cc8ae86Sav char memb_num; 830aeb241b2Sav int cs; 8310cc8ae86Sav int i; 832aeb241b2Sav int j; 8330cc8ae86Sav 834aeb241b2Sav cs = SLOT_TO_CS(d_slot); 8350cc8ae86Sav 8360cc8ae86Sav if (plat_model == MODEL_DC) { 837056c948bStsien if (mf_type == FLT_TYPE_INTERMITTENT_CE || 838056c948bStsien mf_type == FLT_TYPE_PERMANENT_CE) { 8390cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 8400cc8ae86Sav dimmnm = mc_dc_dimm_unum_table[i]; 8410cc8ae86Sav snprintf(buf, buflen, "/%s%02d/MEM%s", 8420cc8ae86Sav model_names[plat_model].unit_name, sb, dimmnm); 8430cc8ae86Sav } else { 8440cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 845aeb241b2Sav j = (cs == 0) ? i : i + 2; 846aeb241b2Sav snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s", 8470cc8ae86Sav model_names[plat_model].unit_name, sb, 848aeb241b2Sav mc_dc_dimm_unum_table[j], 849aeb241b2Sav mc_dc_dimm_unum_table[j + 1]); 8500cc8ae86Sav } 8510cc8ae86Sav } else { 852056c948bStsien if (mf_type == FLT_TYPE_INTERMITTENT_CE || 853056c948bStsien mf_type == FLT_TYPE_PERMANENT_CE) { 854aeb241b2Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 8550cc8ae86Sav dimmnm = mc_ff_dimm_unum_table[i]; 8560cc8ae86Sav memb_num = dimmnm[0]; 8570cc8ae86Sav snprintf(buf, buflen, "/%s/%s%c/MEM%s", 8580cc8ae86Sav model_names[plat_model].unit_name, 8590cc8ae86Sav model_names[plat_model].mem_name, 8600cc8ae86Sav memb_num, &dimmnm[1]); 8610cc8ae86Sav } else { 8620cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 863aeb241b2Sav j = (cs == 0) ? i : i + 2; 8640cc8ae86Sav memb_num = mc_ff_dimm_unum_table[i][0], 865d8a0cca9Swh snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s", 8660cc8ae86Sav model_names[plat_model].unit_name, 8670cc8ae86Sav model_names[plat_model].mem_name, memb_num, 868aeb241b2Sav &mc_ff_dimm_unum_table[j][1], 869aeb241b2Sav &mc_ff_dimm_unum_table[j + 1][1]); 8700cc8ae86Sav } 8710cc8ae86Sav } 8720cc8ae86Sav return (0); 8730cc8ae86Sav } 8740cc8ae86Sav 87525cf1a30Sjl static void 87625cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt) 87725cf1a30Sjl { 87825cf1a30Sjl char buf[FM_MAX_CLASS]; 87925cf1a30Sjl char device_path[MAXPATHLEN]; 8800cc8ae86Sav char sid[MAXPATHLEN]; 88125cf1a30Sjl nv_alloc_t *nva = NULL; 88225cf1a30Sjl nvlist_t *ereport, *detector, *resource; 88325cf1a30Sjl errorq_elem_t *eqep; 88425cf1a30Sjl int nflts; 88525cf1a30Sjl mc_flt_stat_t *flt_stat; 8860cc8ae86Sav int i, n; 8870cc8ae86Sav int blen = MAXPATHLEN; 8880cc8ae86Sav char *p, *s = NULL; 88925cf1a30Sjl uint32_t values[2], synd[2], dslot[2]; 8900cc8ae86Sav uint64_t offset = (uint64_t)-1; 8910cc8ae86Sav int ret = -1; 89225cf1a30Sjl 89325cf1a30Sjl if (panicstr) { 89425cf1a30Sjl eqep = errorq_reserve(ereport_errorq); 89525cf1a30Sjl if (eqep == NULL) 89625cf1a30Sjl return; 89725cf1a30Sjl ereport = errorq_elem_nvl(ereport_errorq, eqep); 89825cf1a30Sjl nva = errorq_elem_nva(ereport_errorq, eqep); 89925cf1a30Sjl } else { 90025cf1a30Sjl ereport = fm_nvlist_create(nva); 90125cf1a30Sjl } 90225cf1a30Sjl 90325cf1a30Sjl /* 90425cf1a30Sjl * Create the scheme "dev" FMRI. 90525cf1a30Sjl */ 90625cf1a30Sjl detector = fm_nvlist_create(nva); 90725cf1a30Sjl resource = fm_nvlist_create(nva); 90825cf1a30Sjl 90925cf1a30Sjl nflts = mc_aflt->mflt_nflts; 91025cf1a30Sjl 91125cf1a30Sjl ASSERT(nflts >= 1 && nflts <= 2); 91225cf1a30Sjl 91325cf1a30Sjl flt_stat = mc_aflt->mflt_stat[0]; 91425cf1a30Sjl (void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path); 91525cf1a30Sjl (void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 91625cf1a30Sjl device_path, NULL); 91725cf1a30Sjl 91825cf1a30Sjl /* 91925cf1a30Sjl * Encode all the common data into the ereport. 92025cf1a30Sjl */ 921d8a0cca9Swh (void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", MC_OPL_ERROR_CLASS, 922d8a0cca9Swh mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : MC_OPL_MI_SUBCLASS, 923d8a0cca9Swh mc_aflt->mflt_erpt_class); 92425cf1a30Sjl 92525cf1a30Sjl MC_LOG("mc_ereport_post: ereport %s\n", buf); 92625cf1a30Sjl 92725cf1a30Sjl 92825cf1a30Sjl fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, 929d8a0cca9Swh fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), detector, NULL); 93025cf1a30Sjl 93125cf1a30Sjl /* 93225cf1a30Sjl * Set payload. 93325cf1a30Sjl */ 93425cf1a30Sjl fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32, 935d8a0cca9Swh flt_stat->mf_flt_maddr.ma_bd, NULL); 93625cf1a30Sjl 93725cf1a30Sjl fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64, 938d8a0cca9Swh flt_stat->mf_flt_paddr, NULL); 93925cf1a30Sjl 940056c948bStsien if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE || 941056c948bStsien flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 942d8a0cca9Swh fm_payload_set(ereport, MC_OPL_FLT_TYPE, DATA_TYPE_UINT8, 943d8a0cca9Swh ECC_STICKY, NULL); 94425cf1a30Sjl } 94525cf1a30Sjl 94625cf1a30Sjl for (i = 0; i < nflts; i++) 94725cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank; 94825cf1a30Sjl 949d8a0cca9Swh fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, nflts, 950d8a0cca9Swh values, NULL); 95125cf1a30Sjl 95225cf1a30Sjl for (i = 0; i < nflts; i++) 95325cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_cntl; 95425cf1a30Sjl 955d8a0cca9Swh fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, nflts, 956d8a0cca9Swh values, NULL); 95725cf1a30Sjl 95825cf1a30Sjl for (i = 0; i < nflts; i++) 95925cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_add; 96025cf1a30Sjl 961056c948bStsien /* offset is set only for PCE and ICE */ 962056c948bStsien if (mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_INTERMITTENT_CE || 963056c948bStsien mc_aflt->mflt_stat[0]->mf_type == FLT_TYPE_PERMANENT_CE) { 9640cc8ae86Sav offset = values[0]; 9650cc8ae86Sav 9660cc8ae86Sav } 967d8a0cca9Swh fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, nflts, 968d8a0cca9Swh values, NULL); 96925cf1a30Sjl 97025cf1a30Sjl for (i = 0; i < nflts; i++) 97125cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_log; 97225cf1a30Sjl 973d8a0cca9Swh fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, nflts, 974d8a0cca9Swh values, NULL); 97525cf1a30Sjl 97625cf1a30Sjl for (i = 0; i < nflts; i++) { 97725cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 97825cf1a30Sjl if (flt_stat->mf_errlog_valid) { 97925cf1a30Sjl synd[i] = flt_stat->mf_synd; 98025cf1a30Sjl dslot[i] = flt_stat->mf_dimm_slot; 98125cf1a30Sjl values[i] = flt_stat->mf_dram_place; 98225cf1a30Sjl } else { 98325cf1a30Sjl synd[i] = 0; 98425cf1a30Sjl dslot[i] = 0; 98525cf1a30Sjl values[i] = 0; 98625cf1a30Sjl } 98725cf1a30Sjl } 98825cf1a30Sjl 989d8a0cca9Swh fm_payload_set(ereport, MC_OPL_ERR_SYND, DATA_TYPE_UINT32_ARRAY, nflts, 990d8a0cca9Swh synd, NULL); 99125cf1a30Sjl 992d8a0cca9Swh fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, DATA_TYPE_UINT32_ARRAY, 993d8a0cca9Swh nflts, dslot, NULL); 99425cf1a30Sjl 995d8a0cca9Swh fm_payload_set(ereport, MC_OPL_ERR_DRAM, DATA_TYPE_UINT32_ARRAY, nflts, 996d8a0cca9Swh values, NULL); 99725cf1a30Sjl 99825cf1a30Sjl device_path[0] = 0; 99925cf1a30Sjl p = &device_path[0]; 10000cc8ae86Sav sid[0] = 0; 10010cc8ae86Sav s = &sid[0]; 10020cc8ae86Sav ret = 0; 100325cf1a30Sjl 100425cf1a30Sjl for (i = 0; i < nflts; i++) { 10050cc8ae86Sav int bank; 100625cf1a30Sjl 100725cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 10080cc8ae86Sav bank = flt_stat->mf_flt_maddr.ma_bank; 1009d8a0cca9Swh ret = mc_set_mem_unum(p + strlen(p), blen, 1010d8a0cca9Swh flt_stat->mf_flt_maddr.ma_phys_bd, bank, flt_stat->mf_type, 1011d8a0cca9Swh flt_stat->mf_dimm_slot); 10120cc8ae86Sav 10130cc8ae86Sav if (ret != 0) { 10140cc8ae86Sav cmn_err(CE_WARN, 10150cc8ae86Sav "mc_ereport_post: Failed to determine the unum " 10160cc8ae86Sav "for board=%d bank=%d type=0x%x slot=0x%x", 10170cc8ae86Sav flt_stat->mf_flt_maddr.ma_bd, bank, 10180cc8ae86Sav flt_stat->mf_type, flt_stat->mf_dimm_slot); 10190cc8ae86Sav continue; 102025cf1a30Sjl } 10210cc8ae86Sav n = strlen(device_path); 102225cf1a30Sjl blen = MAXPATHLEN - n; 102325cf1a30Sjl p = &device_path[n]; 102425cf1a30Sjl if (i < (nflts - 1)) { 102525cf1a30Sjl snprintf(p, blen, " "); 10260cc8ae86Sav blen--; 10270cc8ae86Sav p++; 10280cc8ae86Sav } 10290cc8ae86Sav 10300cc8ae86Sav if (ret == 0) { 10310cc8ae86Sav ret = mc_set_mem_sid(mc_aflt->mflt_mcp, s + strlen(s), 1032aeb241b2Sav blen, flt_stat->mf_flt_maddr.ma_phys_bd, bank, 10330cc8ae86Sav flt_stat->mf_type, flt_stat->mf_dimm_slot); 10340cc8ae86Sav 103525cf1a30Sjl } 103625cf1a30Sjl } 103725cf1a30Sjl 1038d8a0cca9Swh (void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, NULL, 1039d8a0cca9Swh device_path, (ret == 0) ? sid : NULL, (ret == 0) ? offset : 1040d8a0cca9Swh (uint64_t)-1); 104125cf1a30Sjl 1042d8a0cca9Swh fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, resource, 1043d8a0cca9Swh NULL); 104425cf1a30Sjl 104525cf1a30Sjl if (panicstr) { 104625cf1a30Sjl errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); 104725cf1a30Sjl } else { 104825cf1a30Sjl (void) fm_ereport_post(ereport, EVCH_TRYHARD); 104925cf1a30Sjl fm_nvlist_destroy(ereport, FM_NVA_FREE); 105025cf1a30Sjl fm_nvlist_destroy(detector, FM_NVA_FREE); 105125cf1a30Sjl fm_nvlist_destroy(resource, FM_NVA_FREE); 105225cf1a30Sjl } 105325cf1a30Sjl } 105425cf1a30Sjl 10550cc8ae86Sav 105625cf1a30Sjl static void 105725cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt) 105825cf1a30Sjl { 105925cf1a30Sjl int rv; 106025cf1a30Sjl uint64_t pa = (uint64_t)(-1); 10610cc8ae86Sav int i; 106225cf1a30Sjl 1063d8a0cca9Swh MC_LOG("mc_err_drain: %s\n", mc_aflt->mflt_erpt_class); 106425cf1a30Sjl /* 106525cf1a30Sjl * we come here only when we have: 10661039f409Sav * In mirror mode: MUE, SUE 1067056c948bStsien * In normal mode: UE, Permanent CE, Intermittent CE 106825cf1a30Sjl */ 10690cc8ae86Sav for (i = 0; i < mc_aflt->mflt_nflts; i++) { 10700cc8ae86Sav rv = mcaddr_to_pa(mc_aflt->mflt_mcp, 1071d8a0cca9Swh &(mc_aflt->mflt_stat[i]->mf_flt_maddr), &pa); 1072738dd194Shyw 1073738dd194Shyw /* Ensure the pa is valid (not in isolated memory block) */ 1074738dd194Shyw if (rv == 0 && pa_is_valid(mc_aflt->mflt_mcp, pa)) 10750cc8ae86Sav mc_aflt->mflt_stat[i]->mf_flt_paddr = pa; 10760cc8ae86Sav else 10770cc8ae86Sav mc_aflt->mflt_stat[i]->mf_flt_paddr = (uint64_t)-1; 10780cc8ae86Sav } 10790cc8ae86Sav 1080738dd194Shyw MC_LOG("mc_err_drain:pa = %lx\n", pa); 108125cf1a30Sjl 1082738dd194Shyw switch (page_retire_check(pa, NULL)) { 1083738dd194Shyw case 0: 1084738dd194Shyw case EAGAIN: 1085738dd194Shyw MC_LOG("Page retired or pending\n"); 1086738dd194Shyw return; 1087738dd194Shyw case EIO: 1088738dd194Shyw /* 1089056c948bStsien * Do page retirement except for the PCE and ICE cases. 1090738dd194Shyw * This is taken care by the OPL DE 1091738dd194Shyw */ 1092056c948bStsien if (mc_aflt->mflt_stat[0]->mf_type != 1093056c948bStsien FLT_TYPE_INTERMITTENT_CE && 1094056c948bStsien mc_aflt->mflt_stat[0]->mf_type != FLT_TYPE_PERMANENT_CE) { 1095738dd194Shyw MC_LOG("offline page at pa %lx error %x\n", pa, 1096d8a0cca9Swh mc_aflt->mflt_pr); 1097738dd194Shyw (void) page_retire(pa, mc_aflt->mflt_pr); 109825cf1a30Sjl } 1099738dd194Shyw break; 1100738dd194Shyw case EINVAL: 1101738dd194Shyw default: 1102738dd194Shyw /* 1103738dd194Shyw * Some memory do not have page structure so 1104738dd194Shyw * we keep going in case of EINVAL. 1105738dd194Shyw */ 1106738dd194Shyw break; 110725cf1a30Sjl } 110825cf1a30Sjl 11090cc8ae86Sav for (i = 0; i < mc_aflt->mflt_nflts; i++) { 11100cc8ae86Sav mc_aflt_t mc_aflt0; 11110cc8ae86Sav if (mc_aflt->mflt_stat[i]->mf_flt_paddr != (uint64_t)-1) { 11120cc8ae86Sav mc_aflt0 = *mc_aflt; 11130cc8ae86Sav mc_aflt0.mflt_nflts = 1; 11140cc8ae86Sav mc_aflt0.mflt_stat[0] = mc_aflt->mflt_stat[i]; 11150cc8ae86Sav mc_ereport_post(&mc_aflt0); 11160cc8ae86Sav } 11170cc8ae86Sav } 11180cc8ae86Sav } 111925cf1a30Sjl 112025cf1a30Sjl /* 112125cf1a30Sjl * The restart address is actually defined in unit of PA[37:6] 112225cf1a30Sjl * the mac patrol will convert that to dimm offset. If the 112325cf1a30Sjl * address is not in the bank, it will continue to search for 112425cf1a30Sjl * the next PA that is within the bank. 112525cf1a30Sjl * 112625cf1a30Sjl * Also the mac patrol scans the dimms based on PA, not 112725cf1a30Sjl * dimm offset. 112825cf1a30Sjl */ 112925cf1a30Sjl static int 1130738dd194Shyw restart_patrol(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr_info) 113125cf1a30Sjl { 113225cf1a30Sjl uint64_t pa; 113325cf1a30Sjl int rv; 113425cf1a30Sjl 1135*601c2e1eSdhain if (MC_REWRITE_MODE(mcp, bank)) { 1136*601c2e1eSdhain return (0); 1137*601c2e1eSdhain } 1138738dd194Shyw if (rsaddr_info == NULL || (rsaddr_info->mi_valid == 0)) { 113925cf1a30Sjl MAC_PTRL_START(mcp, bank); 114025cf1a30Sjl return (0); 114125cf1a30Sjl } 114225cf1a30Sjl 1143738dd194Shyw rv = mcaddr_to_pa(mcp, &rsaddr_info->mi_restartaddr, &pa); 114425cf1a30Sjl if (rv != 0) { 114525cf1a30Sjl MC_LOG("cannot convert mcaddr to pa. use auto restart\n"); 114625cf1a30Sjl MAC_PTRL_START(mcp, bank); 114725cf1a30Sjl return (0); 114825cf1a30Sjl } 114925cf1a30Sjl 1150738dd194Shyw if (!mc_rangecheck_pa(mcp, pa)) { 115125cf1a30Sjl /* pa is not on this board, just retry */ 115225cf1a30Sjl cmn_err(CE_WARN, "restart_patrol: invalid address %lx " 1153d8a0cca9Swh "on board %d\n", pa, mcp->mc_board_num); 115425cf1a30Sjl MAC_PTRL_START(mcp, bank); 115525cf1a30Sjl return (0); 115625cf1a30Sjl } 115725cf1a30Sjl 115825cf1a30Sjl MC_LOG("restart_patrol: pa = %lx\n", pa); 115925cf1a30Sjl 1160738dd194Shyw if (!rsaddr_info->mi_injectrestart) { 1161738dd194Shyw /* 11621039f409Sav * For non-error injection restart we need to 1163738dd194Shyw * determine if the current restart pa/page is 1164738dd194Shyw * a "good" page. A "good" page is a page that 1165738dd194Shyw * has not been page retired. If the current 1166738dd194Shyw * page that contains the pa is "good", we will 1167738dd194Shyw * do a HW auto restart and let HW patrol continue 1168738dd194Shyw * where it last stopped. Most desired scenario. 1169738dd194Shyw * 1170738dd194Shyw * If the current page is not "good", we will advance 1171738dd194Shyw * to the next page to find the next "good" page and 1172738dd194Shyw * restart the patrol from there. 1173738dd194Shyw */ 1174738dd194Shyw int wrapcount = 0; 1175738dd194Shyw uint64_t origpa = pa; 1176738dd194Shyw while (wrapcount < 2) { 1177d8a0cca9Swh if (!pa_is_valid(mcp, pa)) { 1178*601c2e1eSdhain /* 1179*601c2e1eSdhain * Not in physinstall - advance to the 1180*601c2e1eSdhain * next memory isolation blocksize 1181*601c2e1eSdhain */ 1182*601c2e1eSdhain MC_LOG("Invalid PA\n"); 1183*601c2e1eSdhain pa = roundup(pa + 1, mc_isolation_bsize); 1184d8a0cca9Swh } else { 1185*601c2e1eSdhain int rv; 1186*601c2e1eSdhain if ((rv = page_retire_check(pa, NULL)) != 0 && 1187*601c2e1eSdhain rv != EAGAIN) { 1188d8a0cca9Swh /* 1189d8a0cca9Swh * The page is "good" (not retired), 1190d8a0cca9Swh * we will use automatic HW restart 1191d8a0cca9Swh * algorithm if this is the original 1192d8a0cca9Swh * current starting page. 1193d8a0cca9Swh */ 1194*601c2e1eSdhain if (pa == origpa) { 1195*601c2e1eSdhain MC_LOG("Page has no error. " 1196*601c2e1eSdhain "Auto restart\n"); 1197*601c2e1eSdhain MAC_PTRL_START(mcp, bank); 1198*601c2e1eSdhain return (0); 1199*601c2e1eSdhain } else { 1200*601c2e1eSdhain /* 1201*601c2e1eSdhain * found a subsequent good page 1202*601c2e1eSdhain */ 1203*601c2e1eSdhain break; 1204738dd194Shyw } 1205*601c2e1eSdhain } 1206738dd194Shyw 1207*601c2e1eSdhain /* 1208*601c2e1eSdhain * Skip to the next page 1209*601c2e1eSdhain */ 1210*601c2e1eSdhain pa = roundup(pa + 1, PAGESIZE); 1211*601c2e1eSdhain MC_LOG("Skipping bad page to %lx\n", pa); 1212d8a0cca9Swh } 1213738dd194Shyw 1214*601c2e1eSdhain /* Check to see if we hit the end of the memory range */ 1215d8a0cca9Swh if (pa >= (mcp->mc_start_address + mcp->mc_size)) { 1216*601c2e1eSdhain MC_LOG("Wrap around\n"); 1217*601c2e1eSdhain pa = mcp->mc_start_address; 1218*601c2e1eSdhain wrapcount++; 1219d8a0cca9Swh } 1220738dd194Shyw } 1221738dd194Shyw 1222738dd194Shyw if (wrapcount > 1) { 1223d8a0cca9Swh MC_LOG("Failed to find a good page. Just restart\n"); 1224d8a0cca9Swh MAC_PTRL_START(mcp, bank); 1225d8a0cca9Swh return (0); 122625cf1a30Sjl } 122725cf1a30Sjl } 122825cf1a30Sjl 1229738dd194Shyw /* 1230738dd194Shyw * We reached here either: 1231738dd194Shyw * 1. We are doing an error injection restart that specify 1232738dd194Shyw * the exact pa/page to restart. OR 1233738dd194Shyw * 2. We found a subsequent good page different from the 1234738dd194Shyw * original restart pa/page. 1235738dd194Shyw * Restart MAC patrol: PA[37:6] 1236738dd194Shyw */ 123725cf1a30Sjl MC_LOG("restart at pa = %lx\n", pa); 123825cf1a30Sjl ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa)); 123925cf1a30Sjl MAC_PTRL_START_ADD(mcp, bank); 124025cf1a30Sjl 124125cf1a30Sjl return (0); 124225cf1a30Sjl } 124325cf1a30Sjl 1244*601c2e1eSdhain static void 1245*601c2e1eSdhain mc_retry_info_put(mc_retry_info_t **q, mc_retry_info_t *p) 1246*601c2e1eSdhain { 1247*601c2e1eSdhain ASSERT(p != NULL); 1248*601c2e1eSdhain p->ri_next = *q; 1249*601c2e1eSdhain *q = p; 1250*601c2e1eSdhain } 1251*601c2e1eSdhain 1252*601c2e1eSdhain static mc_retry_info_t * 1253*601c2e1eSdhain mc_retry_info_get(mc_retry_info_t **q) 1254*601c2e1eSdhain { 1255*601c2e1eSdhain mc_retry_info_t *p; 1256*601c2e1eSdhain 1257*601c2e1eSdhain if ((p = *q) != NULL) { 1258*601c2e1eSdhain *q = p->ri_next; 1259*601c2e1eSdhain return (p); 1260*601c2e1eSdhain } else { 1261*601c2e1eSdhain return (NULL); 1262*601c2e1eSdhain } 1263*601c2e1eSdhain } 1264*601c2e1eSdhain 126525cf1a30Sjl /* 126625cf1a30Sjl * Rewriting is used for two purposes. 126725cf1a30Sjl * - to correct the error in memory. 126825cf1a30Sjl * - to determine whether the error is permanent or intermittent. 126925cf1a30Sjl * It's done by writing the address in MAC_BANKm_REWRITE_ADD 127025cf1a30Sjl * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that, 127125cf1a30Sjl * REW_END (and REW_CE/REW_UE if some error detected) is set when 127225cf1a30Sjl * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM. 127325cf1a30Sjl * 127425cf1a30Sjl * Note that rewrite operation doesn't change RAW_UE to Marked UE. 127525cf1a30Sjl * Therefore, we use it only CE case. 127625cf1a30Sjl */ 1277*601c2e1eSdhain 127825cf1a30Sjl static uint32_t 1279*601c2e1eSdhain do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr, int retrying) 128025cf1a30Sjl { 128125cf1a30Sjl uint32_t cntl; 128225cf1a30Sjl int count = 0; 1283*601c2e1eSdhain int max_count; 1284*601c2e1eSdhain int retry_state; 1285*601c2e1eSdhain 1286*601c2e1eSdhain if (retrying) 1287*601c2e1eSdhain max_count = 1; 1288*601c2e1eSdhain else 1289*601c2e1eSdhain max_count = mc_max_rewrite_loop; 1290*601c2e1eSdhain 1291*601c2e1eSdhain retry_state = RETRY_STATE_PENDING; 1292*601c2e1eSdhain 1293*601c2e1eSdhain if (!retrying && MC_REWRITE_MODE(mcp, bank)) { 1294*601c2e1eSdhain goto timeout; 1295*601c2e1eSdhain } 1296*601c2e1eSdhain 1297*601c2e1eSdhain retry_state = RETRY_STATE_ACTIVE; 129825cf1a30Sjl 129925cf1a30Sjl /* first wait to make sure PTRL_STATUS is 0 */ 1300*601c2e1eSdhain while (count++ < max_count) { 130125cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 1302*601c2e1eSdhain if (!(cntl & MAC_CNTL_PTRL_STATUS)) { 1303*601c2e1eSdhain count = 0; 130425cf1a30Sjl break; 1305*601c2e1eSdhain } 13060cc8ae86Sav drv_usecwait(mc_rewrite_delay); 130725cf1a30Sjl } 1308*601c2e1eSdhain if (count >= max_count) 1309*601c2e1eSdhain goto timeout; 131025cf1a30Sjl 131125cf1a30Sjl count = 0; 131225cf1a30Sjl 131325cf1a30Sjl ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr); 131425cf1a30Sjl MAC_REW_REQ(mcp, bank); 131525cf1a30Sjl 1316*601c2e1eSdhain retry_state = RETRY_STATE_REWRITE; 1317*601c2e1eSdhain 131825cf1a30Sjl do { 1319*601c2e1eSdhain if (count++ > max_count) { 1320*601c2e1eSdhain goto timeout; 13210cc8ae86Sav } else { 13220cc8ae86Sav drv_usecwait(mc_rewrite_delay); 13230cc8ae86Sav } 1324*601c2e1eSdhain cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 132525cf1a30Sjl /* 132625cf1a30Sjl * If there are other MEMORY or PCI activities, this 132725cf1a30Sjl * will be BUSY, else it should be set immediately 132825cf1a30Sjl */ 132925cf1a30Sjl } while (!(cntl & MAC_CNTL_REW_END)); 133025cf1a30Sjl 133125cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 133225cf1a30Sjl return (cntl); 1333*601c2e1eSdhain timeout: 1334*601c2e1eSdhain mc_set_rewrite(mcp, bank, dimm_addr, retry_state); 1335*601c2e1eSdhain 1336*601c2e1eSdhain return (0); 1337*601c2e1eSdhain } 1338*601c2e1eSdhain 1339*601c2e1eSdhain void 1340*601c2e1eSdhain mc_clear_rewrite(mc_opl_t *mcp, int bank) 1341*601c2e1eSdhain { 1342*601c2e1eSdhain struct mc_bank *bankp; 1343*601c2e1eSdhain mc_retry_info_t *retry; 1344*601c2e1eSdhain uint32_t rew_addr; 1345*601c2e1eSdhain 1346*601c2e1eSdhain bankp = &(mcp->mc_bank[bank]); 1347*601c2e1eSdhain retry = bankp->mcb_active; 1348*601c2e1eSdhain bankp->mcb_active = NULL; 1349*601c2e1eSdhain mc_retry_info_put(&bankp->mcb_retry_freelist, retry); 1350*601c2e1eSdhain 1351*601c2e1eSdhain again: 1352*601c2e1eSdhain bankp->mcb_rewrite_count = 0; 1353*601c2e1eSdhain 1354*601c2e1eSdhain while (retry = mc_retry_info_get(&bankp->mcb_retry_pending)) { 1355*601c2e1eSdhain rew_addr = retry->ri_addr; 1356*601c2e1eSdhain mc_retry_info_put(&bankp->mcb_retry_freelist, retry); 1357*601c2e1eSdhain if (do_rewrite(mcp, bank, rew_addr, 1) == 0) 1358*601c2e1eSdhain break; 1359*601c2e1eSdhain } 1360*601c2e1eSdhain 1361*601c2e1eSdhain /* we break out if no more pending rewrite or we got timeout again */ 1362*601c2e1eSdhain 1363*601c2e1eSdhain if (!bankp->mcb_active && !bankp->mcb_retry_pending) { 1364*601c2e1eSdhain if (!IS_MIRROR(mcp, bank)) { 1365*601c2e1eSdhain MC_CLEAR_REWRITE_MODE(mcp, bank); 1366*601c2e1eSdhain } else { 1367*601c2e1eSdhain int mbank = bank ^ 1; 1368*601c2e1eSdhain bankp = &(mcp->mc_bank[mbank]); 1369*601c2e1eSdhain if (!bankp->mcb_active && !bankp->mcb_retry_pending) { 1370*601c2e1eSdhain MC_CLEAR_REWRITE_MODE(mcp, bank); 1371*601c2e1eSdhain MC_CLEAR_REWRITE_MODE(mcp, mbank); 1372*601c2e1eSdhain } else { 1373*601c2e1eSdhain bank = mbank; 1374*601c2e1eSdhain goto again; 1375*601c2e1eSdhain } 1376*601c2e1eSdhain } 1377*601c2e1eSdhain } 137825cf1a30Sjl } 1379*601c2e1eSdhain 1380*601c2e1eSdhain void 1381*601c2e1eSdhain mc_set_rewrite(mc_opl_t *mcp, int bank, uint32_t addr, int state) 1382*601c2e1eSdhain { 1383*601c2e1eSdhain mc_retry_info_t *retry; 1384*601c2e1eSdhain struct mc_bank *bankp; 1385*601c2e1eSdhain 1386*601c2e1eSdhain bankp = &mcp->mc_bank[bank]; 1387*601c2e1eSdhain 1388*601c2e1eSdhain retry = mc_retry_info_get(&bankp->mcb_retry_freelist); 1389*601c2e1eSdhain 1390*601c2e1eSdhain ASSERT(retry != NULL); 1391*601c2e1eSdhain 1392*601c2e1eSdhain retry->ri_addr = addr; 1393*601c2e1eSdhain retry->ri_state = state; 1394*601c2e1eSdhain 1395*601c2e1eSdhain MC_SET_REWRITE_MODE(mcp, bank); 1396*601c2e1eSdhain 1397*601c2e1eSdhain if ((state > RETRY_STATE_PENDING)) { 1398*601c2e1eSdhain ASSERT(bankp->mcb_active == NULL); 1399*601c2e1eSdhain bankp->mcb_active = retry; 1400*601c2e1eSdhain } else { 1401*601c2e1eSdhain mc_retry_info_put(&bankp->mcb_retry_pending, retry); 1402*601c2e1eSdhain } 1403*601c2e1eSdhain 1404*601c2e1eSdhain if (IS_MIRROR(mcp, bank)) { 1405*601c2e1eSdhain int mbank = bank ^1; 1406*601c2e1eSdhain MC_SET_REWRITE_MODE(mcp, mbank); 1407*601c2e1eSdhain } 1408*601c2e1eSdhain } 1409*601c2e1eSdhain 141025cf1a30Sjl void 141125cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp) 141225cf1a30Sjl { 14130cc8ae86Sav int count; 14140cc8ae86Sav int n = 0; 141525cf1a30Sjl scf_log_t *p; 141625cf1a30Sjl int bank; 141725cf1a30Sjl 14180cc8ae86Sav for (bank = 0; bank < BANKNUM_PER_SB; bank++) { 1419d8a0cca9Swh while ((p = mcp->mc_scf_log[bank]) != NULL && 1420d8a0cca9Swh (n < mc_max_errlog_processed)) { 1421*601c2e1eSdhain ASSERT(bank == p->sl_bank); 1422*601c2e1eSdhain count = 0; 1423*601c2e1eSdhain while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank)) 1424*601c2e1eSdhain & MAC_STATIC_ERR_VLD)) { 1425*601c2e1eSdhain if (count++ >= (mc_max_scf_loop)) { 1426*601c2e1eSdhain break; 142725cf1a30Sjl } 1428*601c2e1eSdhain drv_usecwait(mc_scf_delay); 1429*601c2e1eSdhain } 143025cf1a30Sjl 1431*601c2e1eSdhain if (count < mc_max_scf_loop) { 1432*601c2e1eSdhain ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank), 1433*601c2e1eSdhain p->sl_err_log); 143425cf1a30Sjl 1435*601c2e1eSdhain ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank), 1436*601c2e1eSdhain p->sl_err_add|MAC_STATIC_ERR_VLD); 1437*601c2e1eSdhain mcp->mc_scf_retry[bank] = 0; 1438*601c2e1eSdhain } else { 1439*601c2e1eSdhain /* 1440*601c2e1eSdhain * if we try too many times, just drop the req 1441*601c2e1eSdhain */ 1442*601c2e1eSdhain if (mcp->mc_scf_retry[bank]++ <= 1443*601c2e1eSdhain mc_max_scf_retry) { 1444*601c2e1eSdhain return; 144525cf1a30Sjl } else { 1446*601c2e1eSdhain if ((++mc_pce_dropped & 0xff) == 0) { 1447*601c2e1eSdhain cmn_err(CE_WARN, "Cannot " 1448*601c2e1eSdhain "report Permanent CE to " 1449*601c2e1eSdhain "SCF\n"); 1450d8a0cca9Swh } 145125cf1a30Sjl } 1452*601c2e1eSdhain } 1453*601c2e1eSdhain n++; 1454*601c2e1eSdhain mcp->mc_scf_log[bank] = p->sl_next; 1455*601c2e1eSdhain mcp->mc_scf_total[bank]--; 1456*601c2e1eSdhain ASSERT(mcp->mc_scf_total[bank] >= 0); 1457*601c2e1eSdhain kmem_free(p, sizeof (scf_log_t)); 145825cf1a30Sjl } 145925cf1a30Sjl } 146025cf1a30Sjl } 146125cf1a30Sjl void 146225cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank) 146325cf1a30Sjl { 146425cf1a30Sjl scf_log_t *p; 146525cf1a30Sjl 14660cc8ae86Sav if (mcp->mc_scf_total[bank] >= mc_max_scf_logs) { 14670cc8ae86Sav if ((++mc_pce_dropped & 0xff) == 0) { 1468d8a0cca9Swh cmn_err(CE_WARN, "Too many Permanent CE requests.\n"); 14690cc8ae86Sav } 147025cf1a30Sjl return; 147125cf1a30Sjl } 147225cf1a30Sjl p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP); 147325cf1a30Sjl p->sl_next = 0; 147425cf1a30Sjl p->sl_err_add = flt_stat->mf_err_add; 147525cf1a30Sjl p->sl_err_log = flt_stat->mf_err_log; 147625cf1a30Sjl p->sl_bank = bank; 147725cf1a30Sjl 14780cc8ae86Sav if (mcp->mc_scf_log[bank] == NULL) { 147925cf1a30Sjl /* 148025cf1a30Sjl * we rely on mc_scf_log to detect NULL queue. 148125cf1a30Sjl * mc_scf_log_tail is irrelevant is such case. 148225cf1a30Sjl */ 14830cc8ae86Sav mcp->mc_scf_log_tail[bank] = mcp->mc_scf_log[bank] = p; 148425cf1a30Sjl } else { 14850cc8ae86Sav mcp->mc_scf_log_tail[bank]->sl_next = p; 14860cc8ae86Sav mcp->mc_scf_log_tail[bank] = p; 148725cf1a30Sjl } 14880cc8ae86Sav mcp->mc_scf_total[bank]++; 148925cf1a30Sjl } 149025cf1a30Sjl /* 149125cf1a30Sjl * This routine determines what kind of CE happens, intermittent 149225cf1a30Sjl * or permanent as follows. (See 4.7.3 in Columbus2 PRM.) 149325cf1a30Sjl * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register. 149425cf1a30Sjl * - If CE is still detected on the same address even after doing 149525cf1a30Sjl * rewrite operation twice, it is determined as permanent error. 149625cf1a30Sjl * - If error is not detected anymore, it is determined as intermittent 149725cf1a30Sjl * error. 149825cf1a30Sjl * - If UE is detected due to rewrite operation, it should be treated 149925cf1a30Sjl * as UE. 150025cf1a30Sjl */ 150125cf1a30Sjl 150225cf1a30Sjl /* ARGSUSED */ 150325cf1a30Sjl static void 150425cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error) 150525cf1a30Sjl { 150625cf1a30Sjl uint32_t cntl; 150725cf1a30Sjl int i; 150825cf1a30Sjl 150925cf1a30Sjl flt_stat->mf_type = FLT_TYPE_PERMANENT_CE; 151025cf1a30Sjl /* 151125cf1a30Sjl * rewrite request 1st time reads and correct error data 151225cf1a30Sjl * and write to DIMM. 2nd rewrite request must be issued 151325cf1a30Sjl * after REW_CE/UE/END is 0. When the 2nd request is completed, 151425cf1a30Sjl * if REW_CE = 1, then it is permanent CE. 151525cf1a30Sjl */ 151625cf1a30Sjl for (i = 0; i < 2; i++) { 1517*601c2e1eSdhain cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add, 0); 1518*601c2e1eSdhain 1519*601c2e1eSdhain if (cntl == 0) { 1520*601c2e1eSdhain /* timeout case */ 1521*601c2e1eSdhain return; 1522*601c2e1eSdhain } 152325cf1a30Sjl /* 152425cf1a30Sjl * If the error becomes UE or CMPE 152525cf1a30Sjl * we return to the caller immediately. 152625cf1a30Sjl */ 152725cf1a30Sjl if (cntl & MAC_CNTL_REW_UE) { 152825cf1a30Sjl if (ptrl_error) 152925cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE; 153025cf1a30Sjl else 153125cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_UE; 153225cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 153325cf1a30Sjl return; 153425cf1a30Sjl } 153525cf1a30Sjl if (cntl & MAC_CNTL_REW_CMPE) { 153625cf1a30Sjl if (ptrl_error) 153725cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE; 153825cf1a30Sjl else 153925cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE; 154025cf1a30Sjl flt_stat->mf_type = FLT_TYPE_CMPE; 154125cf1a30Sjl return; 154225cf1a30Sjl } 154325cf1a30Sjl } 154425cf1a30Sjl if (!(cntl & MAC_CNTL_REW_CE)) { 154525cf1a30Sjl flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE; 154625cf1a30Sjl } 154725cf1a30Sjl 154825cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 154925cf1a30Sjl /* report PERMANENT_CE to SP via SCF */ 155025cf1a30Sjl if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) { 155125cf1a30Sjl mc_queue_scf_log(mcp, flt_stat, bank); 155225cf1a30Sjl } 155325cf1a30Sjl } 155425cf1a30Sjl } 155525cf1a30Sjl 155625cf1a30Sjl #define IS_CMPE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\ 155725cf1a30Sjl MAC_CNTL_MI_CMPE)) 155825cf1a30Sjl #define IS_UE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE)) 155925cf1a30Sjl #define IS_CE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE)) 156025cf1a30Sjl #define IS_OK(cntl, f) (!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \ 156125cf1a30Sjl MAC_CNTL_MI_ERRS))) 156225cf1a30Sjl 156325cf1a30Sjl 156425cf1a30Sjl static int 156525cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error) 156625cf1a30Sjl { 156725cf1a30Sjl if (ptrl_error) { 156825cf1a30Sjl return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE); 156925cf1a30Sjl } else { 157025cf1a30Sjl return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE); 157125cf1a30Sjl } 157225cf1a30Sjl } 157325cf1a30Sjl 157425cf1a30Sjl void 157525cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value) 157625cf1a30Sjl { 157737afe445Shyw int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank; 157837afe445Shyw 157937afe445Shyw if (mcp->mc_speedup_period[ebank] > 0) 15800cc8ae86Sav value |= mc_max_speed; 15810cc8ae86Sav else 15820cc8ae86Sav value |= mcp->mc_speed; 158325cf1a30Sjl ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value); 158425cf1a30Sjl } 158525cf1a30Sjl 158625cf1a30Sjl static void 158725cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 158825cf1a30Sjl { 158925cf1a30Sjl flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 1590d8a0cca9Swh MAC_CNTL_PTRL_ERRS; 159125cf1a30Sjl flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank)); 159225cf1a30Sjl flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank)); 159325cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 1594aeb241b2Sav flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num; 159525cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 159625cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 159725cf1a30Sjl } 159825cf1a30Sjl 159925cf1a30Sjl static void 160025cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 160125cf1a30Sjl { 160225cf1a30Sjl uint32_t status, old_status; 160325cf1a30Sjl 1604d8a0cca9Swh status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & MAC_CNTL_MI_ERRS; 160525cf1a30Sjl old_status = 0; 160625cf1a30Sjl 160725cf1a30Sjl /* we keep reading until the status is stable */ 160825cf1a30Sjl while (old_status != status) { 160925cf1a30Sjl old_status = status; 1610d8a0cca9Swh flt_stat->mf_err_add = LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank)); 1611d8a0cca9Swh flt_stat->mf_err_log = LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank)); 161225cf1a30Sjl status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 1613d8a0cca9Swh MAC_CNTL_MI_ERRS; 161425cf1a30Sjl if (status == old_status) { 161525cf1a30Sjl break; 161625cf1a30Sjl } 161725cf1a30Sjl } 161825cf1a30Sjl 161925cf1a30Sjl flt_stat->mf_cntl = status; 162025cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 1621aeb241b2Sav flt_stat->mf_flt_maddr.ma_phys_bd = mcp->mc_phys_board_num; 162225cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 162325cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 162425cf1a30Sjl } 162525cf1a30Sjl 162625cf1a30Sjl 162725cf1a30Sjl /* 162825cf1a30Sjl * Error philosophy for mirror mode: 162925cf1a30Sjl * 163025cf1a30Sjl * PTRL (The error address for both banks are same, since ptrl stops if it 163125cf1a30Sjl * detects error.) 16321039f409Sav * - Compare error log CMPE. 163325cf1a30Sjl * 163425cf1a30Sjl * - UE-UE Report MUE. No rewrite. 163525cf1a30Sjl * 163625cf1a30Sjl * - UE-* UE-(CE/OK). Rewrite to scrub UE. Report SUE. 163725cf1a30Sjl * 163825cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 163925cf1a30Sjl * If CE is permanent, inform SCF. Once for each 164025cf1a30Sjl * Dimm. If CE becomes UE or CMPE, go back to above. 164125cf1a30Sjl * 164225cf1a30Sjl * 164325cf1a30Sjl * MI (The error addresses for each bank are the same or different.) 16441039f409Sav * - Compare error If addresses are the same. Just CMPE, so log CMPE. 164525cf1a30Sjl * If addresses are different (this could happen 16461039f409Sav * as a result of scrubbing. Report each separately. 164725cf1a30Sjl * Only report error info on each side. 164825cf1a30Sjl * 164925cf1a30Sjl * - UE-UE Addresses are the same. Report MUE. 165025cf1a30Sjl * Addresses are different. Report SUE on each bank. 165125cf1a30Sjl * Rewrite to clear UE. 165225cf1a30Sjl * 165325cf1a30Sjl * - UE-* UE-(CE/OK) 165425cf1a30Sjl * Rewrite to clear UE. Report SUE for the bank. 165525cf1a30Sjl * 165625cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 165725cf1a30Sjl * If CE becomes UE or CMPE, go back to above. 165825cf1a30Sjl * 165925cf1a30Sjl */ 166025cf1a30Sjl 166125cf1a30Sjl static int 166225cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat) 166325cf1a30Sjl { 166425cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 166525cf1a30Sjl int i; 166625cf1a30Sjl int rv = 0; 1667*601c2e1eSdhain int bank; 1668*601c2e1eSdhain int rewrite_timeout = 0; 166925cf1a30Sjl 167025cf1a30Sjl MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n", 1671d8a0cca9Swh flt_stat[0].mf_cntl, flt_stat[1].mf_cntl); 167225cf1a30Sjl 167325cf1a30Sjl if (ptrl_error) { 1674d8a0cca9Swh if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) & 1675d8a0cca9Swh MAC_CNTL_PTRL_ERRS) == 0) 167625cf1a30Sjl return (0); 167725cf1a30Sjl } else { 1678d8a0cca9Swh if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) & 1679d8a0cca9Swh MAC_CNTL_MI_ERRS) == 0) 168025cf1a30Sjl return (0); 168125cf1a30Sjl } 168225cf1a30Sjl 168325cf1a30Sjl /* 168425cf1a30Sjl * First we take care of the case of CE 168525cf1a30Sjl * because they can become UE or CMPE 168625cf1a30Sjl */ 168725cf1a30Sjl for (i = 0; i < 2; i++) { 168825cf1a30Sjl if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) { 1689*601c2e1eSdhain bank = flt_stat[i].mf_flt_maddr.ma_bank; 1690*601c2e1eSdhain MC_LOG("CE detected on bank %d\n", bank); 1691*601c2e1eSdhain mc_scrub_ce(mcp, bank, &flt_stat[i], ptrl_error); 1692*601c2e1eSdhain if (MC_REWRITE_ACTIVE(mcp, bank)) { 1693*601c2e1eSdhain rewrite_timeout = 1; 1694*601c2e1eSdhain } 169525cf1a30Sjl rv = 1; 169625cf1a30Sjl } 169725cf1a30Sjl } 169825cf1a30Sjl 1699*601c2e1eSdhain if (rewrite_timeout) 1700*601c2e1eSdhain return (0); 1701*601c2e1eSdhain 170225cf1a30Sjl /* The above scrubbing can turn CE into UE or CMPE */ 170325cf1a30Sjl 170425cf1a30Sjl /* 170525cf1a30Sjl * Now we distinguish two cases: same address or not 170625cf1a30Sjl * the same address. It might seem more intuitive to 170725cf1a30Sjl * distinguish PTRL v.s. MI error but it is more 170825cf1a30Sjl * complicated that way. 170925cf1a30Sjl */ 171025cf1a30Sjl 171125cf1a30Sjl if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) { 171225cf1a30Sjl 171325cf1a30Sjl if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) || 171425cf1a30Sjl IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) { 171525cf1a30Sjl flt_stat[0].mf_type = FLT_TYPE_CMPE; 171625cf1a30Sjl flt_stat[1].mf_type = FLT_TYPE_CMPE; 171725cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 171825cf1a30Sjl mc_aflt->mflt_nflts = 2; 171925cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 172025cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 172125cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 17221039f409Sav /* 17231039f409Sav * Compare error is result of MAC internal error, so 17241039f409Sav * simply log it instead of publishing an ereport. SCF 17251039f409Sav * diagnoses all the MAC internal and its i/f error. 17261039f409Sav * mc_err_drain(mc_aflt); 17271039f409Sav */ 17281039f409Sav MC_LOG("cmpe error detected\n"); 172925cf1a30Sjl return (1); 173025cf1a30Sjl } 173125cf1a30Sjl 173225cf1a30Sjl if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) && 1733d8a0cca9Swh IS_UE(flt_stat[1].mf_cntl, ptrl_error)) { 173425cf1a30Sjl /* Both side are UE's */ 173525cf1a30Sjl 173625cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[0]); 173725cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[1]); 173825cf1a30Sjl MC_LOG("MUE detected\n"); 17390cc8ae86Sav flt_stat[0].mf_type = FLT_TYPE_MUE; 17400cc8ae86Sav flt_stat[1].mf_type = FLT_TYPE_MUE; 174125cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_MUE; 174225cf1a30Sjl mc_aflt->mflt_nflts = 2; 174325cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 174425cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 174525cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 174625cf1a30Sjl mc_err_drain(mc_aflt); 174725cf1a30Sjl return (1); 174825cf1a30Sjl } 174925cf1a30Sjl 175025cf1a30Sjl /* Now the only case is UE/CE, UE/OK, or don't care */ 175125cf1a30Sjl for (i = 0; i < 2; i++) { 1752*601c2e1eSdhain if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 17530cc8ae86Sav 17540cc8ae86Sav /* rewrite can clear the one side UE error */ 17550cc8ae86Sav 175625cf1a30Sjl if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) { 175725cf1a30Sjl (void) do_rewrite(mcp, 175825cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank, 1759*601c2e1eSdhain flt_stat[i].mf_flt_maddr.ma_dimm_addr, 0); 176025cf1a30Sjl } 176125cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_UE; 176225cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[i]); 176325cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_SUE; 176425cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 176525cf1a30Sjl mc_aflt->mflt_nflts = 1; 176625cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 176725cf1a30Sjl mc_err_drain(mc_aflt); 176825cf1a30Sjl /* Once we hit a UE/CE or UE/OK case, done */ 176925cf1a30Sjl return (1); 1770*601c2e1eSdhain } 177125cf1a30Sjl } 177225cf1a30Sjl 177325cf1a30Sjl } else { 177425cf1a30Sjl /* 177525cf1a30Sjl * addresses are different. That means errors 177625cf1a30Sjl * on the 2 banks are not related at all. 177725cf1a30Sjl */ 177825cf1a30Sjl for (i = 0; i < 2; i++) { 1779d8a0cca9Swh if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) { 1780d8a0cca9Swh flt_stat[i].mf_type = FLT_TYPE_CMPE; 1781d8a0cca9Swh mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 1782d8a0cca9Swh mc_aflt->mflt_nflts = 1; 1783d8a0cca9Swh mc_aflt->mflt_stat[0] = &flt_stat[i]; 1784d8a0cca9Swh mc_aflt->mflt_pr = PR_UE; 1785d8a0cca9Swh /* 1786d8a0cca9Swh * Compare error is result of MAC internal 1787d8a0cca9Swh * error, so simply log it instead of 1788d8a0cca9Swh * publishing an ereport. SCF diagnoses all 1789d8a0cca9Swh * the MAC internal and its interface error. 1790d8a0cca9Swh * mc_err_drain(mc_aflt); 1791d8a0cca9Swh */ 1792d8a0cca9Swh MC_LOG("cmpe error detected\n"); 1793d8a0cca9Swh /* no more report on this bank */ 1794d8a0cca9Swh flt_stat[i].mf_cntl = 0; 1795d8a0cca9Swh rv = 1; 1796d8a0cca9Swh } 179725cf1a30Sjl } 179825cf1a30Sjl 17990cc8ae86Sav /* rewrite can clear the one side UE error */ 18000cc8ae86Sav 180125cf1a30Sjl for (i = 0; i < 2; i++) { 1802d8a0cca9Swh if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 1803d8a0cca9Swh (void) do_rewrite(mcp, 1804d8a0cca9Swh flt_stat[i].mf_flt_maddr.ma_bank, 1805*601c2e1eSdhain flt_stat[i].mf_flt_maddr.ma_dimm_addr, 1806*601c2e1eSdhain 0); 1807d8a0cca9Swh flt_stat[i].mf_type = FLT_TYPE_UE; 1808d8a0cca9Swh MAC_SET_ERRLOG_INFO(&flt_stat[i]); 1809d8a0cca9Swh mc_aflt->mflt_erpt_class = MC_OPL_SUE; 1810d8a0cca9Swh mc_aflt->mflt_stat[0] = &flt_stat[i]; 1811d8a0cca9Swh mc_aflt->mflt_nflts = 1; 1812d8a0cca9Swh mc_aflt->mflt_pr = PR_MCE; 1813d8a0cca9Swh mc_err_drain(mc_aflt); 1814d8a0cca9Swh rv = 1; 1815d8a0cca9Swh } 181625cf1a30Sjl } 181725cf1a30Sjl } 181825cf1a30Sjl return (rv); 181925cf1a30Sjl } 182025cf1a30Sjl static void 1821738dd194Shyw mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr) 182225cf1a30Sjl { 182325cf1a30Sjl mc_aflt_t mc_aflt; 182425cf1a30Sjl mc_flt_stat_t flt_stat[2], mi_flt_stat[2]; 18250cc8ae86Sav int i; 18260cc8ae86Sav int mi_valid; 182725cf1a30Sjl 1828738dd194Shyw ASSERT(rsaddr); 1829738dd194Shyw 183025cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 183125cf1a30Sjl bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t)); 183225cf1a30Sjl bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t)); 183325cf1a30Sjl 1834ad59b69dSbm 183525cf1a30Sjl mc_aflt.mflt_mcp = mcp; 183625cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 183725cf1a30Sjl 183825cf1a30Sjl /* Now read all the registers into flt_stat */ 183925cf1a30Sjl 18400cc8ae86Sav for (i = 0; i < 2; i++) { 18410cc8ae86Sav MC_LOG("Reading registers of bank %d\n", bank); 18420cc8ae86Sav /* patrol registers */ 18430cc8ae86Sav mc_read_ptrl_reg(mcp, bank, &flt_stat[i]); 184425cf1a30Sjl 1845738dd194Shyw /* 1846738dd194Shyw * In mirror mode, it is possible that only one bank 1847738dd194Shyw * may report the error. We need to check for it to 1848738dd194Shyw * ensure we pick the right addr value for patrol restart. 1849738dd194Shyw * Note that if both banks reported errors, we pick the 1850738dd194Shyw * 2nd one. Both banks should reported the same error address. 1851738dd194Shyw */ 1852738dd194Shyw if (flt_stat[i].mf_cntl & MAC_CNTL_PTRL_ERRS) 1853738dd194Shyw rsaddr->mi_restartaddr = flt_stat[i].mf_flt_maddr; 185425cf1a30Sjl 18550cc8ae86Sav MC_LOG("ptrl registers cntl %x add %x log %x\n", 1856d8a0cca9Swh flt_stat[i].mf_cntl, flt_stat[i].mf_err_add, 1857d8a0cca9Swh flt_stat[i].mf_err_log); 185825cf1a30Sjl 18590cc8ae86Sav /* MI registers */ 18600cc8ae86Sav mc_read_mi_reg(mcp, bank, &mi_flt_stat[i]); 186125cf1a30Sjl 18620cc8ae86Sav MC_LOG("MI registers cntl %x add %x log %x\n", 1863d8a0cca9Swh mi_flt_stat[i].mf_cntl, mi_flt_stat[i].mf_err_add, 1864d8a0cca9Swh mi_flt_stat[i].mf_err_log); 186525cf1a30Sjl 18660cc8ae86Sav bank = bank^1; 18670cc8ae86Sav } 186825cf1a30Sjl 186925cf1a30Sjl /* clear errors once we read all the registers */ 1870d8a0cca9Swh MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 187125cf1a30Sjl 18720cc8ae86Sav MAC_CLEAR_ERRS(mcp, bank ^ 1, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 18730cc8ae86Sav 18740cc8ae86Sav /* Process MI errors first */ 187525cf1a30Sjl 18760cc8ae86Sav /* if not error mode, cntl1 is 0 */ 18770cc8ae86Sav if ((mi_flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 1878d8a0cca9Swh (mi_flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 18790cc8ae86Sav mi_flt_stat[0].mf_cntl = 0; 18800cc8ae86Sav 18810cc8ae86Sav if ((mi_flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 1882d8a0cca9Swh (mi_flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 18830cc8ae86Sav mi_flt_stat[1].mf_cntl = 0; 188425cf1a30Sjl 18850cc8ae86Sav mc_aflt.mflt_is_ptrl = 0; 18860cc8ae86Sav mi_valid = mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]); 18870cc8ae86Sav 18880cc8ae86Sav if ((((flt_stat[0].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 1889d8a0cca9Swh MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[0].mf_cntl & 1890d8a0cca9Swh MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) && 1891d8a0cca9Swh (flt_stat[0].mf_err_add == mi_flt_stat[0].mf_err_add) && 1892d8a0cca9Swh (((flt_stat[1].mf_cntl & MAC_CNTL_PTRL_ERRS) >> 1893d8a0cca9Swh MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat[1].mf_cntl & 1894d8a0cca9Swh MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) && 1895d8a0cca9Swh (flt_stat[1].mf_err_add == mi_flt_stat[1].mf_err_add)) { 18960cc8ae86Sav #ifdef DEBUG 18970cc8ae86Sav MC_LOG("discarding PTRL error because " 18980cc8ae86Sav "it is the same as MI\n"); 18990cc8ae86Sav #endif 1900738dd194Shyw rsaddr->mi_valid = mi_valid; 19010cc8ae86Sav return; 19020cc8ae86Sav } 190325cf1a30Sjl /* if not error mode, cntl1 is 0 */ 190425cf1a30Sjl if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 1905d8a0cca9Swh (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 190625cf1a30Sjl flt_stat[0].mf_cntl = 0; 190725cf1a30Sjl 190825cf1a30Sjl if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 1909d8a0cca9Swh (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 191025cf1a30Sjl flt_stat[1].mf_cntl = 0; 191125cf1a30Sjl 191225cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 1913738dd194Shyw rsaddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]); 191425cf1a30Sjl } 191525cf1a30Sjl static int 191625cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt, 191725cf1a30Sjl mc_flt_stat_t *flt_stat) 191825cf1a30Sjl { 191925cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 192025cf1a30Sjl int rv = 0; 192125cf1a30Sjl 192225cf1a30Sjl mc_aflt->mflt_erpt_class = NULL; 192325cf1a30Sjl if (IS_UE(flt_stat->mf_cntl, ptrl_error)) { 19241039f409Sav MC_LOG("UE detected\n"); 192525cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 192625cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 192725cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 192825cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 192925cf1a30Sjl rv = 1; 193025cf1a30Sjl } else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) { 19311039f409Sav MC_LOG("CE detected\n"); 193225cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 193325cf1a30Sjl 19341039f409Sav /* Error type can change after scrubbing */ 193525cf1a30Sjl mc_scrub_ce(mcp, bank, flt_stat, ptrl_error); 1936*601c2e1eSdhain if (MC_REWRITE_ACTIVE(mcp, bank)) { 1937*601c2e1eSdhain return (0); 1938*601c2e1eSdhain } 193925cf1a30Sjl 1940056c948bStsien if (flt_stat->mf_type == FLT_TYPE_INTERMITTENT_CE) { 1941056c948bStsien mc_aflt->mflt_erpt_class = MC_OPL_ICE; 1942056c948bStsien mc_aflt->mflt_pr = PR_MCE; 1943056c948bStsien } else if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 194425cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CE; 194525cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 194625cf1a30Sjl } else if (flt_stat->mf_type == FLT_TYPE_UE) { 194725cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 194825cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 194925cf1a30Sjl } 195025cf1a30Sjl rv = 1; 195125cf1a30Sjl } 1952d8a0cca9Swh MC_LOG("mc_process_error: fault type %x erpt %s\n", flt_stat->mf_type, 1953d8a0cca9Swh mc_aflt->mflt_erpt_class); 195425cf1a30Sjl if (mc_aflt->mflt_erpt_class) { 195525cf1a30Sjl mc_aflt->mflt_stat[0] = flt_stat; 195625cf1a30Sjl mc_aflt->mflt_nflts = 1; 195725cf1a30Sjl mc_err_drain(mc_aflt); 195825cf1a30Sjl } 195925cf1a30Sjl return (rv); 196025cf1a30Sjl } 196125cf1a30Sjl 196225cf1a30Sjl static void 1963738dd194Shyw mc_error_handler(mc_opl_t *mcp, int bank, mc_rsaddr_info_t *rsaddr) 196425cf1a30Sjl { 196525cf1a30Sjl mc_aflt_t mc_aflt; 196625cf1a30Sjl mc_flt_stat_t flt_stat, mi_flt_stat; 19670cc8ae86Sav int mi_valid; 196825cf1a30Sjl 196925cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 197025cf1a30Sjl bzero(&flt_stat, sizeof (mc_flt_stat_t)); 197125cf1a30Sjl bzero(&mi_flt_stat, sizeof (mc_flt_stat_t)); 197225cf1a30Sjl 197325cf1a30Sjl mc_aflt.mflt_mcp = mcp; 197425cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 197525cf1a30Sjl 197625cf1a30Sjl /* patrol registers */ 197725cf1a30Sjl mc_read_ptrl_reg(mcp, bank, &flt_stat); 197825cf1a30Sjl 1979738dd194Shyw ASSERT(rsaddr); 1980738dd194Shyw rsaddr->mi_restartaddr = flt_stat.mf_flt_maddr; 198125cf1a30Sjl 1982d8a0cca9Swh MC_LOG("ptrl registers cntl %x add %x log %x\n", flt_stat.mf_cntl, 1983d8a0cca9Swh flt_stat.mf_err_add, flt_stat.mf_err_log); 198425cf1a30Sjl 198525cf1a30Sjl /* MI registers */ 198625cf1a30Sjl mc_read_mi_reg(mcp, bank, &mi_flt_stat); 198725cf1a30Sjl 19880cc8ae86Sav 1989d8a0cca9Swh MC_LOG("MI registers cntl %x add %x log %x\n", mi_flt_stat.mf_cntl, 1990d8a0cca9Swh mi_flt_stat.mf_err_add, mi_flt_stat.mf_err_log); 199125cf1a30Sjl 199225cf1a30Sjl /* clear errors once we read all the registers */ 199325cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 199425cf1a30Sjl 19950cc8ae86Sav mc_aflt.mflt_is_ptrl = 0; 19960cc8ae86Sav if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) && 1997d8a0cca9Swh ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 1998d8a0cca9Swh ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 19990cc8ae86Sav mi_valid = mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat); 20000cc8ae86Sav } 20010cc8ae86Sav 20020cc8ae86Sav if ((((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) >> 2003d8a0cca9Swh MAC_CNTL_PTRL_ERR_SHIFT) == ((mi_flt_stat.mf_cntl & 2004d8a0cca9Swh MAC_CNTL_MI_ERRS) >> MAC_CNTL_MI_ERR_SHIFT)) && 2005d8a0cca9Swh (flt_stat.mf_err_add == mi_flt_stat.mf_err_add)) { 20060cc8ae86Sav #ifdef DEBUG 20070cc8ae86Sav MC_LOG("discarding PTRL error because " 20080cc8ae86Sav "it is the same as MI\n"); 20090cc8ae86Sav #endif 2010738dd194Shyw rsaddr->mi_valid = mi_valid; 20110cc8ae86Sav return; 20120cc8ae86Sav } 20130cc8ae86Sav 201425cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 201525cf1a30Sjl if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) && 2016d8a0cca9Swh ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 2017d8a0cca9Swh ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 2018d8a0cca9Swh rsaddr->mi_valid = mc_process_error(mcp, bank, &mc_aflt, 2019d8a0cca9Swh &flt_stat); 202025cf1a30Sjl } 202125cf1a30Sjl } 202225cf1a30Sjl /* 202325cf1a30Sjl * memory patrol error handling algorithm: 202425cf1a30Sjl * timeout() is used to do periodic polling 202525cf1a30Sjl * This is the flow chart. 202625cf1a30Sjl * timeout -> 202725cf1a30Sjl * mc_check_errors() 202825cf1a30Sjl * if memory bank is installed, read the status register 202925cf1a30Sjl * if any error bit is set, 203025cf1a30Sjl * -> mc_error_handler() 20311039f409Sav * -> read all error registers 203225cf1a30Sjl * -> mc_process_error() 203325cf1a30Sjl * determine error type 203425cf1a30Sjl * rewrite to clear error or scrub to determine CE type 203525cf1a30Sjl * inform SCF on permanent CE 203625cf1a30Sjl * -> mc_err_drain 203725cf1a30Sjl * page offline processing 203825cf1a30Sjl * -> mc_ereport_post() 203925cf1a30Sjl */ 204025cf1a30Sjl 2041*601c2e1eSdhain static void 2042*601c2e1eSdhain mc_process_rewrite(mc_opl_t *mcp, int bank) 2043*601c2e1eSdhain { 2044*601c2e1eSdhain uint32_t rew_addr, cntl; 2045*601c2e1eSdhain mc_retry_info_t *retry; 2046*601c2e1eSdhain struct mc_bank *bankp; 2047*601c2e1eSdhain 2048*601c2e1eSdhain bankp = &(mcp->mc_bank[bank]); 2049*601c2e1eSdhain retry = bankp->mcb_active; 2050*601c2e1eSdhain if (retry == NULL) 2051*601c2e1eSdhain return; 2052*601c2e1eSdhain 2053*601c2e1eSdhain if (retry->ri_state <= RETRY_STATE_ACTIVE) { 2054*601c2e1eSdhain cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 2055*601c2e1eSdhain if (cntl & MAC_CNTL_PTRL_STATUS) 2056*601c2e1eSdhain return; 2057*601c2e1eSdhain rew_addr = retry->ri_addr; 2058*601c2e1eSdhain ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), rew_addr); 2059*601c2e1eSdhain MAC_REW_REQ(mcp, bank); 2060*601c2e1eSdhain 2061*601c2e1eSdhain retry->ri_state = RETRY_STATE_REWRITE; 2062*601c2e1eSdhain } 2063*601c2e1eSdhain 2064*601c2e1eSdhain cntl = ldphysio(MAC_PTRL_CNTL(mcp, bank)); 2065*601c2e1eSdhain 2066*601c2e1eSdhain if (cntl & MAC_CNTL_REW_END) { 2067*601c2e1eSdhain MAC_CLEAR_ERRS(mcp, bank, 2068*601c2e1eSdhain MAC_CNTL_REW_ERRS); 2069*601c2e1eSdhain mc_clear_rewrite(mcp, bank); 2070*601c2e1eSdhain } else { 2071*601c2e1eSdhain /* 2072*601c2e1eSdhain * If the rewrite does not complete in 2073*601c2e1eSdhain * 1 hour, we have to consider this a HW 2074*601c2e1eSdhain * failure. However, there is no recovery 2075*601c2e1eSdhain * mechanism. The only thing we can do 2076*601c2e1eSdhain * to to print a warning message to the 2077*601c2e1eSdhain * console. We continue to increment the 2078*601c2e1eSdhain * counter but we only print the message 2079*601c2e1eSdhain * once. It will take the counter a long 2080*601c2e1eSdhain * time to wrap around and the user might 2081*601c2e1eSdhain * see a second message. In practice, 2082*601c2e1eSdhain * we have never hit this condition but 2083*601c2e1eSdhain * we have to keep the code here just in case. 2084*601c2e1eSdhain */ 2085*601c2e1eSdhain if (++mcp->mc_bank[bank].mcb_rewrite_count 2086*601c2e1eSdhain == mc_max_rewrite_retry) { 2087*601c2e1eSdhain cmn_err(CE_WARN, "Memory patrol feature is" 2088*601c2e1eSdhain " partly suspended on /LSB%d/B%d" 2089*601c2e1eSdhain " due to heavy memory load," 2090*601c2e1eSdhain " and it will restart" 2091*601c2e1eSdhain " automatically.\n", mcp->mc_board_num, 2092*601c2e1eSdhain bank); 2093*601c2e1eSdhain } 2094*601c2e1eSdhain } 2095*601c2e1eSdhain } 2096*601c2e1eSdhain 209725cf1a30Sjl static void 209825cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp) 209925cf1a30Sjl { 2100738dd194Shyw mc_rsaddr_info_t rsaddr_info; 210125cf1a30Sjl int i, error_count = 0; 210225cf1a30Sjl uint32_t stat, cntl; 21030cc8ae86Sav int running; 2104cfb9e062Shyw int wrapped; 210537afe445Shyw int ebk; 210625cf1a30Sjl 210725cf1a30Sjl /* 210825cf1a30Sjl * scan errors. 210925cf1a30Sjl */ 21100cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) 21110cc8ae86Sav return; 21120cc8ae86Sav 211325cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 211425cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 2115*601c2e1eSdhain if (MC_REWRITE_ACTIVE(mcp, i)) { 2116*601c2e1eSdhain mc_process_rewrite(mcp, i); 2117*601c2e1eSdhain } 211825cf1a30Sjl stat = ldphysio(MAC_PTRL_STAT(mcp, i)); 211925cf1a30Sjl cntl = ldphysio(MAC_PTRL_CNTL(mcp, i)); 21200cc8ae86Sav running = cntl & MAC_CNTL_PTRL_START; 2121cfb9e062Shyw wrapped = cntl & MAC_CNTL_PTRL_ADD_MAX; 21220cc8ae86Sav 212337afe445Shyw /* Compute the effective bank idx */ 212437afe445Shyw ebk = (IS_MIRROR(mcp, i)) ? MIRROR_IDX(i) : i; 212537afe445Shyw 2126cfb9e062Shyw if (mc_debug_show_all || stat) { 2127cfb9e062Shyw MC_LOG("/LSB%d/B%d stat %x cntl %x\n", 2128d8a0cca9Swh mcp->mc_board_num, i, stat, cntl); 2129cfb9e062Shyw } 2130cfb9e062Shyw 2131cfb9e062Shyw /* 2132cfb9e062Shyw * Update stats and reset flag if the HW patrol 2133cfb9e062Shyw * wrapped around in its scan. 2134cfb9e062Shyw */ 2135cfb9e062Shyw if (wrapped) { 213625cf1a30Sjl MAC_CLEAR_MAX(mcp, i); 213737afe445Shyw mcp->mc_period[ebk]++; 213837afe445Shyw if (IS_MIRROR(mcp, i)) 2139d8a0cca9Swh MC_LOG("mirror mc period %ld on " 2140d8a0cca9Swh "/LSB%d/B%d\n", mcp->mc_period[ebk], 2141d8a0cca9Swh mcp->mc_board_num, i); 214237afe445Shyw else { 2143d8a0cca9Swh MC_LOG("mc period %ld on " 2144d8a0cca9Swh "/LSB%d/B%d\n", mcp->mc_period[ebk], 2145d8a0cca9Swh mcp->mc_board_num, i); 214637afe445Shyw } 214725cf1a30Sjl } 2148cfb9e062Shyw 2149cfb9e062Shyw if (running) { 2150cfb9e062Shyw /* 2151cfb9e062Shyw * Mac patrol HW is still running. 2152cfb9e062Shyw * Normally when an error is detected, 2153cfb9e062Shyw * the HW patrol will stop so that we 2154cfb9e062Shyw * can collect error data for reporting. 2155cfb9e062Shyw * Certain errors (MI errors) detected may not 2156cfb9e062Shyw * cause the HW patrol to stop which is a 2157cfb9e062Shyw * problem since we cannot read error data while 2158cfb9e062Shyw * the HW patrol is running. SW is not allowed 2159cfb9e062Shyw * to stop the HW patrol while it is running 2160cfb9e062Shyw * as it may cause HW inconsistency. This is 2161cfb9e062Shyw * described in a HW errata. 2162cfb9e062Shyw * In situations where we detected errors 2163cfb9e062Shyw * that may not cause the HW patrol to stop. 2164cfb9e062Shyw * We speed up the HW patrol scanning in 2165cfb9e062Shyw * the hope that it will find the 'real' PTRL 2166cfb9e062Shyw * errors associated with the previous errors 2167cfb9e062Shyw * causing the HW to finally stop so that we 2168cfb9e062Shyw * can do the reporting. 2169cfb9e062Shyw */ 2170cfb9e062Shyw /* 2171cfb9e062Shyw * Check to see if we did speed up 2172cfb9e062Shyw * the HW patrol due to previous errors 2173cfb9e062Shyw * detected that did not cause the patrol 2174cfb9e062Shyw * to stop. We only do it if HW patrol scan 2175cfb9e062Shyw * wrapped (counted as completing a 'period'). 2176cfb9e062Shyw */ 217737afe445Shyw if (mcp->mc_speedup_period[ebk] > 0) { 2178d8a0cca9Swh if (wrapped && 2179d8a0cca9Swh (--mcp->mc_speedup_period[ebk] == 2180d8a0cca9Swh 0)) { 2181d8a0cca9Swh /* 2182d8a0cca9Swh * We did try to speed up. 2183d8a0cca9Swh * The speed up period has 2184d8a0cca9Swh * expired and the HW patrol 2185d8a0cca9Swh * is still running. The 2186d8a0cca9Swh * errors must be intermittent. 2187d8a0cca9Swh * We have no choice but to 2188d8a0cca9Swh * ignore them, reset the scan 2189d8a0cca9Swh * speed to normal and clear 2190d8a0cca9Swh * the MI error bits. For 2191d8a0cca9Swh * mirror mode, we need to 2192d8a0cca9Swh * clear errors on both banks. 2193d8a0cca9Swh */ 2194d8a0cca9Swh MC_LOG("Clearing MI errors\n"); 2195d8a0cca9Swh MAC_CLEAR_ERRS(mcp, i, 2196d8a0cca9Swh MAC_CNTL_MI_ERRS); 2197d8a0cca9Swh 2198d8a0cca9Swh if (IS_MIRROR(mcp, i)) { 2199d8a0cca9Swh MC_LOG("Clearing " 2200d8a0cca9Swh "Mirror MI errs\n"); 2201d8a0cca9Swh MAC_CLEAR_ERRS(mcp, 2202d8a0cca9Swh i^1, 2203d8a0cca9Swh MAC_CNTL_MI_ERRS); 2204d8a0cca9Swh } 220537afe445Shyw } 2206cfb9e062Shyw } else if (stat & MAC_STAT_MI_ERRS) { 2207cfb9e062Shyw /* 2208cfb9e062Shyw * MI errors detected but we cannot 2209cfb9e062Shyw * report them since the HW patrol 2210cfb9e062Shyw * is still running. 2211cfb9e062Shyw * We will attempt to speed up the 2212cfb9e062Shyw * scanning and hopefully the HW 2213cfb9e062Shyw * can detect PRTL errors at the same 2214cfb9e062Shyw * location that cause the HW patrol 2215cfb9e062Shyw * to stop. 2216cfb9e062Shyw */ 221737afe445Shyw mcp->mc_speedup_period[ebk] = 2; 22180cc8ae86Sav MAC_CMD(mcp, i, 0); 22190cc8ae86Sav } 2220cfb9e062Shyw } else if (stat & (MAC_STAT_PTRL_ERRS | 2221cfb9e062Shyw MAC_STAT_MI_ERRS)) { 2222cfb9e062Shyw /* 2223cfb9e062Shyw * HW Patrol has stopped and we found errors. 2224cfb9e062Shyw * Proceed to collect and report error info. 2225cfb9e062Shyw */ 222637afe445Shyw mcp->mc_speedup_period[ebk] = 0; 2227738dd194Shyw rsaddr_info.mi_valid = 0; 2228738dd194Shyw rsaddr_info.mi_injectrestart = 0; 2229738dd194Shyw if (IS_MIRROR(mcp, i)) { 2230d8a0cca9Swh mc_error_handler_mir(mcp, i, 2231d8a0cca9Swh &rsaddr_info); 2232738dd194Shyw } else { 2233d8a0cca9Swh mc_error_handler(mcp, i, &rsaddr_info); 2234738dd194Shyw } 2235cfb9e062Shyw 2236cfb9e062Shyw error_count++; 2237738dd194Shyw restart_patrol(mcp, i, &rsaddr_info); 223825cf1a30Sjl } else { 2239cfb9e062Shyw /* 2240cfb9e062Shyw * HW patrol scan has apparently stopped 2241cfb9e062Shyw * but no errors detected/flagged. 2242cfb9e062Shyw * Restart the HW patrol just to be sure. 224337afe445Shyw * In mirror mode, the odd bank might have 224437afe445Shyw * reported errors that caused the patrol to 224537afe445Shyw * stop. We'll defer the restart to the odd 224637afe445Shyw * bank in this case. 2247cfb9e062Shyw */ 224837afe445Shyw if (!IS_MIRROR(mcp, i) || (i & 0x1)) 224937afe445Shyw restart_patrol(mcp, i, NULL); 225025cf1a30Sjl } 225125cf1a30Sjl } 225225cf1a30Sjl } 225325cf1a30Sjl if (error_count > 0) 225425cf1a30Sjl mcp->mc_last_error += error_count; 225525cf1a30Sjl else 225625cf1a30Sjl mcp->mc_last_error = 0; 225725cf1a30Sjl } 225825cf1a30Sjl 22590cc8ae86Sav /* 22600cc8ae86Sav * mc_polling -- Check errors for only one instance, 22610cc8ae86Sav * but process errors for all instances to make sure we drain the errors 22620cc8ae86Sav * faster than they can be accumulated. 22630cc8ae86Sav * 22640cc8ae86Sav * Polling on each board should be done only once per each 22650cc8ae86Sav * mc_patrol_interval_sec. This is equivalent to setting mc_tick_left 22660cc8ae86Sav * to OPL_MAX_BOARDS and decrement by 1 on each timeout. 22670cc8ae86Sav * Once mc_tick_left becomes negative, the board becomes a candidate 22680cc8ae86Sav * for polling because it has waited for at least 22690cc8ae86Sav * mc_patrol_interval_sec's long. If mc_timeout_period is calculated 22701039f409Sav * differently, this has to be updated accordingly. 22710cc8ae86Sav */ 227225cf1a30Sjl 227325cf1a30Sjl static void 22740cc8ae86Sav mc_polling(void) 227525cf1a30Sjl { 22760cc8ae86Sav int i, scan_error; 22770cc8ae86Sav mc_opl_t *mcp; 227825cf1a30Sjl 227925cf1a30Sjl 22800cc8ae86Sav scan_error = 1; 22810cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 22820cc8ae86Sav mutex_enter(&mcmutex); 22830cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) { 22840cc8ae86Sav mutex_exit(&mcmutex); 22850cc8ae86Sav continue; 22860cc8ae86Sav } 22870cc8ae86Sav mutex_enter(&mcp->mc_lock); 22880cc8ae86Sav mutex_exit(&mcmutex); 2289738dd194Shyw if (!(mcp->mc_status & MC_POLL_RUNNING)) { 2290738dd194Shyw mutex_exit(&mcp->mc_lock); 2291738dd194Shyw continue; 2292738dd194Shyw } 22930cc8ae86Sav if (scan_error && mcp->mc_tick_left <= 0) { 22940cc8ae86Sav mc_check_errors_func((void *)mcp); 22950cc8ae86Sav mcp->mc_tick_left = OPL_MAX_BOARDS; 22960cc8ae86Sav scan_error = 0; 22970cc8ae86Sav } else { 22980cc8ae86Sav mcp->mc_tick_left--; 22990cc8ae86Sav } 23000cc8ae86Sav mc_process_scf_log(mcp); 23010cc8ae86Sav mutex_exit(&mcp->mc_lock); 230225cf1a30Sjl } 230325cf1a30Sjl } 230425cf1a30Sjl 230525cf1a30Sjl static void 230625cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr) 230725cf1a30Sjl { 230825cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 230925cf1a30Sjl maddr->ma_bank = bank; 231025cf1a30Sjl maddr->ma_dimm_addr = 0; 231125cf1a30Sjl } 231225cf1a30Sjl 231325cf1a30Sjl typedef struct mc_mem_range { 231425cf1a30Sjl uint64_t addr; 231525cf1a30Sjl uint64_t size; 231625cf1a30Sjl } mc_mem_range_t; 231725cf1a30Sjl 231825cf1a30Sjl static int 231925cf1a30Sjl get_base_address(mc_opl_t *mcp) 232025cf1a30Sjl { 232125cf1a30Sjl mc_mem_range_t *mem_range; 232225cf1a30Sjl int len; 232325cf1a30Sjl 232425cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 2325d8a0cca9Swh "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) { 232625cf1a30Sjl return (DDI_FAILURE); 232725cf1a30Sjl } 232825cf1a30Sjl 232925cf1a30Sjl mcp->mc_start_address = mem_range->addr; 233025cf1a30Sjl mcp->mc_size = mem_range->size; 233125cf1a30Sjl 233225cf1a30Sjl kmem_free(mem_range, len); 233325cf1a30Sjl return (DDI_SUCCESS); 233425cf1a30Sjl } 233525cf1a30Sjl 233625cf1a30Sjl struct mc_addr_spec { 233725cf1a30Sjl uint32_t bank; 233825cf1a30Sjl uint32_t phys_hi; 233925cf1a30Sjl uint32_t phys_lo; 234025cf1a30Sjl }; 234125cf1a30Sjl 234225cf1a30Sjl #define REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo) 234325cf1a30Sjl 234425cf1a30Sjl static char *mc_tbl_name[] = { 234525cf1a30Sjl "cs0-mc-pa-trans-table", 234625cf1a30Sjl "cs1-mc-pa-trans-table" 234725cf1a30Sjl }; 234825cf1a30Sjl 2349738dd194Shyw /* 2350738dd194Shyw * This routine performs a rangecheck for a given PA 2351738dd194Shyw * to see if it belongs to the memory range for this board. 2352738dd194Shyw * Return 1 if it is valid (within the range) and 0 otherwise 2353738dd194Shyw */ 235425cf1a30Sjl static int 2355738dd194Shyw mc_rangecheck_pa(mc_opl_t *mcp, uint64_t pa) 235625cf1a30Sjl { 2357d8a0cca9Swh if ((pa < mcp->mc_start_address) || (mcp->mc_start_address + 2358d8a0cca9Swh mcp->mc_size <= pa)) 2359738dd194Shyw return (0); 2360738dd194Shyw else 2361738dd194Shyw return (1); 236225cf1a30Sjl } 236325cf1a30Sjl 236425cf1a30Sjl static void 236525cf1a30Sjl mc_memlist_delete(struct memlist *mlist) 236625cf1a30Sjl { 236725cf1a30Sjl struct memlist *ml; 236825cf1a30Sjl 236925cf1a30Sjl for (ml = mlist; ml; ml = mlist) { 237025cf1a30Sjl mlist = ml->next; 237125cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 237225cf1a30Sjl } 237325cf1a30Sjl } 237425cf1a30Sjl 237525cf1a30Sjl static struct memlist * 237625cf1a30Sjl mc_memlist_dup(struct memlist *mlist) 237725cf1a30Sjl { 237825cf1a30Sjl struct memlist *hl = NULL, *tl, **mlp; 237925cf1a30Sjl 238025cf1a30Sjl if (mlist == NULL) 238125cf1a30Sjl return (NULL); 238225cf1a30Sjl 238325cf1a30Sjl mlp = &hl; 238425cf1a30Sjl tl = *mlp; 238525cf1a30Sjl for (; mlist; mlist = mlist->next) { 238625cf1a30Sjl *mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP); 238725cf1a30Sjl (*mlp)->address = mlist->address; 238825cf1a30Sjl (*mlp)->size = mlist->size; 238925cf1a30Sjl (*mlp)->prev = tl; 239025cf1a30Sjl tl = *mlp; 239125cf1a30Sjl mlp = &((*mlp)->next); 239225cf1a30Sjl } 239325cf1a30Sjl *mlp = NULL; 239425cf1a30Sjl 239525cf1a30Sjl return (hl); 239625cf1a30Sjl } 239725cf1a30Sjl 239825cf1a30Sjl 239925cf1a30Sjl static struct memlist * 240025cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len) 240125cf1a30Sjl { 240225cf1a30Sjl uint64_t end; 240325cf1a30Sjl struct memlist *ml, *tl, *nlp; 240425cf1a30Sjl 240525cf1a30Sjl if (mlist == NULL) 240625cf1a30Sjl return (NULL); 240725cf1a30Sjl 240825cf1a30Sjl end = base + len; 240925cf1a30Sjl if ((end <= mlist->address) || (base == end)) 241025cf1a30Sjl return (mlist); 241125cf1a30Sjl 241225cf1a30Sjl for (tl = ml = mlist; ml; tl = ml, ml = nlp) { 241325cf1a30Sjl uint64_t mend; 241425cf1a30Sjl 241525cf1a30Sjl nlp = ml->next; 241625cf1a30Sjl 241725cf1a30Sjl if (end <= ml->address) 241825cf1a30Sjl break; 241925cf1a30Sjl 242025cf1a30Sjl mend = ml->address + ml->size; 242125cf1a30Sjl if (base < mend) { 242225cf1a30Sjl if (base <= ml->address) { 242325cf1a30Sjl ml->address = end; 242425cf1a30Sjl if (end >= mend) 242525cf1a30Sjl ml->size = 0ull; 242625cf1a30Sjl else 242725cf1a30Sjl ml->size = mend - ml->address; 242825cf1a30Sjl } else { 242925cf1a30Sjl ml->size = base - ml->address; 243025cf1a30Sjl if (end < mend) { 243125cf1a30Sjl struct memlist *nl; 243225cf1a30Sjl /* 243325cf1a30Sjl * splitting an memlist entry. 243425cf1a30Sjl */ 243525cf1a30Sjl nl = kmem_alloc(sizeof (struct memlist), 2436d8a0cca9Swh KM_SLEEP); 243725cf1a30Sjl nl->address = end; 243825cf1a30Sjl nl->size = mend - nl->address; 243925cf1a30Sjl if ((nl->next = nlp) != NULL) 244025cf1a30Sjl nlp->prev = nl; 244125cf1a30Sjl nl->prev = ml; 244225cf1a30Sjl ml->next = nl; 244325cf1a30Sjl nlp = nl; 244425cf1a30Sjl } 244525cf1a30Sjl } 244625cf1a30Sjl if (ml->size == 0ull) { 244725cf1a30Sjl if (ml == mlist) { 244825cf1a30Sjl if ((mlist = nlp) != NULL) 244925cf1a30Sjl nlp->prev = NULL; 245025cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 245125cf1a30Sjl if (mlist == NULL) 245225cf1a30Sjl break; 245325cf1a30Sjl ml = nlp; 245425cf1a30Sjl } else { 245525cf1a30Sjl if ((tl->next = nlp) != NULL) 245625cf1a30Sjl nlp->prev = tl; 245725cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 245825cf1a30Sjl ml = tl; 245925cf1a30Sjl } 246025cf1a30Sjl } 246125cf1a30Sjl } 246225cf1a30Sjl } 246325cf1a30Sjl 246425cf1a30Sjl return (mlist); 246525cf1a30Sjl } 246625cf1a30Sjl 246725cf1a30Sjl static void 246825cf1a30Sjl mc_get_mlist(mc_opl_t *mcp) 246925cf1a30Sjl { 247025cf1a30Sjl struct memlist *mlist; 247125cf1a30Sjl 247225cf1a30Sjl memlist_read_lock(); 247325cf1a30Sjl mlist = mc_memlist_dup(phys_install); 247425cf1a30Sjl memlist_read_unlock(); 247525cf1a30Sjl 247625cf1a30Sjl if (mlist) { 247725cf1a30Sjl mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address); 247825cf1a30Sjl } 247925cf1a30Sjl 248025cf1a30Sjl if (mlist) { 248125cf1a30Sjl uint64_t startpa, endpa; 248225cf1a30Sjl 248325cf1a30Sjl startpa = mcp->mc_start_address + mcp->mc_size; 248425cf1a30Sjl endpa = ptob(physmax + 1); 248525cf1a30Sjl if (endpa > startpa) { 2486d8a0cca9Swh mlist = mc_memlist_del_span(mlist, startpa, 2487d8a0cca9Swh endpa - startpa); 248825cf1a30Sjl } 248925cf1a30Sjl } 249025cf1a30Sjl 249125cf1a30Sjl if (mlist) { 249225cf1a30Sjl mcp->mlist = mlist; 249325cf1a30Sjl } 249425cf1a30Sjl } 249525cf1a30Sjl 249625cf1a30Sjl int 249725cf1a30Sjl mc_board_add(mc_opl_t *mcp) 249825cf1a30Sjl { 249925cf1a30Sjl struct mc_addr_spec *macaddr; 25000cc8ae86Sav cs_status_t *cs_status; 25010cc8ae86Sav int len, len1, i, bk, cc; 2502738dd194Shyw mc_rsaddr_info_t rsaddr; 250325cf1a30Sjl uint32_t mirr; 25040cc8ae86Sav int nbanks = 0; 25050cc8ae86Sav uint64_t nbytes = 0; 2506d8a0cca9Swh int mirror_mode = 0; 2507d8a0cca9Swh int ret; 250825cf1a30Sjl 250925cf1a30Sjl /* 251025cf1a30Sjl * Get configurations from "pseudo-mc" node which includes: 251125cf1a30Sjl * board# : LSB number 251225cf1a30Sjl * mac-addr : physical base address of MAC registers 251325cf1a30Sjl * csX-mac-pa-trans-table: translation table from DIMM address 251425cf1a30Sjl * to physical address or vice versa. 251525cf1a30Sjl */ 251625cf1a30Sjl mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip, 2517d8a0cca9Swh DDI_PROP_DONTPASS, "board#", -1); 251825cf1a30Sjl 25190cc8ae86Sav if (mcp->mc_board_num == -1) { 25200cc8ae86Sav return (DDI_FAILURE); 25210cc8ae86Sav } 25220cc8ae86Sav 252325cf1a30Sjl /* 252425cf1a30Sjl * Get start address in this CAB. It can be gotten from 252525cf1a30Sjl * "sb-mem-ranges" property. 252625cf1a30Sjl */ 252725cf1a30Sjl 252825cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 252925cf1a30Sjl return (DDI_FAILURE); 253025cf1a30Sjl } 253125cf1a30Sjl /* get mac-pa trans tables */ 253225cf1a30Sjl for (i = 0; i < MC_TT_CS; i++) { 253325cf1a30Sjl len = MC_TT_ENTRIES; 253425cf1a30Sjl cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip, 2535d8a0cca9Swh DDI_PROP_DONTPASS, mc_tbl_name[i], 2536d8a0cca9Swh (caddr_t)mcp->mc_trans_table[i], &len); 253725cf1a30Sjl 253825cf1a30Sjl if (cc != DDI_SUCCESS) { 253925cf1a30Sjl bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES); 254025cf1a30Sjl } 254125cf1a30Sjl } 254225cf1a30Sjl mcp->mlist = NULL; 254325cf1a30Sjl 254425cf1a30Sjl mc_get_mlist(mcp); 254525cf1a30Sjl 254625cf1a30Sjl /* initialize bank informations */ 254725cf1a30Sjl cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 2548d8a0cca9Swh "mc-addr", (caddr_t)&macaddr, &len); 254925cf1a30Sjl if (cc != DDI_SUCCESS) { 255025cf1a30Sjl cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc); 255125cf1a30Sjl return (DDI_FAILURE); 255225cf1a30Sjl } 255325cf1a30Sjl 25540cc8ae86Sav cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 2555d8a0cca9Swh "cs-status", (caddr_t)&cs_status, &len1); 255625cf1a30Sjl 25570cc8ae86Sav if (cc != DDI_SUCCESS) { 25580cc8ae86Sav if (len > 0) 25590cc8ae86Sav kmem_free(macaddr, len); 25600cc8ae86Sav cmn_err(CE_WARN, "Cannot get cs-status. err=%d\n", cc); 25610cc8ae86Sav return (DDI_FAILURE); 25620cc8ae86Sav } 2563aeb241b2Sav /* get the physical board number for a given logical board number */ 2564aeb241b2Sav mcp->mc_phys_board_num = mc_opl_get_physical_board(mcp->mc_board_num); 2565aeb241b2Sav 2566aeb241b2Sav if (mcp->mc_phys_board_num < 0) { 2567aeb241b2Sav if (len > 0) 2568aeb241b2Sav kmem_free(macaddr, len); 2569aeb241b2Sav cmn_err(CE_WARN, "Unable to obtain the physical board number"); 2570aeb241b2Sav return (DDI_FAILURE); 2571aeb241b2Sav } 257225cf1a30Sjl 25730cc8ae86Sav mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL); 25740cc8ae86Sav 25750cc8ae86Sav for (i = 0; i < len1 / sizeof (cs_status_t); i++) { 25760cc8ae86Sav nbytes += ((uint64_t)cs_status[i].cs_avail_hi << 32) | 2577d8a0cca9Swh ((uint64_t)cs_status[i].cs_avail_low); 25780cc8ae86Sav } 25790cc8ae86Sav if (len1 > 0) 25800cc8ae86Sav kmem_free(cs_status, len1); 25810cc8ae86Sav nbanks = len / sizeof (struct mc_addr_spec); 25820cc8ae86Sav 25830cc8ae86Sav if (nbanks > 0) 25840cc8ae86Sav nbytes /= nbanks; 25850cc8ae86Sav else { 25860cc8ae86Sav /* No need to free macaddr because len must be 0 */ 25870cc8ae86Sav mcp->mc_status |= MC_MEMORYLESS; 25880cc8ae86Sav return (DDI_SUCCESS); 25890cc8ae86Sav } 25900cc8ae86Sav 25910cc8ae86Sav for (i = 0; i < BANKNUM_PER_SB; i++) { 25920cc8ae86Sav mcp->mc_scf_retry[i] = 0; 25930cc8ae86Sav mcp->mc_period[i] = 0; 25940cc8ae86Sav mcp->mc_speedup_period[i] = 0; 25950cc8ae86Sav } 25960cc8ae86Sav 25970cc8ae86Sav /* 25980cc8ae86Sav * Get the memory size here. Let it be B (bytes). 25990cc8ae86Sav * Let T be the time in u.s. to scan 64 bytes. 26000cc8ae86Sav * If we want to complete 1 round of scanning in P seconds. 26010cc8ae86Sav * 26020cc8ae86Sav * B * T * 10^(-6) = P 26030cc8ae86Sav * --------------- 26040cc8ae86Sav * 64 26050cc8ae86Sav * 26060cc8ae86Sav * T = P * 64 * 10^6 26070cc8ae86Sav * ------------- 26080cc8ae86Sav * B 26090cc8ae86Sav * 26100cc8ae86Sav * = P * 64 * 10^6 26110cc8ae86Sav * ------------- 26120cc8ae86Sav * B 26130cc8ae86Sav * 26140cc8ae86Sav * The timing bits are set in PTRL_CNTL[28:26] where 26150cc8ae86Sav * 26160cc8ae86Sav * 0 - 1 m.s 26170cc8ae86Sav * 1 - 512 u.s. 26180cc8ae86Sav * 10 - 256 u.s. 26190cc8ae86Sav * 11 - 128 u.s. 26200cc8ae86Sav * 100 - 64 u.s. 26210cc8ae86Sav * 101 - 32 u.s. 26220cc8ae86Sav * 110 - 0 u.s. 26230cc8ae86Sav * 111 - reserved. 26240cc8ae86Sav * 26250cc8ae86Sav * 26260cc8ae86Sav * a[0] = 110, a[1] = 101, ... a[6] = 0 26270cc8ae86Sav * 26280cc8ae86Sav * cs-status property is int x 7 26290cc8ae86Sav * 0 - cs# 26300cc8ae86Sav * 1 - cs-status 26310cc8ae86Sav * 2 - cs-avail.hi 26320cc8ae86Sav * 3 - cs-avail.lo 26330cc8ae86Sav * 4 - dimm-capa.hi 26340cc8ae86Sav * 5 - dimm-capa.lo 26350cc8ae86Sav * 6 - #of dimms 26360cc8ae86Sav */ 26370cc8ae86Sav 26380cc8ae86Sav if (nbytes > 0) { 26390cc8ae86Sav int i; 26400cc8ae86Sav uint64_t ms; 26410cc8ae86Sav ms = ((uint64_t)mc_scan_period * 64 * 1000000)/nbytes; 26420cc8ae86Sav mcp->mc_speed = mc_scan_speeds[MC_MAX_SPEEDS - 1].mc_speeds; 26430cc8ae86Sav for (i = 0; i < MC_MAX_SPEEDS - 1; i++) { 26440cc8ae86Sav if (ms < mc_scan_speeds[i + 1].mc_period) { 26450cc8ae86Sav mcp->mc_speed = mc_scan_speeds[i].mc_speeds; 26460cc8ae86Sav break; 26470cc8ae86Sav } 26480cc8ae86Sav } 26490cc8ae86Sav } else 26500cc8ae86Sav mcp->mc_speed = 0; 26510cc8ae86Sav 26520cc8ae86Sav 26530cc8ae86Sav for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) { 26540cc8ae86Sav struct mc_bank *bankp; 2655*601c2e1eSdhain mc_retry_info_t *retry; 26560cc8ae86Sav uint32_t reg; 2657*601c2e1eSdhain int k; 26580cc8ae86Sav 26590cc8ae86Sav /* 26600cc8ae86Sav * setup bank 26610cc8ae86Sav */ 26620cc8ae86Sav bk = macaddr[i].bank; 26630cc8ae86Sav bankp = &(mcp->mc_bank[bk]); 26640cc8ae86Sav bankp->mcb_status = BANK_INSTALLED; 26650cc8ae86Sav bankp->mcb_reg_base = REGS_PA(macaddr, i); 26660cc8ae86Sav 2667*601c2e1eSdhain bankp->mcb_retry_freelist = NULL; 2668*601c2e1eSdhain bankp->mcb_retry_pending = NULL; 2669*601c2e1eSdhain bankp->mcb_active = NULL; 2670*601c2e1eSdhain retry = &bankp->mcb_retry_infos[0]; 2671*601c2e1eSdhain for (k = 0; k < MC_RETRY_COUNT; k++, retry++) { 2672*601c2e1eSdhain mc_retry_info_put(&bankp->mcb_retry_freelist, retry); 2673*601c2e1eSdhain } 2674*601c2e1eSdhain 26750cc8ae86Sav reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk)); 26760cc8ae86Sav bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS); 267725cf1a30Sjl 267825cf1a30Sjl /* 267925cf1a30Sjl * check if mirror mode 268025cf1a30Sjl */ 268125cf1a30Sjl mirr = LD_MAC_REG(MAC_MIRR(mcp, bk)); 268225cf1a30Sjl 268325cf1a30Sjl if (mirr & MAC_MIRR_MIRROR_MODE) { 2684d8a0cca9Swh MC_LOG("Mirror -> /LSB%d/B%d\n", mcp->mc_board_num, 2685d8a0cca9Swh bk); 268625cf1a30Sjl bankp->mcb_status |= BANK_MIRROR_MODE; 2687d8a0cca9Swh mirror_mode = 1; 268825cf1a30Sjl /* 268925cf1a30Sjl * The following bit is only used for 269025cf1a30Sjl * error injection. We should clear it 269125cf1a30Sjl */ 269225cf1a30Sjl if (mirr & MAC_MIRR_BANK_EXCLUSIVE) 2693d8a0cca9Swh ST_MAC_REG(MAC_MIRR(mcp, bk), 0); 269425cf1a30Sjl } 269525cf1a30Sjl 269625cf1a30Sjl /* 269725cf1a30Sjl * restart if not mirror mode or the other bank 269825cf1a30Sjl * of the mirror is not running 269925cf1a30Sjl */ 270025cf1a30Sjl if (!(mirr & MAC_MIRR_MIRROR_MODE) || 2701d8a0cca9Swh !(mcp->mc_bank[bk^1].mcb_status & BANK_PTRL_RUNNING)) { 2702d8a0cca9Swh MC_LOG("Starting up /LSB%d/B%d\n", mcp->mc_board_num, 2703d8a0cca9Swh bk); 2704738dd194Shyw get_ptrl_start_address(mcp, bk, &rsaddr.mi_restartaddr); 2705738dd194Shyw rsaddr.mi_valid = 0; 2706738dd194Shyw rsaddr.mi_injectrestart = 0; 2707738dd194Shyw restart_patrol(mcp, bk, &rsaddr); 270825cf1a30Sjl } else { 270925cf1a30Sjl MC_LOG("Not starting up /LSB%d/B%d\n", 2710d8a0cca9Swh mcp->mc_board_num, bk); 271125cf1a30Sjl } 271225cf1a30Sjl bankp->mcb_status |= BANK_PTRL_RUNNING; 271325cf1a30Sjl } 27140cc8ae86Sav if (len > 0) 27150cc8ae86Sav kmem_free(macaddr, len); 27160cc8ae86Sav 2717d8a0cca9Swh ret = ndi_prop_update_int(DDI_DEV_T_NONE, mcp->mc_dip, "mirror-mode", 2718d8a0cca9Swh mirror_mode); 2719d8a0cca9Swh if (ret != DDI_PROP_SUCCESS) { 2720d8a0cca9Swh cmn_err(CE_WARN, "Unable to update mirror-mode property"); 2721d8a0cca9Swh } 2722d8a0cca9Swh 27230cc8ae86Sav mcp->mc_dimm_list = mc_get_dimm_list(mcp); 272425cf1a30Sjl 272525cf1a30Sjl /* 272625cf1a30Sjl * set interval in HZ. 272725cf1a30Sjl */ 272825cf1a30Sjl mcp->mc_last_error = 0; 272925cf1a30Sjl 273025cf1a30Sjl /* restart memory patrol checking */ 273125cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 273225cf1a30Sjl 273325cf1a30Sjl return (DDI_SUCCESS); 273425cf1a30Sjl } 273525cf1a30Sjl 273625cf1a30Sjl int 273725cf1a30Sjl mc_board_del(mc_opl_t *mcp) 273825cf1a30Sjl { 273925cf1a30Sjl int i; 274025cf1a30Sjl scf_log_t *p; 274125cf1a30Sjl 274225cf1a30Sjl /* 274325cf1a30Sjl * cleanup mac state 274425cf1a30Sjl */ 274525cf1a30Sjl mutex_enter(&mcp->mc_lock); 27460cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 27470cc8ae86Sav mutex_exit(&mcp->mc_lock); 27480cc8ae86Sav mutex_destroy(&mcp->mc_lock); 27490cc8ae86Sav return (DDI_SUCCESS); 27500cc8ae86Sav } 275125cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 275225cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 275325cf1a30Sjl mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED; 275425cf1a30Sjl } 275525cf1a30Sjl } 275625cf1a30Sjl 275725cf1a30Sjl /* stop memory patrol checking */ 2758738dd194Shyw mcp->mc_status &= ~MC_POLL_RUNNING; 275925cf1a30Sjl 276025cf1a30Sjl /* just throw away all the scf logs */ 27610cc8ae86Sav for (i = 0; i < BANKNUM_PER_SB; i++) { 2762d8a0cca9Swh while ((p = mcp->mc_scf_log[i]) != NULL) { 2763d8a0cca9Swh mcp->mc_scf_log[i] = p->sl_next; 2764d8a0cca9Swh mcp->mc_scf_total[i]--; 2765d8a0cca9Swh kmem_free(p, sizeof (scf_log_t)); 2766d8a0cca9Swh } 276725cf1a30Sjl } 276825cf1a30Sjl 276925cf1a30Sjl if (mcp->mlist) 277025cf1a30Sjl mc_memlist_delete(mcp->mlist); 277125cf1a30Sjl 27720cc8ae86Sav if (mcp->mc_dimm_list) 27730cc8ae86Sav mc_free_dimm_list(mcp->mc_dimm_list); 27740cc8ae86Sav 277525cf1a30Sjl mutex_exit(&mcp->mc_lock); 277625cf1a30Sjl 277725cf1a30Sjl mutex_destroy(&mcp->mc_lock); 277825cf1a30Sjl return (DDI_SUCCESS); 277925cf1a30Sjl } 278025cf1a30Sjl 278125cf1a30Sjl int 278225cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag) 278325cf1a30Sjl { 278425cf1a30Sjl /* stop memory patrol checking */ 278525cf1a30Sjl mutex_enter(&mcp->mc_lock); 27860cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 27870cc8ae86Sav mutex_exit(&mcp->mc_lock); 27880cc8ae86Sav return (DDI_SUCCESS); 27890cc8ae86Sav } 27900cc8ae86Sav 2791738dd194Shyw mcp->mc_status &= ~MC_POLL_RUNNING; 2792738dd194Shyw 279325cf1a30Sjl mcp->mc_status |= flag; 279425cf1a30Sjl mutex_exit(&mcp->mc_lock); 279525cf1a30Sjl 279625cf1a30Sjl return (DDI_SUCCESS); 279725cf1a30Sjl } 279825cf1a30Sjl 279968ac2337Sjl void 280068ac2337Sjl opl_mc_update_mlist(void) 280168ac2337Sjl { 280268ac2337Sjl int i; 280368ac2337Sjl mc_opl_t *mcp; 280468ac2337Sjl 280568ac2337Sjl /* 280668ac2337Sjl * memory information is not updated until 280768ac2337Sjl * the post attach/detach stage during DR. 280868ac2337Sjl * This interface is used by dr_mem to inform 280968ac2337Sjl * mc-opl to update the mlist. 281068ac2337Sjl */ 281168ac2337Sjl 281268ac2337Sjl mutex_enter(&mcmutex); 281368ac2337Sjl for (i = 0; i < OPL_MAX_BOARDS; i++) { 281468ac2337Sjl if ((mcp = mc_instances[i]) == NULL) 281568ac2337Sjl continue; 281668ac2337Sjl mutex_enter(&mcp->mc_lock); 281768ac2337Sjl if (mcp->mlist) 281868ac2337Sjl mc_memlist_delete(mcp->mlist); 281968ac2337Sjl mcp->mlist = NULL; 282068ac2337Sjl mc_get_mlist(mcp); 282168ac2337Sjl mutex_exit(&mcp->mc_lock); 282268ac2337Sjl } 282368ac2337Sjl mutex_exit(&mcmutex); 282468ac2337Sjl } 282568ac2337Sjl 282625cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */ 282725cf1a30Sjl 282825cf1a30Sjl int 282925cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag) 283025cf1a30Sjl { 283125cf1a30Sjl int i; 283225cf1a30Sjl uint64_t basepa; 283325cf1a30Sjl 283425cf1a30Sjl mutex_enter(&mcp->mc_lock); 28350cc8ae86Sav if (mcp->mc_status & MC_MEMORYLESS) { 28360cc8ae86Sav mutex_exit(&mcp->mc_lock); 28370cc8ae86Sav return (DDI_SUCCESS); 28380cc8ae86Sav } 283925cf1a30Sjl basepa = mcp->mc_start_address; 284025cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 284125cf1a30Sjl mutex_exit(&mcp->mc_lock); 284225cf1a30Sjl return (DDI_FAILURE); 284325cf1a30Sjl } 284425cf1a30Sjl 284525cf1a30Sjl if (basepa != mcp->mc_start_address) { 284625cf1a30Sjl if (mcp->mlist) 284725cf1a30Sjl mc_memlist_delete(mcp->mlist); 284825cf1a30Sjl mcp->mlist = NULL; 284925cf1a30Sjl mc_get_mlist(mcp); 285025cf1a30Sjl } 285125cf1a30Sjl 285225cf1a30Sjl mcp->mc_status &= ~flag; 285325cf1a30Sjl 285425cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 285525cf1a30Sjl mutex_exit(&mcp->mc_lock); 285625cf1a30Sjl return (DDI_SUCCESS); 285725cf1a30Sjl } 285825cf1a30Sjl 285925cf1a30Sjl if (!(mcp->mc_status & MC_POLL_RUNNING)) { 286025cf1a30Sjl /* restart memory patrol checking */ 286125cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 286225cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 286325cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 2864*601c2e1eSdhain mc_check_errors_func(mcp); 286525cf1a30Sjl } 286625cf1a30Sjl } 286725cf1a30Sjl } 286825cf1a30Sjl mutex_exit(&mcp->mc_lock); 286925cf1a30Sjl 287025cf1a30Sjl return (DDI_SUCCESS); 287125cf1a30Sjl } 287225cf1a30Sjl 287325cf1a30Sjl static mc_opl_t * 287425cf1a30Sjl mc_pa_to_mcp(uint64_t pa) 287525cf1a30Sjl { 28760cc8ae86Sav mc_opl_t *mcp; 28770cc8ae86Sav int i; 28780cc8ae86Sav 287925cf1a30Sjl ASSERT(MUTEX_HELD(&mcmutex)); 28800cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 28810cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 28820cc8ae86Sav continue; 288325cf1a30Sjl /* if mac patrol is suspended, we cannot rely on it */ 28840cc8ae86Sav if (!(mcp->mc_status & MC_POLL_RUNNING) || 2885d8a0cca9Swh (mcp->mc_status & MC_SOFT_SUSPENDED)) 288625cf1a30Sjl continue; 2887738dd194Shyw if (mc_rangecheck_pa(mcp, pa)) { 28880cc8ae86Sav return (mcp); 288925cf1a30Sjl } 289025cf1a30Sjl } 289125cf1a30Sjl return (NULL); 289225cf1a30Sjl } 289325cf1a30Sjl 289425cf1a30Sjl /* 289525cf1a30Sjl * Get Physical Board number from Logical one. 289625cf1a30Sjl */ 289725cf1a30Sjl static int 289825cf1a30Sjl mc_opl_get_physical_board(int sb) 289925cf1a30Sjl { 290025cf1a30Sjl if (&opl_get_physical_board) { 290125cf1a30Sjl return (opl_get_physical_board(sb)); 290225cf1a30Sjl } 290325cf1a30Sjl 290425cf1a30Sjl cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n"); 290525cf1a30Sjl return (-1); 290625cf1a30Sjl } 290725cf1a30Sjl 290825cf1a30Sjl /* ARGSUSED */ 290925cf1a30Sjl int 291025cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen, 291125cf1a30Sjl int *lenp) 291225cf1a30Sjl { 29130cc8ae86Sav int i; 2914aeb241b2Sav int j; 291525cf1a30Sjl int sb; 29160cc8ae86Sav int bank; 2917aeb241b2Sav int cs; 29180cc8ae86Sav mc_opl_t *mcp; 29190cc8ae86Sav char memb_num; 292025cf1a30Sjl 292125cf1a30Sjl mutex_enter(&mcmutex); 292225cf1a30Sjl 292325cf1a30Sjl if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) || 2924d8a0cca9Swh (!pa_is_valid(mcp, flt_addr))) { 292525cf1a30Sjl mutex_exit(&mcmutex); 292625cf1a30Sjl if (snprintf(buf, buflen, "UNKNOWN") >= buflen) { 292725cf1a30Sjl return (ENOSPC); 292825cf1a30Sjl } else { 292925cf1a30Sjl if (lenp) 293025cf1a30Sjl *lenp = strlen(buf); 293125cf1a30Sjl } 293225cf1a30Sjl return (0); 293325cf1a30Sjl } 293425cf1a30Sjl 293525cf1a30Sjl bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address); 2936aeb241b2Sav sb = mcp->mc_phys_board_num; 2937aeb241b2Sav cs = pa_to_cs(mcp, flt_addr - mcp->mc_start_address); 293825cf1a30Sjl 293925cf1a30Sjl if (sb == -1) { 294025cf1a30Sjl mutex_exit(&mcmutex); 294125cf1a30Sjl return (ENXIO); 294225cf1a30Sjl } 294325cf1a30Sjl 29440cc8ae86Sav if (plat_model == MODEL_DC) { 29450cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(0, bank, 0); 2946aeb241b2Sav j = (cs == 0) ? i : i + 2; 2947aeb241b2Sav snprintf(buf, buflen, "/%s%02d/MEM%s MEM%s", 29480cc8ae86Sav model_names[plat_model].unit_name, sb, 2949aeb241b2Sav mc_dc_dimm_unum_table[j], 2950aeb241b2Sav mc_dc_dimm_unum_table[j + 1]); 295125cf1a30Sjl } else { 29520cc8ae86Sav i = BD_BK_SLOT_TO_INDEX(sb, bank, 0); 2953aeb241b2Sav j = (cs == 0) ? i : i + 2; 29540cc8ae86Sav memb_num = mc_ff_dimm_unum_table[i][0]; 2955aeb241b2Sav snprintf(buf, buflen, "/%s/%s%c/MEM%s MEM%s", 29560cc8ae86Sav model_names[plat_model].unit_name, 29570cc8ae86Sav model_names[plat_model].mem_name, memb_num, 2958aeb241b2Sav &mc_ff_dimm_unum_table[j][1], 2959aeb241b2Sav &mc_ff_dimm_unum_table[j + 1][1]); 29600cc8ae86Sav } 29610cc8ae86Sav if (lenp) { 29620cc8ae86Sav *lenp = strlen(buf); 296325cf1a30Sjl } 296425cf1a30Sjl mutex_exit(&mcmutex); 296525cf1a30Sjl return (0); 296625cf1a30Sjl } 296725cf1a30Sjl 296825cf1a30Sjl int 29690cc8ae86Sav opl_mc_suspend(void) 297025cf1a30Sjl { 297125cf1a30Sjl mc_opl_t *mcp; 29720cc8ae86Sav int i; 297325cf1a30Sjl 297425cf1a30Sjl mutex_enter(&mcmutex); 29750cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 29760cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 29770cc8ae86Sav continue; 29780cc8ae86Sav mc_suspend(mcp, MC_SOFT_SUSPENDED); 297925cf1a30Sjl } 298025cf1a30Sjl mutex_exit(&mcmutex); 29810cc8ae86Sav 298225cf1a30Sjl return (0); 298325cf1a30Sjl } 298425cf1a30Sjl 298525cf1a30Sjl int 29860cc8ae86Sav opl_mc_resume(void) 298725cf1a30Sjl { 298825cf1a30Sjl mc_opl_t *mcp; 29890cc8ae86Sav int i; 299025cf1a30Sjl 299125cf1a30Sjl mutex_enter(&mcmutex); 29920cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 29930cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 29940cc8ae86Sav continue; 29950cc8ae86Sav mc_resume(mcp, MC_SOFT_SUSPENDED); 299625cf1a30Sjl } 299725cf1a30Sjl mutex_exit(&mcmutex); 29980cc8ae86Sav 299925cf1a30Sjl return (0); 300025cf1a30Sjl } 300125cf1a30Sjl static void 300225cf1a30Sjl insert_mcp(mc_opl_t *mcp) 300325cf1a30Sjl { 300425cf1a30Sjl mutex_enter(&mcmutex); 30050cc8ae86Sav if (mc_instances[mcp->mc_board_num] != NULL) { 30060cc8ae86Sav MC_LOG("mc-opl instance for board# %d already exists\n", 3007d8a0cca9Swh mcp->mc_board_num); 30080cc8ae86Sav } 30090cc8ae86Sav mc_instances[mcp->mc_board_num] = mcp; 301025cf1a30Sjl mutex_exit(&mcmutex); 301125cf1a30Sjl } 301225cf1a30Sjl 301325cf1a30Sjl static void 301425cf1a30Sjl delete_mcp(mc_opl_t *mcp) 301525cf1a30Sjl { 30160cc8ae86Sav mutex_enter(&mcmutex); 30170cc8ae86Sav mc_instances[mcp->mc_board_num] = 0; 30180cc8ae86Sav mutex_exit(&mcmutex); 301925cf1a30Sjl } 302025cf1a30Sjl 302125cf1a30Sjl /* Error injection interface */ 302225cf1a30Sjl 3023cfb9e062Shyw static void 3024cfb9e062Shyw mc_lock_va(uint64_t pa, caddr_t new_va) 3025cfb9e062Shyw { 3026cfb9e062Shyw tte_t tte; 3027cfb9e062Shyw 3028738dd194Shyw vtag_flushpage(new_va, (uint64_t)ksfmmup); 3029d8a0cca9Swh sfmmu_memtte(&tte, pa >> PAGESHIFT, PROC_DATA|HAT_NOSYNC, TTE8K); 3030cfb9e062Shyw tte.tte_intlo |= TTE_LCK_INT; 3031cfb9e062Shyw sfmmu_dtlb_ld_kva(new_va, &tte); 3032cfb9e062Shyw } 3033cfb9e062Shyw 3034cfb9e062Shyw static void 3035cfb9e062Shyw mc_unlock_va(caddr_t va) 3036cfb9e062Shyw { 3037cfb9e062Shyw vtag_flushpage(va, (uint64_t)ksfmmup); 3038cfb9e062Shyw } 3039cfb9e062Shyw 304025cf1a30Sjl /* ARGSUSED */ 304125cf1a30Sjl int 304225cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags) 304325cf1a30Sjl { 304425cf1a30Sjl mc_opl_t *mcp; 304525cf1a30Sjl int bank; 304625cf1a30Sjl uint32_t dimm_addr; 304725cf1a30Sjl uint32_t cntl; 3048738dd194Shyw mc_rsaddr_info_t rsaddr; 304925cf1a30Sjl uint32_t data, stat; 305025cf1a30Sjl int both_sides = 0; 305125cf1a30Sjl uint64_t pa0; 3052cfb9e062Shyw int extra_injection_needed = 0; 305325cf1a30Sjl extern void cpu_flush_ecache(void); 305425cf1a30Sjl 305525cf1a30Sjl MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags); 305625cf1a30Sjl 305725cf1a30Sjl mutex_enter(&mcmutex); 305825cf1a30Sjl if ((mcp = mc_pa_to_mcp(pa)) == NULL) { 305925cf1a30Sjl mutex_exit(&mcmutex); 306025cf1a30Sjl MC_LOG("mc_inject_error: invalid pa\n"); 306125cf1a30Sjl return (ENOTSUP); 306225cf1a30Sjl } 306325cf1a30Sjl 306425cf1a30Sjl mutex_enter(&mcp->mc_lock); 306525cf1a30Sjl mutex_exit(&mcmutex); 306625cf1a30Sjl 306725cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 306825cf1a30Sjl mutex_exit(&mcp->mc_lock); 306925cf1a30Sjl MC_LOG("mc-opl has been suspended. No error injection.\n"); 307025cf1a30Sjl return (EBUSY); 307125cf1a30Sjl } 307225cf1a30Sjl 307325cf1a30Sjl /* convert pa to offset within the board */ 307425cf1a30Sjl MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address); 307525cf1a30Sjl 307625cf1a30Sjl if (!pa_is_valid(mcp, pa)) { 307725cf1a30Sjl mutex_exit(&mcp->mc_lock); 307825cf1a30Sjl return (EINVAL); 307925cf1a30Sjl } 308025cf1a30Sjl 308125cf1a30Sjl pa0 = pa - mcp->mc_start_address; 308225cf1a30Sjl 308325cf1a30Sjl bank = pa_to_bank(mcp, pa0); 308425cf1a30Sjl 308525cf1a30Sjl if (flags & MC_INJECT_FLAG_OTHER) 308625cf1a30Sjl bank = bank ^ 1; 308725cf1a30Sjl 308825cf1a30Sjl if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) { 308925cf1a30Sjl mutex_exit(&mcp->mc_lock); 309025cf1a30Sjl MC_LOG("Not mirror mode\n"); 309125cf1a30Sjl return (EINVAL); 309225cf1a30Sjl } 309325cf1a30Sjl 309425cf1a30Sjl dimm_addr = pa_to_dimm(mcp, pa0); 309525cf1a30Sjl 3096d8a0cca9Swh MC_LOG("injecting error to /LSB%d/B%d/%x\n", mcp->mc_board_num, bank, 3097d8a0cca9Swh dimm_addr); 309825cf1a30Sjl 309925cf1a30Sjl 310025cf1a30Sjl switch (error_type) { 310125cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 310225cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 310325cf1a30Sjl case MC_INJECT_MUE: 310425cf1a30Sjl both_sides = 1; 310525cf1a30Sjl } 310625cf1a30Sjl 310725cf1a30Sjl if (flags & MC_INJECT_FLAG_RESET) 310825cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0); 310925cf1a30Sjl 311025cf1a30Sjl ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK); 311125cf1a30Sjl 311225cf1a30Sjl if (both_sides) { 311325cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0); 3114d8a0cca9Swh ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), dimm_addr & 3115d8a0cca9Swh MAC_EG_ADD_MASK); 311625cf1a30Sjl } 311725cf1a30Sjl 311825cf1a30Sjl switch (error_type) { 311925cf1a30Sjl case MC_INJECT_SUE: 3120cfb9e062Shyw extra_injection_needed = 1; 3121cfb9e062Shyw /*FALLTHROUGH*/ 3122cfb9e062Shyw case MC_INJECT_UE: 312325cf1a30Sjl case MC_INJECT_MUE: 312425cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 3125d8a0cca9Swh cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 | 3126d8a0cca9Swh MAC_EG_FORCE_READ16 | MAC_EG_RDERR_ONCE; 312725cf1a30Sjl } else { 3128d8a0cca9Swh cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR00 | 3129d8a0cca9Swh MAC_EG_FORCE_DERR16 | MAC_EG_DERR_ONCE; 313025cf1a30Sjl } 313125cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 313225cf1a30Sjl break; 313325cf1a30Sjl case MC_INJECT_INTERMITTENT_CE: 313425cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 313525cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 3136d8a0cca9Swh cntl = MAC_EG_ADD_FIX |MAC_EG_FORCE_READ00 | 3137d8a0cca9Swh MAC_EG_RDERR_ONCE; 313825cf1a30Sjl } else { 3139d8a0cca9Swh cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 | 3140d8a0cca9Swh MAC_EG_DERR_ONCE; 314125cf1a30Sjl } 3142cfb9e062Shyw extra_injection_needed = 1; 314325cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 314425cf1a30Sjl break; 314525cf1a30Sjl case MC_INJECT_PERMANENT_CE: 314625cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 314725cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 3148d8a0cca9Swh cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_READ00 | 3149d8a0cca9Swh MAC_EG_RDERR_ALWAYS; 315025cf1a30Sjl } else { 3151d8a0cca9Swh cntl = MAC_EG_ADD_FIX | MAC_EG_FORCE_DERR16 | 3152d8a0cca9Swh MAC_EG_DERR_ALWAYS; 315325cf1a30Sjl } 315425cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 315525cf1a30Sjl break; 315625cf1a30Sjl case MC_INJECT_CMPE: 315725cf1a30Sjl data = 0xabcdefab; 315825cf1a30Sjl stphys(pa, data); 315925cf1a30Sjl cpu_flush_ecache(); 316025cf1a30Sjl MC_LOG("CMPE: writing data %x to %lx\n", data, pa); 316125cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE); 316225cf1a30Sjl stphys(pa, data ^ 0xffffffff); 3163738dd194Shyw membar_sync(); 316425cf1a30Sjl cpu_flush_ecache(); 316525cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), 0); 316625cf1a30Sjl MC_LOG("CMPE: write new data %xto %lx\n", data, pa); 316725cf1a30Sjl cntl = 0; 316825cf1a30Sjl break; 316925cf1a30Sjl case MC_INJECT_NOP: 317025cf1a30Sjl cntl = 0; 317125cf1a30Sjl break; 317225cf1a30Sjl default: 317325cf1a30Sjl MC_LOG("mc_inject_error: invalid option\n"); 317425cf1a30Sjl cntl = 0; 317525cf1a30Sjl } 317625cf1a30Sjl 317725cf1a30Sjl if (cntl) { 317825cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK); 317925cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 318025cf1a30Sjl 318125cf1a30Sjl if (both_sides) { 318225cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 3183d8a0cca9Swh MAC_EG_SETUP_MASK); 318425cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 318525cf1a30Sjl } 318625cf1a30Sjl } 318725cf1a30Sjl 318825cf1a30Sjl /* 318925cf1a30Sjl * For all injection cases except compare error, we 319025cf1a30Sjl * must write to the PA to trigger the error. 319125cf1a30Sjl */ 319225cf1a30Sjl 319325cf1a30Sjl if (flags & MC_INJECT_FLAG_ST) { 319425cf1a30Sjl data = 0xf0e0d0c0; 319525cf1a30Sjl MC_LOG("Writing %x to %lx\n", data, pa); 319625cf1a30Sjl stphys(pa, data); 319725cf1a30Sjl cpu_flush_ecache(); 319825cf1a30Sjl } 319925cf1a30Sjl 320025cf1a30Sjl 320125cf1a30Sjl if (flags & MC_INJECT_FLAG_LD) { 3202cfb9e062Shyw if (flags & MC_INJECT_FLAG_PREFETCH) { 3203cfb9e062Shyw /* 3204cfb9e062Shyw * Use strong prefetch operation to 3205cfb9e062Shyw * inject MI errors. 3206cfb9e062Shyw */ 3207cfb9e062Shyw page_t *pp; 3208cfb9e062Shyw extern void mc_prefetch(caddr_t); 3209cfb9e062Shyw 3210cfb9e062Shyw MC_LOG("prefetch\n"); 3211cfb9e062Shyw 3212cfb9e062Shyw pp = page_numtopp_nolock(pa >> PAGESHIFT); 3213cfb9e062Shyw if (pp != NULL) { 3214cfb9e062Shyw caddr_t va, va1; 3215cfb9e062Shyw 3216cfb9e062Shyw va = ppmapin(pp, PROT_READ|PROT_WRITE, 3217d8a0cca9Swh (caddr_t)-1); 3218cfb9e062Shyw kpreempt_disable(); 3219cfb9e062Shyw mc_lock_va((uint64_t)pa, va); 3220cfb9e062Shyw va1 = va + (pa & (PAGESIZE - 1)); 3221cfb9e062Shyw mc_prefetch(va1); 3222cfb9e062Shyw mc_unlock_va(va); 3223cfb9e062Shyw kpreempt_enable(); 3224cfb9e062Shyw ppmapout(va); 3225cfb9e062Shyw 3226cfb9e062Shyw /* 3227cfb9e062Shyw * For MI errors, we need one extra 3228cfb9e062Shyw * injection for HW patrol to stop. 3229cfb9e062Shyw */ 3230cfb9e062Shyw extra_injection_needed = 1; 323125cf1a30Sjl } else { 3232cfb9e062Shyw cmn_err(CE_WARN, "Cannot find page structure" 3233d8a0cca9Swh " for PA %lx\n", pa); 323425cf1a30Sjl } 323525cf1a30Sjl } else { 323625cf1a30Sjl MC_LOG("Reading from %lx\n", pa); 323725cf1a30Sjl data = ldphys(pa); 323825cf1a30Sjl MC_LOG("data = %x\n", data); 323925cf1a30Sjl } 3240cfb9e062Shyw 3241cfb9e062Shyw if (extra_injection_needed) { 3242cfb9e062Shyw /* 3243cfb9e062Shyw * These are the injection cases where the 3244cfb9e062Shyw * requested injected errors will not cause the HW 3245cfb9e062Shyw * patrol to stop. For these cases, we need to inject 3246cfb9e062Shyw * an extra 'real' PTRL error to force the 3247cfb9e062Shyw * HW patrol to stop so that we can report the 3248cfb9e062Shyw * errors injected. Note that we cannot read 3249cfb9e062Shyw * and report error status while the HW patrol 3250cfb9e062Shyw * is running. 3251cfb9e062Shyw */ 3252cfb9e062Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 3253d8a0cca9Swh cntl & MAC_EG_SETUP_MASK); 3254cfb9e062Shyw ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 3255cfb9e062Shyw 3256cfb9e062Shyw if (both_sides) { 3257d8a0cca9Swh ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 3258d8a0cca9Swh MAC_EG_SETUP_MASK); 3259d8a0cca9Swh ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 3260cfb9e062Shyw } 3261cfb9e062Shyw data = 0xf0e0d0c0; 3262cfb9e062Shyw MC_LOG("Writing %x to %lx\n", data, pa); 3263cfb9e062Shyw stphys(pa, data); 3264cfb9e062Shyw cpu_flush_ecache(); 3265cfb9e062Shyw } 326625cf1a30Sjl } 326725cf1a30Sjl 326825cf1a30Sjl if (flags & MC_INJECT_FLAG_RESTART) { 326925cf1a30Sjl MC_LOG("Restart patrol\n"); 3270738dd194Shyw rsaddr.mi_restartaddr.ma_bd = mcp->mc_board_num; 3271738dd194Shyw rsaddr.mi_restartaddr.ma_bank = bank; 3272738dd194Shyw rsaddr.mi_restartaddr.ma_dimm_addr = dimm_addr; 3273738dd194Shyw rsaddr.mi_valid = 1; 3274738dd194Shyw rsaddr.mi_injectrestart = 1; 3275738dd194Shyw restart_patrol(mcp, bank, &rsaddr); 327625cf1a30Sjl } 327725cf1a30Sjl 327825cf1a30Sjl if (flags & MC_INJECT_FLAG_POLL) { 32790cc8ae86Sav int running; 328037afe445Shyw int ebank = (IS_MIRROR(mcp, bank)) ? MIRROR_IDX(bank) : bank; 328125cf1a30Sjl 328225cf1a30Sjl MC_LOG("Poll patrol error\n"); 328325cf1a30Sjl stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank)); 328425cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 32850cc8ae86Sav running = cntl & MAC_CNTL_PTRL_START; 328625cf1a30Sjl 3287cfb9e062Shyw if (!running && 3288cfb9e062Shyw (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS))) { 3289cfb9e062Shyw /* 3290cfb9e062Shyw * HW patrol stopped and we have errors to 3291cfb9e062Shyw * report. Do it. 3292cfb9e062Shyw */ 329337afe445Shyw mcp->mc_speedup_period[ebank] = 0; 3294738dd194Shyw rsaddr.mi_valid = 0; 3295738dd194Shyw rsaddr.mi_injectrestart = 0; 3296738dd194Shyw if (IS_MIRROR(mcp, bank)) { 3297738dd194Shyw mc_error_handler_mir(mcp, bank, &rsaddr); 3298738dd194Shyw } else { 3299738dd194Shyw mc_error_handler(mcp, bank, &rsaddr); 3300738dd194Shyw } 3301cfb9e062Shyw 3302738dd194Shyw restart_patrol(mcp, bank, &rsaddr); 3303cfb9e062Shyw } else { 3304cfb9e062Shyw /* 3305cfb9e062Shyw * We are expecting to report injected 3306cfb9e062Shyw * errors but the HW patrol is still running. 3307cfb9e062Shyw * Speed up the scanning 3308cfb9e062Shyw */ 330937afe445Shyw mcp->mc_speedup_period[ebank] = 2; 3310cfb9e062Shyw MAC_CMD(mcp, bank, 0); 331125cf1a30Sjl restart_patrol(mcp, bank, NULL); 3312cfb9e062Shyw } 331325cf1a30Sjl } 331425cf1a30Sjl 331525cf1a30Sjl mutex_exit(&mcp->mc_lock); 331625cf1a30Sjl return (0); 331725cf1a30Sjl } 3318cfb9e062Shyw 331925cf1a30Sjl void 332025cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data) 332125cf1a30Sjl { 332225cf1a30Sjl MC_LOG("0x%x -> pa(%lx)\n", data, pa); 332325cf1a30Sjl stphysio(pa, data); 33240cc8ae86Sav 33250cc8ae86Sav /* force the above write to be processed by mac patrol */ 3326cfb9e062Shyw data = ldphysio(pa); 3327cfb9e062Shyw MC_LOG("pa(%lx) = 0x%x\n", pa, data); 332825cf1a30Sjl } 332925cf1a30Sjl 333025cf1a30Sjl uint32_t 333125cf1a30Sjl mc_ldphysio(uint64_t pa) 333225cf1a30Sjl { 333325cf1a30Sjl uint32_t rv; 333425cf1a30Sjl 333525cf1a30Sjl rv = ldphysio(pa); 333625cf1a30Sjl MC_LOG("pa(%lx) = 0x%x\n", pa, rv); 333725cf1a30Sjl return (rv); 333825cf1a30Sjl } 33390cc8ae86Sav 33400cc8ae86Sav #define isdigit(ch) ((ch) >= '0' && (ch) <= '9') 33410cc8ae86Sav 33420cc8ae86Sav /* 33430cc8ae86Sav * parse_unum_memory -- extract the board number and the DIMM name from 33440cc8ae86Sav * the unum. 33450cc8ae86Sav * 33460cc8ae86Sav * Return 0 for success and non-zero for a failure. 33470cc8ae86Sav */ 33480cc8ae86Sav int 33490cc8ae86Sav parse_unum_memory(char *unum, int *board, char *dname) 33500cc8ae86Sav { 33510cc8ae86Sav char *c; 33520cc8ae86Sav char x, y, z; 33530cc8ae86Sav 33540cc8ae86Sav if ((c = strstr(unum, "CMU")) != NULL) { 33550cc8ae86Sav /* DC Model */ 33560cc8ae86Sav c += 3; 33570cc8ae86Sav *board = (uint8_t)stoi(&c); 33580cc8ae86Sav if ((c = strstr(c, "MEM")) == NULL) { 33590cc8ae86Sav return (1); 33600cc8ae86Sav } 33610cc8ae86Sav c += 3; 33620cc8ae86Sav if (strlen(c) < 3) { 33630cc8ae86Sav return (2); 33640cc8ae86Sav } 33650cc8ae86Sav if ((!isdigit(c[0])) || (!(isdigit(c[1]))) || 33660cc8ae86Sav ((c[2] != 'A') && (c[2] != 'B'))) { 33670cc8ae86Sav return (3); 33680cc8ae86Sav } 33690cc8ae86Sav x = c[0]; 33700cc8ae86Sav y = c[1]; 33710cc8ae86Sav z = c[2]; 33720cc8ae86Sav } else if ((c = strstr(unum, "MBU_")) != NULL) { 33730cc8ae86Sav /* FF1/FF2 Model */ 33740cc8ae86Sav c += 4; 33750cc8ae86Sav if ((c[0] != 'A') && (c[0] != 'B')) { 33760cc8ae86Sav return (4); 33770cc8ae86Sav } 33780cc8ae86Sav if ((c = strstr(c, "MEMB")) == NULL) { 33790cc8ae86Sav return (5); 33800cc8ae86Sav } 33810cc8ae86Sav c += 4; 33820cc8ae86Sav 33830cc8ae86Sav x = c[0]; 33840cc8ae86Sav *board = ((uint8_t)stoi(&c)) / 4; 33850cc8ae86Sav if ((c = strstr(c, "MEM")) == NULL) { 33860cc8ae86Sav return (6); 33870cc8ae86Sav } 33880cc8ae86Sav c += 3; 33890cc8ae86Sav if (strlen(c) < 2) { 33900cc8ae86Sav return (7); 33910cc8ae86Sav } 33920cc8ae86Sav if ((!isdigit(c[0])) || ((c[1] != 'A') && (c[1] != 'B'))) { 33930cc8ae86Sav return (8); 33940cc8ae86Sav } 33950cc8ae86Sav y = c[0]; 33960cc8ae86Sav z = c[1]; 33970cc8ae86Sav } else { 33980cc8ae86Sav return (9); 33990cc8ae86Sav } 34000cc8ae86Sav if (*board < 0) { 34010cc8ae86Sav return (10); 34020cc8ae86Sav } 34030cc8ae86Sav dname[0] = x; 34040cc8ae86Sav dname[1] = y; 34050cc8ae86Sav dname[2] = z; 34060cc8ae86Sav dname[3] = '\0'; 34070cc8ae86Sav return (0); 34080cc8ae86Sav } 34090cc8ae86Sav 34100cc8ae86Sav /* 34110cc8ae86Sav * mc_get_mem_sid_dimm -- Get the serial-ID for a given board and 34120cc8ae86Sav * the DIMM name. 34130cc8ae86Sav */ 34140cc8ae86Sav int 34150cc8ae86Sav mc_get_mem_sid_dimm(mc_opl_t *mcp, char *dname, char *buf, 34160cc8ae86Sav int buflen, int *lenp) 34170cc8ae86Sav { 34180cc8ae86Sav int ret = ENODEV; 34190cc8ae86Sav mc_dimm_info_t *d = NULL; 34200cc8ae86Sav 34210cc8ae86Sav if ((d = mcp->mc_dimm_list) == NULL) 34220cc8ae86Sav return (ENOTSUP); 34230cc8ae86Sav 34240cc8ae86Sav for (; d != NULL; d = d->md_next) { 34250cc8ae86Sav if (strcmp(d->md_dimmname, dname) == 0) { 34260cc8ae86Sav break; 34270cc8ae86Sav } 34280cc8ae86Sav } 34290cc8ae86Sav if (d != NULL) { 34300cc8ae86Sav *lenp = strlen(d->md_serial) + strlen(d->md_partnum); 34310cc8ae86Sav if (buflen <= *lenp) { 34320cc8ae86Sav cmn_err(CE_WARN, "mc_get_mem_sid_dimm: " 34330cc8ae86Sav "buflen is smaller than %d\n", *lenp); 34340cc8ae86Sav ret = ENOSPC; 34350cc8ae86Sav } else { 34360cc8ae86Sav snprintf(buf, buflen, "%s:%s", 34370cc8ae86Sav d->md_serial, d->md_partnum); 34380cc8ae86Sav ret = 0; 34390cc8ae86Sav } 34400cc8ae86Sav } 34410cc8ae86Sav MC_LOG("mc_get_mem_sid_dimm: Ret=%d Name=%s Serial-ID=%s\n", 34420cc8ae86Sav ret, dname, (ret == 0) ? buf : ""); 34430cc8ae86Sav return (ret); 34440cc8ae86Sav } 34450cc8ae86Sav 34460cc8ae86Sav int 3447aeb241b2Sav mc_set_mem_sid(mc_opl_t *mcp, char *buf, int buflen, int sb, 34480cc8ae86Sav int bank, uint32_t mf_type, uint32_t d_slot) 34490cc8ae86Sav { 34500cc8ae86Sav int lenp = buflen; 34510cc8ae86Sav int id; 34520cc8ae86Sav int ret; 34530cc8ae86Sav char *dimmnm; 34540cc8ae86Sav 3455056c948bStsien if (mf_type == FLT_TYPE_INTERMITTENT_CE || 3456056c948bStsien mf_type == FLT_TYPE_PERMANENT_CE) { 34570cc8ae86Sav if (plat_model == MODEL_DC) { 34580cc8ae86Sav id = BD_BK_SLOT_TO_INDEX(0, bank, d_slot); 3459aeb241b2Sav dimmnm = mc_dc_dimm_unum_table[id]; 34600cc8ae86Sav } else { 34610cc8ae86Sav id = BD_BK_SLOT_TO_INDEX(sb, bank, d_slot); 3462aeb241b2Sav dimmnm = mc_ff_dimm_unum_table[id]; 34630cc8ae86Sav } 34640cc8ae86Sav if ((ret = mc_get_mem_sid_dimm(mcp, dimmnm, buf, buflen, 34650cc8ae86Sav &lenp)) != 0) { 34660cc8ae86Sav return (ret); 34670cc8ae86Sav } 34680cc8ae86Sav } else { 34690cc8ae86Sav return (1); 34700cc8ae86Sav } 34710cc8ae86Sav 34720cc8ae86Sav return (0); 34730cc8ae86Sav } 34740cc8ae86Sav 34750cc8ae86Sav /* 34760cc8ae86Sav * mc_get_mem_sid -- get the DIMM serial-ID corresponding to the unum. 34770cc8ae86Sav */ 34780cc8ae86Sav int 34790cc8ae86Sav mc_get_mem_sid(char *unum, char *buf, int buflen, int *lenp) 34800cc8ae86Sav { 34810cc8ae86Sav int i; 34820cc8ae86Sav int ret = ENODEV; 34830cc8ae86Sav int board; 34840cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 34850cc8ae86Sav mc_opl_t *mcp; 34860cc8ae86Sav 34870cc8ae86Sav MC_LOG("mc_get_mem_sid: unum=%s buflen=%d\n", unum, buflen); 34880cc8ae86Sav if ((ret = parse_unum_memory(unum, &board, dname)) != 0) { 34890cc8ae86Sav MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 34900cc8ae86Sav unum, ret); 34910cc8ae86Sav return (EINVAL); 34920cc8ae86Sav } 34930cc8ae86Sav 34940cc8ae86Sav if (board < 0) { 34950cc8ae86Sav MC_LOG("mc_get_mem_sid: Invalid board=%d dimm=%s\n", 34960cc8ae86Sav board, dname); 34970cc8ae86Sav return (EINVAL); 34980cc8ae86Sav } 34990cc8ae86Sav 35000cc8ae86Sav mutex_enter(&mcmutex); 35011039f409Sav /* 35021039f409Sav * return ENOENT if we can not find the matching board. 35031039f409Sav */ 35041039f409Sav ret = ENOENT; 35050cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 35060cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 35070cc8ae86Sav continue; 35080cc8ae86Sav mutex_enter(&mcp->mc_lock); 3509aeb241b2Sav if (mcp->mc_phys_board_num != board) { 3510aeb241b2Sav mutex_exit(&mcp->mc_lock); 3511aeb241b2Sav continue; 3512aeb241b2Sav } 3513aeb241b2Sav ret = mc_get_mem_sid_dimm(mcp, dname, buf, buflen, lenp); 3514aeb241b2Sav if (ret == 0) { 35150cc8ae86Sav mutex_exit(&mcp->mc_lock); 35160cc8ae86Sav break; 35170cc8ae86Sav } 35180cc8ae86Sav mutex_exit(&mcp->mc_lock); 35190cc8ae86Sav } 35200cc8ae86Sav mutex_exit(&mcmutex); 35210cc8ae86Sav return (ret); 35220cc8ae86Sav } 35230cc8ae86Sav 35240cc8ae86Sav /* 35250cc8ae86Sav * mc_get_mem_offset -- get the offset in a DIMM for a given physical address. 35260cc8ae86Sav */ 35270cc8ae86Sav int 35280cc8ae86Sav mc_get_mem_offset(uint64_t paddr, uint64_t *offp) 35290cc8ae86Sav { 35300cc8ae86Sav int i; 35310cc8ae86Sav int ret = ENODEV; 35320cc8ae86Sav mc_addr_t maddr; 35330cc8ae86Sav mc_opl_t *mcp; 35340cc8ae86Sav 35350cc8ae86Sav mutex_enter(&mcmutex); 3536c964b0e6Sraghuram for (i = 0; ((i < OPL_MAX_BOARDS) && (ret != 0)); i++) { 35370cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 35380cc8ae86Sav continue; 35390cc8ae86Sav mutex_enter(&mcp->mc_lock); 35400cc8ae86Sav if (!pa_is_valid(mcp, paddr)) { 35410cc8ae86Sav mutex_exit(&mcp->mc_lock); 35420cc8ae86Sav continue; 35430cc8ae86Sav } 35440cc8ae86Sav if (pa_to_maddr(mcp, paddr, &maddr) == 0) { 35450cc8ae86Sav *offp = maddr.ma_dimm_addr; 35460cc8ae86Sav ret = 0; 35470cc8ae86Sav } 35480cc8ae86Sav mutex_exit(&mcp->mc_lock); 35490cc8ae86Sav } 35500cc8ae86Sav mutex_exit(&mcmutex); 35510cc8ae86Sav MC_LOG("mc_get_mem_offset: Ret=%d paddr=0x%lx offset=0x%lx\n", 35520cc8ae86Sav ret, paddr, *offp); 35530cc8ae86Sav return (ret); 35540cc8ae86Sav } 35550cc8ae86Sav 35560cc8ae86Sav /* 35570cc8ae86Sav * dname_to_bankslot - Get the bank and slot number from the DIMM name. 35580cc8ae86Sav */ 35590cc8ae86Sav int 35600cc8ae86Sav dname_to_bankslot(char *dname, int *bank, int *slot) 35610cc8ae86Sav { 35620cc8ae86Sav int i; 35630cc8ae86Sav int tsz; 35640cc8ae86Sav char **tbl; 35650cc8ae86Sav 35660cc8ae86Sav if (plat_model == MODEL_DC) { /* DC */ 35670cc8ae86Sav tbl = mc_dc_dimm_unum_table; 35680cc8ae86Sav tsz = OPL_MAX_DIMMS; 35690cc8ae86Sav } else { 35700cc8ae86Sav tbl = mc_ff_dimm_unum_table; 35710cc8ae86Sav tsz = 2 * OPL_MAX_DIMMS; 35720cc8ae86Sav } 35730cc8ae86Sav 35740cc8ae86Sav for (i = 0; i < tsz; i++) { 35750cc8ae86Sav if (strcmp(dname, tbl[i]) == 0) { 35760cc8ae86Sav break; 35770cc8ae86Sav } 35780cc8ae86Sav } 35790cc8ae86Sav if (i == tsz) { 35800cc8ae86Sav return (1); 35810cc8ae86Sav } 35820cc8ae86Sav *bank = INDEX_TO_BANK(i); 35830cc8ae86Sav *slot = INDEX_TO_SLOT(i); 35840cc8ae86Sav return (0); 35850cc8ae86Sav } 35860cc8ae86Sav 35870cc8ae86Sav /* 35880cc8ae86Sav * mc_get_mem_addr -- get the physical address of a DIMM corresponding 35890cc8ae86Sav * to the unum and sid. 35900cc8ae86Sav */ 35910cc8ae86Sav int 35920cc8ae86Sav mc_get_mem_addr(char *unum, char *sid, uint64_t offset, uint64_t *paddr) 35930cc8ae86Sav { 35940cc8ae86Sav int board; 35950cc8ae86Sav int bank; 35960cc8ae86Sav int slot; 35970cc8ae86Sav int i; 35980cc8ae86Sav int ret = ENODEV; 35990cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 36000cc8ae86Sav mc_addr_t maddr; 36010cc8ae86Sav mc_opl_t *mcp; 36020cc8ae86Sav 36030cc8ae86Sav MC_LOG("mc_get_mem_addr: unum=%s sid=%s offset=0x%lx\n", 36040cc8ae86Sav unum, sid, offset); 36050cc8ae86Sav if (parse_unum_memory(unum, &board, dname) != 0) { 36060cc8ae86Sav MC_LOG("mc_get_mem_sid: unum(%s) parsing failed ret=%d\n", 36070cc8ae86Sav unum, ret); 36080cc8ae86Sav return (EINVAL); 36090cc8ae86Sav } 36100cc8ae86Sav 36110cc8ae86Sav if (board < 0) { 36120cc8ae86Sav MC_LOG("mc_get_mem_addr: Invalid board=%d dimm=%s\n", 36130cc8ae86Sav board, dname); 36140cc8ae86Sav return (EINVAL); 36150cc8ae86Sav } 36160cc8ae86Sav 36170cc8ae86Sav mutex_enter(&mcmutex); 36180cc8ae86Sav for (i = 0; i < OPL_MAX_BOARDS; i++) { 36190cc8ae86Sav if ((mcp = mc_instances[i]) == NULL) 36200cc8ae86Sav continue; 36210cc8ae86Sav mutex_enter(&mcp->mc_lock); 3622aeb241b2Sav if (mcp->mc_phys_board_num != board) { 36230cc8ae86Sav mutex_exit(&mcp->mc_lock); 36240cc8ae86Sav continue; 36250cc8ae86Sav } 36260cc8ae86Sav 36270cc8ae86Sav ret = dname_to_bankslot(dname, &bank, &slot); 36280cc8ae86Sav MC_LOG("mc_get_mem_addr: bank=%d slot=%d\n", bank, slot); 36290cc8ae86Sav if (ret != 0) { 36300cc8ae86Sav MC_LOG("mc_get_mem_addr: dname_to_bankslot failed\n"); 36310cc8ae86Sav ret = ENODEV; 36320cc8ae86Sav } else { 3633aeb241b2Sav maddr.ma_bd = mcp->mc_board_num; 36340cc8ae86Sav maddr.ma_bank = bank; 36350cc8ae86Sav maddr.ma_dimm_addr = offset; 36360cc8ae86Sav ret = mcaddr_to_pa(mcp, &maddr, paddr); 36370cc8ae86Sav if (ret != 0) { 36380cc8ae86Sav MC_LOG("mc_get_mem_addr: " 36390cc8ae86Sav "mcaddr_to_pa failed\n"); 36400cc8ae86Sav ret = ENODEV; 36410cc8ae86Sav } 3642aeb241b2Sav mutex_exit(&mcp->mc_lock); 3643aeb241b2Sav break; 36440cc8ae86Sav } 36450cc8ae86Sav mutex_exit(&mcp->mc_lock); 36460cc8ae86Sav } 36470cc8ae86Sav mutex_exit(&mcmutex); 36480cc8ae86Sav MC_LOG("mc_get_mem_addr: Ret=%d, Paddr=0x%lx\n", ret, *paddr); 36490cc8ae86Sav return (ret); 36500cc8ae86Sav } 36510cc8ae86Sav 36520cc8ae86Sav static void 36530cc8ae86Sav mc_free_dimm_list(mc_dimm_info_t *d) 36540cc8ae86Sav { 36550cc8ae86Sav mc_dimm_info_t *next; 36560cc8ae86Sav 36570cc8ae86Sav while (d != NULL) { 36580cc8ae86Sav next = d->md_next; 36590cc8ae86Sav kmem_free(d, sizeof (mc_dimm_info_t)); 36600cc8ae86Sav d = next; 36610cc8ae86Sav } 36620cc8ae86Sav } 36630cc8ae86Sav 36640cc8ae86Sav /* 36650cc8ae86Sav * mc_get_dimm_list -- get the list of dimms with serial-id info 36660cc8ae86Sav * from the SP. 36670cc8ae86Sav */ 36680cc8ae86Sav mc_dimm_info_t * 36690cc8ae86Sav mc_get_dimm_list(mc_opl_t *mcp) 36700cc8ae86Sav { 36710cc8ae86Sav uint32_t bufsz; 36720cc8ae86Sav uint32_t maxbufsz; 36730cc8ae86Sav int ret; 36740cc8ae86Sav int sexp; 36750cc8ae86Sav board_dimm_info_t *bd_dimmp; 36760cc8ae86Sav mc_dimm_info_t *dimm_list = NULL; 36770cc8ae86Sav 36780cc8ae86Sav maxbufsz = bufsz = sizeof (board_dimm_info_t) + 36790cc8ae86Sav ((MCOPL_MAX_DIMMNAME + MCOPL_MAX_SERIAL + 36800cc8ae86Sav MCOPL_MAX_PARTNUM) * OPL_MAX_DIMMS); 36810cc8ae86Sav 36820cc8ae86Sav bd_dimmp = (board_dimm_info_t *)kmem_alloc(bufsz, KM_SLEEP); 36830cc8ae86Sav ret = scf_get_dimminfo(mcp->mc_board_num, (void *)bd_dimmp, &bufsz); 36840cc8ae86Sav 36850cc8ae86Sav MC_LOG("mc_get_dimm_list: scf_service_getinfo returned=%d\n", ret); 36860cc8ae86Sav if (ret == 0) { 36870cc8ae86Sav sexp = sizeof (board_dimm_info_t) + 36880cc8ae86Sav ((bd_dimmp->bd_dnamesz + bd_dimmp->bd_serialsz + 36890cc8ae86Sav bd_dimmp->bd_partnumsz) * bd_dimmp->bd_numdimms); 36900cc8ae86Sav 36910cc8ae86Sav if ((bd_dimmp->bd_version == OPL_DIMM_INFO_VERSION) && 36920cc8ae86Sav (bd_dimmp->bd_dnamesz <= MCOPL_MAX_DIMMNAME) && 36930cc8ae86Sav (bd_dimmp->bd_serialsz <= MCOPL_MAX_SERIAL) && 36940cc8ae86Sav (bd_dimmp->bd_partnumsz <= MCOPL_MAX_PARTNUM) && 36950cc8ae86Sav (sexp <= bufsz)) { 36960cc8ae86Sav 36970cc8ae86Sav #ifdef DEBUG 36980cc8ae86Sav if (oplmc_debug) 36990cc8ae86Sav mc_dump_dimm_info(bd_dimmp); 37000cc8ae86Sav #endif 37010cc8ae86Sav dimm_list = mc_prepare_dimmlist(bd_dimmp); 37020cc8ae86Sav 37030cc8ae86Sav } else { 37040cc8ae86Sav cmn_err(CE_WARN, "DIMM info version mismatch\n"); 37050cc8ae86Sav } 37060cc8ae86Sav } 37070cc8ae86Sav kmem_free(bd_dimmp, maxbufsz); 37080cc8ae86Sav MC_LOG("mc_get_dimm_list: dimmlist=0x%p\n", dimm_list); 37090cc8ae86Sav return (dimm_list); 37100cc8ae86Sav } 37110cc8ae86Sav 37120cc8ae86Sav /* 37131039f409Sav * mc_prepare_dimmlist - Prepare the dimm list from the information 37141039f409Sav * received from the SP. 37150cc8ae86Sav */ 37160cc8ae86Sav mc_dimm_info_t * 37170cc8ae86Sav mc_prepare_dimmlist(board_dimm_info_t *bd_dimmp) 37180cc8ae86Sav { 37190cc8ae86Sav char *dimm_name; 37200cc8ae86Sav char *serial; 37210cc8ae86Sav char *part; 37220cc8ae86Sav int dimm; 37230cc8ae86Sav int dnamesz = bd_dimmp->bd_dnamesz; 37240cc8ae86Sav int sersz = bd_dimmp->bd_serialsz; 37250cc8ae86Sav int partsz = bd_dimmp->bd_partnumsz; 37260cc8ae86Sav mc_dimm_info_t *dimm_list = NULL; 37270cc8ae86Sav mc_dimm_info_t *d; 37280cc8ae86Sav 37290cc8ae86Sav dimm_name = (char *)(bd_dimmp + 1); 37300cc8ae86Sav for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 37310cc8ae86Sav 37320cc8ae86Sav d = (mc_dimm_info_t *)kmem_alloc(sizeof (mc_dimm_info_t), 37330cc8ae86Sav KM_SLEEP); 3734ad59b69dSbm 3735ad59b69dSbm bcopy(dimm_name, d->md_dimmname, dnamesz); 3736ad59b69dSbm d->md_dimmname[dnamesz] = 0; 3737ad59b69dSbm 37380cc8ae86Sav serial = dimm_name + dnamesz; 3739ad59b69dSbm bcopy(serial, d->md_serial, sersz); 3740ad59b69dSbm d->md_serial[sersz] = 0; 3741ad59b69dSbm 37420cc8ae86Sav part = serial + sersz; 3743ad59b69dSbm bcopy(part, d->md_partnum, partsz); 3744ad59b69dSbm d->md_partnum[partsz] = 0; 37450cc8ae86Sav 37460cc8ae86Sav d->md_next = dimm_list; 37470cc8ae86Sav dimm_list = d; 37480cc8ae86Sav dimm_name = part + partsz; 37490cc8ae86Sav } 37500cc8ae86Sav return (dimm_list); 37510cc8ae86Sav } 37520cc8ae86Sav 37530cc8ae86Sav #ifdef DEBUG 37540cc8ae86Sav void 37550cc8ae86Sav mc_dump_dimm(char *buf, int dnamesz, int serialsz, int partnumsz) 37560cc8ae86Sav { 37570cc8ae86Sav char dname[MCOPL_MAX_DIMMNAME + 1]; 37580cc8ae86Sav char serial[MCOPL_MAX_SERIAL + 1]; 37590cc8ae86Sav char part[ MCOPL_MAX_PARTNUM + 1]; 37600cc8ae86Sav char *b; 37610cc8ae86Sav 37620cc8ae86Sav b = buf; 3763ad59b69dSbm bcopy(b, dname, dnamesz); 3764ad59b69dSbm dname[dnamesz] = 0; 3765ad59b69dSbm 37660cc8ae86Sav b += dnamesz; 3767ad59b69dSbm bcopy(b, serial, serialsz); 3768ad59b69dSbm serial[serialsz] = 0; 3769ad59b69dSbm 37700cc8ae86Sav b += serialsz; 3771ad59b69dSbm bcopy(b, part, partnumsz); 3772ad59b69dSbm part[partnumsz] = 0; 3773ad59b69dSbm 37740cc8ae86Sav printf("DIMM=%s Serial=%s PartNum=%s\n", dname, serial, part); 37750cc8ae86Sav } 37760cc8ae86Sav 37770cc8ae86Sav void 37780cc8ae86Sav mc_dump_dimm_info(board_dimm_info_t *bd_dimmp) 37790cc8ae86Sav { 37800cc8ae86Sav int dimm; 37810cc8ae86Sav int dnamesz = bd_dimmp->bd_dnamesz; 37820cc8ae86Sav int sersz = bd_dimmp->bd_serialsz; 37830cc8ae86Sav int partsz = bd_dimmp->bd_partnumsz; 37840cc8ae86Sav char *buf; 37850cc8ae86Sav 37860cc8ae86Sav printf("Version=%d Board=%02d DIMMs=%d NameSize=%d " 37870cc8ae86Sav "SerialSize=%d PartnumSize=%d\n", bd_dimmp->bd_version, 37880cc8ae86Sav bd_dimmp->bd_boardnum, bd_dimmp->bd_numdimms, bd_dimmp->bd_dnamesz, 37890cc8ae86Sav bd_dimmp->bd_serialsz, bd_dimmp->bd_partnumsz); 37900cc8ae86Sav printf("======================================================\n"); 37910cc8ae86Sav 37920cc8ae86Sav buf = (char *)(bd_dimmp + 1); 37930cc8ae86Sav for (dimm = 0; dimm < bd_dimmp->bd_numdimms; dimm++) { 37940cc8ae86Sav mc_dump_dimm(buf, dnamesz, sersz, partsz); 37950cc8ae86Sav buf += dnamesz + sersz + partsz; 37960cc8ae86Sav } 37970cc8ae86Sav printf("======================================================\n"); 37980cc8ae86Sav } 37990cc8ae86Sav 38000cc8ae86Sav 38010cc8ae86Sav /* ARGSUSED */ 38020cc8ae86Sav static int 38030cc8ae86Sav mc_ioctl_debug(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 38040cc8ae86Sav int *rvalp) 38050cc8ae86Sav { 38060cc8ae86Sav caddr_t buf; 38070cc8ae86Sav uint64_t pa; 38080cc8ae86Sav int rv = 0; 38090cc8ae86Sav int i; 38100cc8ae86Sav uint32_t flags; 38110cc8ae86Sav static uint32_t offset = 0; 38120cc8ae86Sav 38130cc8ae86Sav 38140cc8ae86Sav flags = (cmd >> 4) & 0xfffffff; 38150cc8ae86Sav 38160cc8ae86Sav cmd &= 0xf; 38170cc8ae86Sav 38180cc8ae86Sav MC_LOG("mc_ioctl(cmd = %x, flags = %x)\n", cmd, flags); 38190cc8ae86Sav 38200cc8ae86Sav if (arg != NULL) { 38210cc8ae86Sav if (ddi_copyin((const void *)arg, (void *)&pa, 3822d8a0cca9Swh sizeof (uint64_t), 0) < 0) { 38230cc8ae86Sav rv = EFAULT; 38240cc8ae86Sav return (rv); 38250cc8ae86Sav } 38260cc8ae86Sav buf = NULL; 38270cc8ae86Sav } else { 38280cc8ae86Sav buf = (caddr_t)kmem_alloc(PAGESIZE, KM_SLEEP); 38290cc8ae86Sav 38300cc8ae86Sav pa = va_to_pa(buf); 38310cc8ae86Sav pa += offset; 38320cc8ae86Sav 38330cc8ae86Sav offset += 64; 38340cc8ae86Sav if (offset >= PAGESIZE) 38350cc8ae86Sav offset = 0; 38360cc8ae86Sav } 38370cc8ae86Sav 38380cc8ae86Sav switch (cmd) { 38390cc8ae86Sav case MCI_CE: 3840d8a0cca9Swh mc_inject_error(MC_INJECT_INTERMITTENT_CE, pa, flags); 38410cc8ae86Sav break; 38420cc8ae86Sav case MCI_PERM_CE: 3843d8a0cca9Swh mc_inject_error(MC_INJECT_PERMANENT_CE, pa, flags); 38440cc8ae86Sav break; 38450cc8ae86Sav case MCI_UE: 3846d8a0cca9Swh mc_inject_error(MC_INJECT_UE, pa, flags); 38470cc8ae86Sav break; 38480cc8ae86Sav case MCI_M_CE: 3849d8a0cca9Swh mc_inject_error(MC_INJECT_INTERMITTENT_MCE, pa, flags); 38500cc8ae86Sav break; 38510cc8ae86Sav case MCI_M_PCE: 3852d8a0cca9Swh mc_inject_error(MC_INJECT_PERMANENT_MCE, pa, flags); 38530cc8ae86Sav break; 38540cc8ae86Sav case MCI_M_UE: 3855d8a0cca9Swh mc_inject_error(MC_INJECT_MUE, pa, flags); 38560cc8ae86Sav break; 38570cc8ae86Sav case MCI_CMP: 3858d8a0cca9Swh mc_inject_error(MC_INJECT_CMPE, pa, flags); 38590cc8ae86Sav break; 38600cc8ae86Sav case MCI_NOP: 3861d8a0cca9Swh mc_inject_error(MC_INJECT_NOP, pa, flags); break; 38620cc8ae86Sav case MCI_SHOW_ALL: 38630cc8ae86Sav mc_debug_show_all = 1; 38640cc8ae86Sav break; 38650cc8ae86Sav case MCI_SHOW_NONE: 38660cc8ae86Sav mc_debug_show_all = 0; 38670cc8ae86Sav break; 38680cc8ae86Sav case MCI_ALLOC: 38690cc8ae86Sav /* 38700cc8ae86Sav * just allocate some kernel memory and never free it 38710cc8ae86Sav * 512 MB seems to be the maximum size supported. 38720cc8ae86Sav */ 38730cc8ae86Sav cmn_err(CE_NOTE, "Allocating kmem %d MB\n", flags * 512); 38740cc8ae86Sav for (i = 0; i < flags; i++) { 38750cc8ae86Sav buf = kmem_alloc(512 * 1024 * 1024, KM_SLEEP); 38760cc8ae86Sav cmn_err(CE_NOTE, "kmem buf %llx PA %llx\n", 3877d8a0cca9Swh (u_longlong_t)buf, (u_longlong_t)va_to_pa(buf)); 38780cc8ae86Sav } 38790cc8ae86Sav break; 38800cc8ae86Sav case MCI_SUSPEND: 38810cc8ae86Sav (void) opl_mc_suspend(); 38820cc8ae86Sav break; 38830cc8ae86Sav case MCI_RESUME: 38840cc8ae86Sav (void) opl_mc_resume(); 38850cc8ae86Sav break; 38860cc8ae86Sav default: 38870cc8ae86Sav rv = ENXIO; 38880cc8ae86Sav } 38890cc8ae86Sav return (rv); 38900cc8ae86Sav } 38910cc8ae86Sav 38920cc8ae86Sav #endif /* DEBUG */ 3893