125cf1a30Sjl /* 225cf1a30Sjl * CDDL HEADER START 325cf1a30Sjl * 425cf1a30Sjl * The contents of this file are subject to the terms of the 525cf1a30Sjl * Common Development and Distribution License (the "License"). 625cf1a30Sjl * You may not use this file except in compliance with the License. 725cf1a30Sjl * 825cf1a30Sjl * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 925cf1a30Sjl * or http://www.opensolaris.org/os/licensing. 1025cf1a30Sjl * See the License for the specific language governing permissions 1125cf1a30Sjl * and limitations under the License. 1225cf1a30Sjl * 1325cf1a30Sjl * When distributing Covered Code, include this CDDL HEADER in each 1425cf1a30Sjl * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1525cf1a30Sjl * If applicable, add the following below this CDDL HEADER, with the 1625cf1a30Sjl * fields enclosed by brackets "[]" replaced with your own identifying 1725cf1a30Sjl * information: Portions Copyright [yyyy] [name of copyright owner] 1825cf1a30Sjl * 1925cf1a30Sjl * CDDL HEADER END 2025cf1a30Sjl */ 2125cf1a30Sjl /* 2225cf1a30Sjl * All Rights Reserved, Copyright (c) FUJITSU LIMITED 2006 2325cf1a30Sjl */ 2425cf1a30Sjl 2525cf1a30Sjl #pragma ident "%Z%%M% %I% %E% SMI" 2625cf1a30Sjl 2725cf1a30Sjl #include <sys/types.h> 2825cf1a30Sjl #include <sys/sysmacros.h> 2925cf1a30Sjl #include <sys/conf.h> 3025cf1a30Sjl #include <sys/modctl.h> 3125cf1a30Sjl #include <sys/stat.h> 3225cf1a30Sjl #include <sys/async.h> 3325cf1a30Sjl #include <sys/machsystm.h> 3425cf1a30Sjl #include <sys/ksynch.h> 3525cf1a30Sjl #include <sys/ddi.h> 3625cf1a30Sjl #include <sys/sunddi.h> 3725cf1a30Sjl #include <sys/ddifm.h> 3825cf1a30Sjl #include <sys/fm/protocol.h> 3925cf1a30Sjl #include <sys/fm/util.h> 4025cf1a30Sjl #include <sys/kmem.h> 4125cf1a30Sjl #include <sys/fm/io/opl_mc_fm.h> 4225cf1a30Sjl #include <sys/memlist.h> 4325cf1a30Sjl #include <sys/param.h> 4425cf1a30Sjl #include <sys/ontrap.h> 4525cf1a30Sjl #include <vm/page.h> 4625cf1a30Sjl #include <sys/mc-opl.h> 4725cf1a30Sjl 4825cf1a30Sjl /* 4925cf1a30Sjl * Function prototypes 5025cf1a30Sjl */ 5125cf1a30Sjl static int mc_open(dev_t *, int, int, cred_t *); 5225cf1a30Sjl static int mc_close(dev_t, int, int, cred_t *); 5325cf1a30Sjl static int mc_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 5425cf1a30Sjl static int mc_attach(dev_info_t *, ddi_attach_cmd_t); 5525cf1a30Sjl static int mc_detach(dev_info_t *, ddi_detach_cmd_t); 5625cf1a30Sjl 5725cf1a30Sjl static int mc_board_add(mc_opl_t *mcp); 5825cf1a30Sjl static int mc_board_del(mc_opl_t *mcp); 5925cf1a30Sjl static int mc_suspend(mc_opl_t *mcp, uint32_t flag); 6025cf1a30Sjl static int mc_resume(mc_opl_t *mcp, uint32_t flag); 6125cf1a30Sjl 6225cf1a30Sjl static void insert_mcp(mc_opl_t *mcp); 6325cf1a30Sjl static void delete_mcp(mc_opl_t *mcp); 6425cf1a30Sjl 6525cf1a30Sjl static int pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr); 6625cf1a30Sjl 6725cf1a30Sjl static int mc_valid_pa(mc_opl_t *mcp, uint64_t pa); 6825cf1a30Sjl 6925cf1a30Sjl int mc_get_mem_unum(int, uint64_t, char *, int, int *); 7025cf1a30Sjl extern int plat_max_boards(void); 7125cf1a30Sjl 7225cf1a30Sjl static void mc_get_mlist(mc_opl_t *); 7325cf1a30Sjl 7425cf1a30Sjl #pragma weak opl_get_physical_board 7525cf1a30Sjl extern int opl_get_physical_board(int); 7625cf1a30Sjl static int mc_opl_get_physical_board(int); 7725cf1a30Sjl 7825cf1a30Sjl /* 7925cf1a30Sjl * Configuration data structures 8025cf1a30Sjl */ 8125cf1a30Sjl static struct cb_ops mc_cb_ops = { 8225cf1a30Sjl mc_open, /* open */ 8325cf1a30Sjl mc_close, /* close */ 8425cf1a30Sjl nulldev, /* strategy */ 8525cf1a30Sjl nulldev, /* print */ 8625cf1a30Sjl nodev, /* dump */ 8725cf1a30Sjl nulldev, /* read */ 8825cf1a30Sjl nulldev, /* write */ 8925cf1a30Sjl mc_ioctl, /* ioctl */ 9025cf1a30Sjl nodev, /* devmap */ 9125cf1a30Sjl nodev, /* mmap */ 9225cf1a30Sjl nodev, /* segmap */ 9325cf1a30Sjl nochpoll, /* poll */ 9425cf1a30Sjl ddi_prop_op, /* cb_prop_op */ 9525cf1a30Sjl 0, /* streamtab */ 9625cf1a30Sjl D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 9725cf1a30Sjl CB_REV, /* rev */ 9825cf1a30Sjl nodev, /* cb_aread */ 9925cf1a30Sjl nodev /* cb_awrite */ 10025cf1a30Sjl }; 10125cf1a30Sjl 10225cf1a30Sjl static struct dev_ops mc_ops = { 10325cf1a30Sjl DEVO_REV, /* rev */ 10425cf1a30Sjl 0, /* refcnt */ 10525cf1a30Sjl ddi_getinfo_1to1, /* getinfo */ 10625cf1a30Sjl nulldev, /* identify */ 10725cf1a30Sjl nulldev, /* probe */ 10825cf1a30Sjl mc_attach, /* attach */ 10925cf1a30Sjl mc_detach, /* detach */ 11025cf1a30Sjl nulldev, /* reset */ 11125cf1a30Sjl &mc_cb_ops, /* cb_ops */ 11225cf1a30Sjl (struct bus_ops *)0, /* bus_ops */ 11325cf1a30Sjl nulldev /* power */ 11425cf1a30Sjl }; 11525cf1a30Sjl 11625cf1a30Sjl /* 11725cf1a30Sjl * Driver globals 11825cf1a30Sjl */ 11925cf1a30Sjl int mc_patrol_interval_sec = 10; 12025cf1a30Sjl 12125cf1a30Sjl int inject_op_delay = 5; 12225cf1a30Sjl 12325cf1a30Sjl mc_inst_list_t *mc_instances; 12425cf1a30Sjl static kmutex_t mcmutex; 12525cf1a30Sjl 12625cf1a30Sjl void *mc_statep; 12725cf1a30Sjl 12825cf1a30Sjl #ifdef DEBUG 129*2742aa22Shyw int oplmc_debug = 0; 13025cf1a30Sjl #endif 13125cf1a30Sjl 13225cf1a30Sjl static int mc_debug_show_all; 13325cf1a30Sjl 13425cf1a30Sjl extern struct mod_ops mod_driverops; 13525cf1a30Sjl 13625cf1a30Sjl static struct modldrv modldrv = { 13725cf1a30Sjl &mod_driverops, /* module type, this one is a driver */ 13825cf1a30Sjl "OPL Memory-controller 1.1", /* module name */ 13925cf1a30Sjl &mc_ops, /* driver ops */ 14025cf1a30Sjl }; 14125cf1a30Sjl 14225cf1a30Sjl static struct modlinkage modlinkage = { 14325cf1a30Sjl MODREV_1, /* rev */ 14425cf1a30Sjl (void *)&modldrv, 14525cf1a30Sjl NULL 14625cf1a30Sjl }; 14725cf1a30Sjl 14825cf1a30Sjl #pragma weak opl_get_mem_unum 14925cf1a30Sjl extern int (*opl_get_mem_unum)(int, uint64_t, char *, int, int *); 15025cf1a30Sjl 15125cf1a30Sjl /* 15225cf1a30Sjl * pseudo-mc node portid format 15325cf1a30Sjl * 15425cf1a30Sjl * [10] = 0 15525cf1a30Sjl * [9] = 1 15625cf1a30Sjl * [8] = LSB_ID[4] = 0 15725cf1a30Sjl * [7:4] = LSB_ID[3:0] 15825cf1a30Sjl * [3:0] = 0 15925cf1a30Sjl * 16025cf1a30Sjl */ 16125cf1a30Sjl 16225cf1a30Sjl /* 16325cf1a30Sjl * These are the module initialization routines. 16425cf1a30Sjl */ 16525cf1a30Sjl int 16625cf1a30Sjl _init(void) 16725cf1a30Sjl { 16825cf1a30Sjl int error; 16925cf1a30Sjl 17025cf1a30Sjl 17125cf1a30Sjl if ((error = ddi_soft_state_init(&mc_statep, 17225cf1a30Sjl sizeof (mc_opl_t), 1)) != 0) 17325cf1a30Sjl return (error); 17425cf1a30Sjl 17525cf1a30Sjl mutex_init(&mcmutex, NULL, MUTEX_DRIVER, NULL); 17625cf1a30Sjl if (&opl_get_mem_unum) 17725cf1a30Sjl opl_get_mem_unum = mc_get_mem_unum; 17825cf1a30Sjl 17925cf1a30Sjl error = mod_install(&modlinkage); 18025cf1a30Sjl if (error != 0) { 18125cf1a30Sjl if (&opl_get_mem_unum) 18225cf1a30Sjl opl_get_mem_unum = NULL; 18325cf1a30Sjl mutex_destroy(&mcmutex); 18425cf1a30Sjl ddi_soft_state_fini(&mc_statep); 18525cf1a30Sjl } 18625cf1a30Sjl 18725cf1a30Sjl return (error); 18825cf1a30Sjl } 18925cf1a30Sjl 19025cf1a30Sjl int 19125cf1a30Sjl _fini(void) 19225cf1a30Sjl { 19325cf1a30Sjl int error; 19425cf1a30Sjl 19525cf1a30Sjl if ((error = mod_remove(&modlinkage)) != 0) 19625cf1a30Sjl return (error); 19725cf1a30Sjl 19825cf1a30Sjl mutex_destroy(&mcmutex); 19925cf1a30Sjl 20025cf1a30Sjl if (&opl_get_mem_unum) 20125cf1a30Sjl opl_get_mem_unum = NULL; 20225cf1a30Sjl 20325cf1a30Sjl ddi_soft_state_fini(&mc_statep); 20425cf1a30Sjl 20525cf1a30Sjl return (0); 20625cf1a30Sjl } 20725cf1a30Sjl 20825cf1a30Sjl int 20925cf1a30Sjl _info(struct modinfo *modinfop) 21025cf1a30Sjl { 21125cf1a30Sjl return (mod_info(&modlinkage, modinfop)); 21225cf1a30Sjl } 21325cf1a30Sjl 21425cf1a30Sjl static int 21525cf1a30Sjl mc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 21625cf1a30Sjl { 21725cf1a30Sjl mc_opl_t *mcp; 21825cf1a30Sjl int instance; 21925cf1a30Sjl 22025cf1a30Sjl /* get the instance of this devi */ 22125cf1a30Sjl instance = ddi_get_instance(devi); 22225cf1a30Sjl 22325cf1a30Sjl switch (cmd) { 22425cf1a30Sjl case DDI_ATTACH: 22525cf1a30Sjl break; 22625cf1a30Sjl case DDI_RESUME: 22725cf1a30Sjl mcp = ddi_get_soft_state(mc_statep, instance); 22825cf1a30Sjl return (mc_resume(mcp, MC_DRIVER_SUSPENDED)); 22925cf1a30Sjl default: 23025cf1a30Sjl return (DDI_FAILURE); 23125cf1a30Sjl } 23225cf1a30Sjl 23325cf1a30Sjl 23425cf1a30Sjl if (ddi_soft_state_zalloc(mc_statep, instance) != DDI_SUCCESS) 23525cf1a30Sjl return (DDI_FAILURE); 23625cf1a30Sjl 23725cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 23825cf1a30Sjl goto bad; 23925cf1a30Sjl } 24025cf1a30Sjl 24125cf1a30Sjl /* set informations in mc state */ 24225cf1a30Sjl mcp->mc_dip = devi; 24325cf1a30Sjl 24425cf1a30Sjl if (mc_board_add(mcp)) 24525cf1a30Sjl goto bad; 24625cf1a30Sjl 24725cf1a30Sjl insert_mcp(mcp); 24825cf1a30Sjl ddi_report_dev(devi); 24925cf1a30Sjl 25025cf1a30Sjl return (DDI_SUCCESS); 25125cf1a30Sjl 25225cf1a30Sjl bad: 25325cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 25425cf1a30Sjl return (DDI_FAILURE); 25525cf1a30Sjl } 25625cf1a30Sjl 25725cf1a30Sjl /* ARGSUSED */ 25825cf1a30Sjl static int 25925cf1a30Sjl mc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 26025cf1a30Sjl { 26125cf1a30Sjl int instance; 26225cf1a30Sjl mc_opl_t *mcp; 26325cf1a30Sjl 26425cf1a30Sjl /* get the instance of this devi */ 26525cf1a30Sjl instance = ddi_get_instance(devi); 26625cf1a30Sjl if ((mcp = ddi_get_soft_state(mc_statep, instance)) == NULL) { 26725cf1a30Sjl return (DDI_FAILURE); 26825cf1a30Sjl } 26925cf1a30Sjl 27025cf1a30Sjl switch (cmd) { 27125cf1a30Sjl case DDI_SUSPEND: 27225cf1a30Sjl return (mc_suspend(mcp, MC_DRIVER_SUSPENDED)); 27325cf1a30Sjl case DDI_DETACH: 27425cf1a30Sjl break; 27525cf1a30Sjl default: 27625cf1a30Sjl return (DDI_FAILURE); 27725cf1a30Sjl } 27825cf1a30Sjl 27925cf1a30Sjl mutex_enter(&mcmutex); 28025cf1a30Sjl if (mc_board_del(mcp) != DDI_SUCCESS) { 28125cf1a30Sjl mutex_exit(&mcmutex); 28225cf1a30Sjl return (DDI_FAILURE); 28325cf1a30Sjl } 28425cf1a30Sjl 28525cf1a30Sjl delete_mcp(mcp); 28625cf1a30Sjl mutex_exit(&mcmutex); 28725cf1a30Sjl 28825cf1a30Sjl /* free up the soft state */ 28925cf1a30Sjl ddi_soft_state_free(mc_statep, instance); 29025cf1a30Sjl 29125cf1a30Sjl return (DDI_SUCCESS); 29225cf1a30Sjl } 29325cf1a30Sjl 29425cf1a30Sjl /* ARGSUSED */ 29525cf1a30Sjl static int 29625cf1a30Sjl mc_open(dev_t *devp, int flag, int otyp, cred_t *credp) 29725cf1a30Sjl { 29825cf1a30Sjl return (0); 29925cf1a30Sjl } 30025cf1a30Sjl 30125cf1a30Sjl /* ARGSUSED */ 30225cf1a30Sjl static int 30325cf1a30Sjl mc_close(dev_t devp, int flag, int otyp, cred_t *credp) 30425cf1a30Sjl { 30525cf1a30Sjl return (0); 30625cf1a30Sjl } 30725cf1a30Sjl 30825cf1a30Sjl /* ARGSUSED */ 30925cf1a30Sjl static int 31025cf1a30Sjl mc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 31125cf1a30Sjl int *rvalp) 31225cf1a30Sjl { 31325cf1a30Sjl return (ENXIO); 31425cf1a30Sjl } 31525cf1a30Sjl 31625cf1a30Sjl /* 31725cf1a30Sjl * PA validity check: 31825cf1a30Sjl * This function return 1 if the PA is valid, otherwise 31925cf1a30Sjl * return 0. 32025cf1a30Sjl */ 32125cf1a30Sjl 32225cf1a30Sjl /* ARGSUSED */ 32325cf1a30Sjl static int 32425cf1a30Sjl pa_is_valid(mc_opl_t *mcp, uint64_t addr) 32525cf1a30Sjl { 32625cf1a30Sjl /* 32725cf1a30Sjl * Check if the addr is on the board. 32825cf1a30Sjl */ 32925cf1a30Sjl if ((addr < mcp->mc_start_address) || 33025cf1a30Sjl (mcp->mc_start_address + mcp->mc_size <= addr)) 33125cf1a30Sjl return (0); 33225cf1a30Sjl 33325cf1a30Sjl if (mcp->mlist == NULL) 33425cf1a30Sjl mc_get_mlist(mcp); 33525cf1a30Sjl 33625cf1a30Sjl if (mcp->mlist && address_in_memlist(mcp->mlist, addr, 0)) { 33725cf1a30Sjl return (1); 33825cf1a30Sjl } 33925cf1a30Sjl return (0); 34025cf1a30Sjl } 34125cf1a30Sjl 34225cf1a30Sjl /* 34325cf1a30Sjl * mac-pa translation routines. 34425cf1a30Sjl * 34525cf1a30Sjl * Input: mc driver state, (LSB#, Bank#, DIMM address) 34625cf1a30Sjl * Output: physical address 34725cf1a30Sjl * 34825cf1a30Sjl * Valid - return value: 0 34925cf1a30Sjl * Invalid - return value: -1 35025cf1a30Sjl */ 35125cf1a30Sjl static int 35225cf1a30Sjl mcaddr_to_pa(mc_opl_t *mcp, mc_addr_t *maddr, uint64_t *pa) 35325cf1a30Sjl { 35425cf1a30Sjl int i; 35525cf1a30Sjl uint64_t pa_offset = 0; 35625cf1a30Sjl int cs = (maddr->ma_dimm_addr >> CS_SHIFT) & 1; 35725cf1a30Sjl int bank = maddr->ma_bank; 35825cf1a30Sjl mc_addr_t maddr1; 35925cf1a30Sjl int bank0, bank1; 36025cf1a30Sjl 36125cf1a30Sjl MC_LOG("mcaddr /LSB%d/B%d/%x\n", maddr->ma_bd, bank, 36225cf1a30Sjl maddr->ma_dimm_addr); 36325cf1a30Sjl 36425cf1a30Sjl /* loc validity check */ 36525cf1a30Sjl ASSERT(maddr->ma_bd >= 0 && OPL_BOARD_MAX > maddr->ma_bd); 36625cf1a30Sjl ASSERT(bank >= 0 && OPL_BANK_MAX > bank); 36725cf1a30Sjl 36825cf1a30Sjl /* Do translation */ 36925cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 37025cf1a30Sjl int pa_bit = 0; 37125cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 37225cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 37325cf1a30Sjl pa_bit = (maddr->ma_dimm_addr >> mc_bit) & 1; 37425cf1a30Sjl } else if (mc_bit == MP_NONE) { 37525cf1a30Sjl pa_bit = 0; 37625cf1a30Sjl } else if (mc_bit == MP_BANK_0) { 37725cf1a30Sjl pa_bit = bank & 1; 37825cf1a30Sjl } else if (mc_bit == MP_BANK_1) { 37925cf1a30Sjl pa_bit = (bank >> 1) & 1; 38025cf1a30Sjl } else if (mc_bit == MP_BANK_2) { 38125cf1a30Sjl pa_bit = (bank >> 2) & 1; 38225cf1a30Sjl } 38325cf1a30Sjl pa_offset |= ((uint64_t)pa_bit) << i; 38425cf1a30Sjl } 38525cf1a30Sjl *pa = mcp->mc_start_address + pa_offset; 38625cf1a30Sjl MC_LOG("pa = %lx\n", *pa); 38725cf1a30Sjl 38825cf1a30Sjl if (pa_to_maddr(mcp, *pa, &maddr1) == -1) { 38925cf1a30Sjl return (-1); 39025cf1a30Sjl } 39125cf1a30Sjl 39225cf1a30Sjl 39325cf1a30Sjl if (IS_MIRROR(mcp, maddr->ma_bank)) { 39425cf1a30Sjl bank0 = maddr->ma_bank & ~(1); 39525cf1a30Sjl bank1 = maddr1.ma_bank & ~(1); 39625cf1a30Sjl } else { 39725cf1a30Sjl bank0 = maddr->ma_bank; 39825cf1a30Sjl bank1 = maddr1.ma_bank; 39925cf1a30Sjl } 40025cf1a30Sjl /* 40125cf1a30Sjl * there is no need to check ma_bd because it is generated from 40225cf1a30Sjl * mcp. They are the same. 40325cf1a30Sjl */ 40425cf1a30Sjl if ((bank0 == bank1) && 40525cf1a30Sjl (maddr->ma_dimm_addr == maddr1.ma_dimm_addr)) { 40625cf1a30Sjl return (0); 40725cf1a30Sjl } else { 40825cf1a30Sjl cmn_err(CE_WARN, "Translation error source /LSB%d/B%d/%x, " 40925cf1a30Sjl "PA %lx, target /LSB%d/B%d/%x\n", 41025cf1a30Sjl maddr->ma_bd, bank, maddr->ma_dimm_addr, 41125cf1a30Sjl *pa, maddr1.ma_bd, maddr1.ma_bank, 41225cf1a30Sjl maddr1.ma_dimm_addr); 41325cf1a30Sjl return (-1); 41425cf1a30Sjl } 41525cf1a30Sjl } 41625cf1a30Sjl 41725cf1a30Sjl /* 41825cf1a30Sjl * PA to CS (used by pa_to_maddr). 41925cf1a30Sjl */ 42025cf1a30Sjl static int 42125cf1a30Sjl pa_to_cs(mc_opl_t *mcp, uint64_t pa_offset) 42225cf1a30Sjl { 42325cf1a30Sjl int i; 42425cf1a30Sjl int cs = 0; 42525cf1a30Sjl 42625cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 42725cf1a30Sjl /* MAC address bit<29> is arranged on the same PA bit */ 42825cf1a30Sjl /* on both table. So we may use any table. */ 42925cf1a30Sjl if (mcp->mc_trans_table[0][i] == CS_SHIFT) { 43025cf1a30Sjl cs = (pa_offset >> i) & 1; 43125cf1a30Sjl break; 43225cf1a30Sjl } 43325cf1a30Sjl } 43425cf1a30Sjl return (cs); 43525cf1a30Sjl } 43625cf1a30Sjl 43725cf1a30Sjl /* 43825cf1a30Sjl * PA to DIMM (used by pa_to_maddr). 43925cf1a30Sjl */ 44025cf1a30Sjl /* ARGSUSED */ 44125cf1a30Sjl static uint32_t 44225cf1a30Sjl pa_to_dimm(mc_opl_t *mcp, uint64_t pa_offset) 44325cf1a30Sjl { 44425cf1a30Sjl int i; 44525cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 44625cf1a30Sjl uint32_t dimm_addr = 0; 44725cf1a30Sjl 44825cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 44925cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 45025cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 45125cf1a30Sjl if (mc_bit < MC_ADDRESS_BITS) { 45225cf1a30Sjl dimm_addr |= pa_bit_value << mc_bit; 45325cf1a30Sjl } 45425cf1a30Sjl } 45525cf1a30Sjl return (dimm_addr); 45625cf1a30Sjl } 45725cf1a30Sjl 45825cf1a30Sjl /* 45925cf1a30Sjl * PA to Bank (used by pa_to_maddr). 46025cf1a30Sjl */ 46125cf1a30Sjl static int 46225cf1a30Sjl pa_to_bank(mc_opl_t *mcp, uint64_t pa_offset) 46325cf1a30Sjl { 46425cf1a30Sjl int i; 46525cf1a30Sjl int cs = pa_to_cs(mcp, pa_offset); 46625cf1a30Sjl int bankno = mcp->mc_trans_table[cs][INDEX_OF_BANK_SUPPLEMENT_BIT]; 46725cf1a30Sjl 46825cf1a30Sjl 46925cf1a30Sjl for (i = 0; i < PA_BITS_FOR_MAC; i++) { 47025cf1a30Sjl int pa_bit_value = (pa_offset >> i) & 1; 47125cf1a30Sjl int mc_bit = mcp->mc_trans_table[cs][i]; 47225cf1a30Sjl switch (mc_bit) { 47325cf1a30Sjl case MP_BANK_0: 47425cf1a30Sjl bankno |= pa_bit_value; 47525cf1a30Sjl break; 47625cf1a30Sjl case MP_BANK_1: 47725cf1a30Sjl bankno |= pa_bit_value << 1; 47825cf1a30Sjl break; 47925cf1a30Sjl case MP_BANK_2: 48025cf1a30Sjl bankno |= pa_bit_value << 2; 48125cf1a30Sjl break; 48225cf1a30Sjl } 48325cf1a30Sjl } 48425cf1a30Sjl 48525cf1a30Sjl return (bankno); 48625cf1a30Sjl } 48725cf1a30Sjl 48825cf1a30Sjl /* 48925cf1a30Sjl * PA to MAC address translation 49025cf1a30Sjl * 49125cf1a30Sjl * Input: MAC driver state, physicall adress 49225cf1a30Sjl * Output: LSB#, Bank id, mac address 49325cf1a30Sjl * 49425cf1a30Sjl * Valid - return value: 0 49525cf1a30Sjl * Invalid - return value: -1 49625cf1a30Sjl */ 49725cf1a30Sjl 49825cf1a30Sjl int 49925cf1a30Sjl pa_to_maddr(mc_opl_t *mcp, uint64_t pa, mc_addr_t *maddr) 50025cf1a30Sjl { 50125cf1a30Sjl uint64_t pa_offset; 50225cf1a30Sjl 50325cf1a30Sjl /* PA validity check */ 50425cf1a30Sjl if (!pa_is_valid(mcp, pa)) 50525cf1a30Sjl return (-1); 50625cf1a30Sjl 50725cf1a30Sjl 50825cf1a30Sjl /* Do translation */ 50925cf1a30Sjl pa_offset = pa - mcp->mc_start_address; 51025cf1a30Sjl 51125cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 51225cf1a30Sjl maddr->ma_bank = pa_to_bank(mcp, pa_offset); 51325cf1a30Sjl maddr->ma_dimm_addr = pa_to_dimm(mcp, pa_offset); 51425cf1a30Sjl MC_LOG("pa %lx -> mcaddr /LSB%d/B%d/%x\n", 51525cf1a30Sjl pa_offset, maddr->ma_bd, maddr->ma_bank, maddr->ma_dimm_addr); 51625cf1a30Sjl return (0); 51725cf1a30Sjl } 51825cf1a30Sjl 51925cf1a30Sjl static void 52025cf1a30Sjl mc_ereport_post(mc_aflt_t *mc_aflt) 52125cf1a30Sjl { 52225cf1a30Sjl char buf[FM_MAX_CLASS]; 52325cf1a30Sjl char device_path[MAXPATHLEN]; 52425cf1a30Sjl nv_alloc_t *nva = NULL; 52525cf1a30Sjl nvlist_t *ereport, *detector, *resource; 52625cf1a30Sjl errorq_elem_t *eqep; 52725cf1a30Sjl int nflts; 52825cf1a30Sjl mc_flt_stat_t *flt_stat; 52925cf1a30Sjl int i, n, blen; 53025cf1a30Sjl char *p; 53125cf1a30Sjl uint32_t values[2], synd[2], dslot[2]; 53225cf1a30Sjl 53325cf1a30Sjl if (panicstr) { 53425cf1a30Sjl eqep = errorq_reserve(ereport_errorq); 53525cf1a30Sjl if (eqep == NULL) 53625cf1a30Sjl return; 53725cf1a30Sjl ereport = errorq_elem_nvl(ereport_errorq, eqep); 53825cf1a30Sjl nva = errorq_elem_nva(ereport_errorq, eqep); 53925cf1a30Sjl } else { 54025cf1a30Sjl ereport = fm_nvlist_create(nva); 54125cf1a30Sjl } 54225cf1a30Sjl 54325cf1a30Sjl /* 54425cf1a30Sjl * Create the scheme "dev" FMRI. 54525cf1a30Sjl */ 54625cf1a30Sjl detector = fm_nvlist_create(nva); 54725cf1a30Sjl resource = fm_nvlist_create(nva); 54825cf1a30Sjl 54925cf1a30Sjl nflts = mc_aflt->mflt_nflts; 55025cf1a30Sjl 55125cf1a30Sjl ASSERT(nflts >= 1 && nflts <= 2); 55225cf1a30Sjl 55325cf1a30Sjl flt_stat = mc_aflt->mflt_stat[0]; 55425cf1a30Sjl (void) ddi_pathname(mc_aflt->mflt_mcp->mc_dip, device_path); 55525cf1a30Sjl (void) fm_fmri_dev_set(detector, FM_DEV_SCHEME_VERSION, NULL, 55625cf1a30Sjl device_path, NULL); 55725cf1a30Sjl 55825cf1a30Sjl /* 55925cf1a30Sjl * Encode all the common data into the ereport. 56025cf1a30Sjl */ 56125cf1a30Sjl (void) snprintf(buf, FM_MAX_CLASS, "%s.%s-%s", 56225cf1a30Sjl MC_OPL_ERROR_CLASS, 56325cf1a30Sjl mc_aflt->mflt_is_ptrl ? MC_OPL_PTRL_SUBCLASS : 56425cf1a30Sjl MC_OPL_MI_SUBCLASS, 56525cf1a30Sjl mc_aflt->mflt_erpt_class); 56625cf1a30Sjl 56725cf1a30Sjl MC_LOG("mc_ereport_post: ereport %s\n", buf); 56825cf1a30Sjl 56925cf1a30Sjl 57025cf1a30Sjl fm_ereport_set(ereport, FM_EREPORT_VERSION, buf, 57125cf1a30Sjl fm_ena_generate(mc_aflt->mflt_id, FM_ENA_FMT1), 57225cf1a30Sjl detector, NULL); 57325cf1a30Sjl 57425cf1a30Sjl /* 57525cf1a30Sjl * Set payload. 57625cf1a30Sjl */ 57725cf1a30Sjl fm_payload_set(ereport, MC_OPL_BOARD, DATA_TYPE_UINT32, 57825cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd, NULL); 57925cf1a30Sjl 58025cf1a30Sjl fm_payload_set(ereport, MC_OPL_PA, DATA_TYPE_UINT64, 58125cf1a30Sjl flt_stat->mf_flt_paddr, NULL); 58225cf1a30Sjl 58325cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 58425cf1a30Sjl fm_payload_set(ereport, MC_OPL_FLT_TYPE, 58525cf1a30Sjl DATA_TYPE_UINT8, ECC_STICKY, NULL); 58625cf1a30Sjl } 58725cf1a30Sjl 58825cf1a30Sjl for (i = 0; i < nflts; i++) 58925cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_flt_maddr.ma_bank; 59025cf1a30Sjl 59125cf1a30Sjl fm_payload_set(ereport, MC_OPL_BANK, DATA_TYPE_UINT32_ARRAY, 59225cf1a30Sjl nflts, values, NULL); 59325cf1a30Sjl 59425cf1a30Sjl for (i = 0; i < nflts; i++) 59525cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_cntl; 59625cf1a30Sjl 59725cf1a30Sjl fm_payload_set(ereport, MC_OPL_STATUS, DATA_TYPE_UINT32_ARRAY, 59825cf1a30Sjl nflts, values, NULL); 59925cf1a30Sjl 60025cf1a30Sjl for (i = 0; i < nflts; i++) 60125cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_add; 60225cf1a30Sjl 60325cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_ADD, DATA_TYPE_UINT32_ARRAY, 60425cf1a30Sjl nflts, values, NULL); 60525cf1a30Sjl 60625cf1a30Sjl for (i = 0; i < nflts; i++) 60725cf1a30Sjl values[i] = mc_aflt->mflt_stat[i]->mf_err_log; 60825cf1a30Sjl 60925cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_LOG, DATA_TYPE_UINT32_ARRAY, 61025cf1a30Sjl nflts, values, NULL); 61125cf1a30Sjl 61225cf1a30Sjl for (i = 0; i < nflts; i++) { 61325cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 61425cf1a30Sjl if (flt_stat->mf_errlog_valid) { 61525cf1a30Sjl synd[i] = flt_stat->mf_synd; 61625cf1a30Sjl dslot[i] = flt_stat->mf_dimm_slot; 61725cf1a30Sjl values[i] = flt_stat->mf_dram_place; 61825cf1a30Sjl } else { 61925cf1a30Sjl synd[i] = 0; 62025cf1a30Sjl dslot[i] = 0; 62125cf1a30Sjl values[i] = 0; 62225cf1a30Sjl } 62325cf1a30Sjl } 62425cf1a30Sjl 62525cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_SYND, 62625cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, synd, NULL); 62725cf1a30Sjl 62825cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_DIMMSLOT, 62925cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, dslot, NULL); 63025cf1a30Sjl 63125cf1a30Sjl fm_payload_set(ereport, MC_OPL_ERR_DRAM, 63225cf1a30Sjl DATA_TYPE_UINT32_ARRAY, nflts, values, NULL); 63325cf1a30Sjl 63425cf1a30Sjl blen = MAXPATHLEN; 63525cf1a30Sjl device_path[0] = 0; 63625cf1a30Sjl p = &device_path[0]; 63725cf1a30Sjl 63825cf1a30Sjl for (i = 0; i < nflts; i++) { 63925cf1a30Sjl int bank = flt_stat->mf_flt_maddr.ma_bank; 64025cf1a30Sjl int psb = -1; 64125cf1a30Sjl 64225cf1a30Sjl flt_stat = mc_aflt->mflt_stat[i]; 64325cf1a30Sjl psb = mc_opl_get_physical_board( 64425cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd); 64525cf1a30Sjl 64625cf1a30Sjl if (psb != -1) { 64725cf1a30Sjl snprintf(p, blen, "/CMU%d/B%d", psb, bank); 64825cf1a30Sjl } else { 64925cf1a30Sjl snprintf(p, blen, "/CMU/B%d", bank); 65025cf1a30Sjl } 65125cf1a30Sjl 65225cf1a30Sjl if (flt_stat->mf_errlog_valid) { 65325cf1a30Sjl snprintf(p + strlen(p), blen, "/MEM%d%d%c", 65425cf1a30Sjl bank/2, (bank & 0x1) * 2 + dslot[i] & 1, 65525cf1a30Sjl (dslot[i] & 0x2) ? 'B' : 'A'); 65625cf1a30Sjl } 65725cf1a30Sjl 65825cf1a30Sjl n = strlen(&device_path[0]); 65925cf1a30Sjl blen = MAXPATHLEN - n; 66025cf1a30Sjl p = &device_path[n]; 66125cf1a30Sjl if (i < (nflts - 1)) { 66225cf1a30Sjl snprintf(p, blen, " "); 66325cf1a30Sjl n += 1; blen -= 1; p += 1; 66425cf1a30Sjl } 66525cf1a30Sjl } 66625cf1a30Sjl 66725cf1a30Sjl /* 66825cf1a30Sjl * UNUM format /LSB#/B#/MEMxyZ 66925cf1a30Sjl * where x is the MAC# = Bank#/2 67025cf1a30Sjl * y is slot info = (Bank# & 0x1)*2 + {0, 1} 0 for DIMM-L, 1 for DIMM-H 67125cf1a30Sjl * DIMM-L is 0 in bit 13, DIMM-H is 1 in bit 13. 67225cf1a30Sjl * Z is A(CS0) or B(CS1) given by bit 14 67325cf1a30Sjl */ 67425cf1a30Sjl (void) fm_fmri_mem_set(resource, FM_MEM_SCHEME_VERSION, 67525cf1a30Sjl NULL, device_path, NULL, 0); 67625cf1a30Sjl 67725cf1a30Sjl fm_payload_set(ereport, MC_OPL_RESOURCE, DATA_TYPE_NVLIST, 67825cf1a30Sjl resource, NULL); 67925cf1a30Sjl 68025cf1a30Sjl if (panicstr) { 68125cf1a30Sjl errorq_commit(ereport_errorq, eqep, ERRORQ_SYNC); 68225cf1a30Sjl } else { 68325cf1a30Sjl (void) fm_ereport_post(ereport, EVCH_TRYHARD); 68425cf1a30Sjl fm_nvlist_destroy(ereport, FM_NVA_FREE); 68525cf1a30Sjl fm_nvlist_destroy(detector, FM_NVA_FREE); 68625cf1a30Sjl fm_nvlist_destroy(resource, FM_NVA_FREE); 68725cf1a30Sjl } 68825cf1a30Sjl } 68925cf1a30Sjl 69025cf1a30Sjl static void 69125cf1a30Sjl mc_err_drain(mc_aflt_t *mc_aflt) 69225cf1a30Sjl { 69325cf1a30Sjl int rv; 69425cf1a30Sjl page_t *pp; 69525cf1a30Sjl uint64_t errors; 69625cf1a30Sjl uint64_t pa = (uint64_t)(-1); 69725cf1a30Sjl 69825cf1a30Sjl MC_LOG("mc_err_drain: %s\n", 69925cf1a30Sjl mc_aflt->mflt_erpt_class); 70025cf1a30Sjl /* 70125cf1a30Sjl * we come here only when we have: 70225cf1a30Sjl * In mirror mode: CMPE, MUE, SUE 70325cf1a30Sjl * In normal mode: UE, Permanent CE 70425cf1a30Sjl */ 70525cf1a30Sjl rv = mcaddr_to_pa(mc_aflt->mflt_mcp, 70625cf1a30Sjl &(mc_aflt->mflt_stat[0]->mf_flt_maddr), &pa); 70725cf1a30Sjl if (rv == 0) 70825cf1a30Sjl mc_aflt->mflt_stat[0]->mf_flt_paddr = pa; 70925cf1a30Sjl else 71025cf1a30Sjl mc_aflt->mflt_stat[0]->mf_flt_paddr = (uint64_t)-1; 71125cf1a30Sjl if (rv == 0) { 71225cf1a30Sjl MC_LOG("mc_err_drain:pa = %lx\n", pa); 71325cf1a30Sjl pp = page_numtopp_nolock(pa >> PAGESHIFT); 71425cf1a30Sjl 71525cf1a30Sjl if (pp) { 71625cf1a30Sjl /* 71725cf1a30Sjl * Don't keep retiring and make ereports 71825cf1a30Sjl * on bad pages in PTRL case 71925cf1a30Sjl */ 72025cf1a30Sjl MC_LOG("mc_err_drain:pp = %p\n", pp); 72125cf1a30Sjl if (mc_aflt->mflt_is_ptrl) { 72225cf1a30Sjl errors = 0; 72325cf1a30Sjl if (page_retire_check(pa, &errors) == 0) { 72425cf1a30Sjl MC_LOG("Page retired\n"); 72525cf1a30Sjl return; 72625cf1a30Sjl } 72725cf1a30Sjl if (errors & mc_aflt->mflt_pr) { 72825cf1a30Sjl MC_LOG("errors %lx, mflt_pr %x\n", 72925cf1a30Sjl errors, mc_aflt->mflt_pr); 73025cf1a30Sjl return; 73125cf1a30Sjl } 73225cf1a30Sjl } 73325cf1a30Sjl MC_LOG("offline page %p error %x\n", pp, 73425cf1a30Sjl mc_aflt->mflt_pr); 73525cf1a30Sjl (void) page_retire(pa, mc_aflt->mflt_pr); 73625cf1a30Sjl } 73725cf1a30Sjl } 73825cf1a30Sjl mc_ereport_post(mc_aflt); 73925cf1a30Sjl } 74025cf1a30Sjl 74125cf1a30Sjl #define DIMM_SIZE 0x80000000 74225cf1a30Sjl 74325cf1a30Sjl #define INC_DIMM_ADDR(p, n) \ 74425cf1a30Sjl (p)->ma_dimm_addr += n; \ 74525cf1a30Sjl (p)->ma_dimm_addr &= (DIMM_SIZE - 1) 74625cf1a30Sjl 74725cf1a30Sjl /* 74825cf1a30Sjl * The restart address is actually defined in unit of PA[37:6] 74925cf1a30Sjl * the mac patrol will convert that to dimm offset. If the 75025cf1a30Sjl * address is not in the bank, it will continue to search for 75125cf1a30Sjl * the next PA that is within the bank. 75225cf1a30Sjl * 75325cf1a30Sjl * Also the mac patrol scans the dimms based on PA, not 75425cf1a30Sjl * dimm offset. 75525cf1a30Sjl */ 75625cf1a30Sjl 75725cf1a30Sjl static int 75825cf1a30Sjl restart_patrol(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr_info) 75925cf1a30Sjl { 76025cf1a30Sjl page_t *pp; 76125cf1a30Sjl uint32_t reg; 76225cf1a30Sjl uint64_t pa; 76325cf1a30Sjl int rv; 76425cf1a30Sjl int loop_count = 0; 76525cf1a30Sjl 76625cf1a30Sjl reg = ldphysio(MAC_PTRL_CNTL(mcp, bank)); 76725cf1a30Sjl 76825cf1a30Sjl /* already running, so we just return */ 76925cf1a30Sjl if (reg & MAC_CNTL_PTRL_START) 77025cf1a30Sjl return (0); 77125cf1a30Sjl 77225cf1a30Sjl if (maddr_info == NULL || (maddr_info->mi_valid == 0)) { 77325cf1a30Sjl MAC_PTRL_START(mcp, bank); 77425cf1a30Sjl return (0); 77525cf1a30Sjl } 77625cf1a30Sjl 77725cf1a30Sjl 77825cf1a30Sjl rv = mcaddr_to_pa(mcp, &maddr_info->mi_maddr, &pa); 77925cf1a30Sjl if (rv != 0) { 78025cf1a30Sjl MC_LOG("cannot convert mcaddr to pa. use auto restart\n"); 78125cf1a30Sjl MAC_PTRL_START(mcp, bank); 78225cf1a30Sjl return (0); 78325cf1a30Sjl } 78425cf1a30Sjl 78525cf1a30Sjl /* 78625cf1a30Sjl * pa is the last address scanned by the mac patrol 78725cf1a30Sjl * we calculate the next restart address as follows: 78825cf1a30Sjl * first we always advance it by 64 byte. Then begin the loop. 78925cf1a30Sjl * loop { 79025cf1a30Sjl * if it is not in phys_install, we advance to next 64 MB boundary 79125cf1a30Sjl * if it is not backed by a page structure, done 79225cf1a30Sjl * if the page is bad, advance to the next page boundary. 79325cf1a30Sjl * else done 79425cf1a30Sjl * if the new address exceeds the board, wrap around. 79525cf1a30Sjl * } <stop if we come back to the same page> 79625cf1a30Sjl */ 79725cf1a30Sjl 79825cf1a30Sjl if (pa < mcp->mc_start_address || pa >= (mcp->mc_start_address 79925cf1a30Sjl + mcp->mc_size)) { 80025cf1a30Sjl /* pa is not on this board, just retry */ 80125cf1a30Sjl cmn_err(CE_WARN, "restart_patrol: invalid address %lx " 80225cf1a30Sjl "on board %d\n", pa, mcp->mc_board_num); 80325cf1a30Sjl MAC_PTRL_START(mcp, bank); 80425cf1a30Sjl return (0); 80525cf1a30Sjl } 80625cf1a30Sjl 80725cf1a30Sjl MC_LOG("restart_patrol: pa = %lx\n", pa); 80825cf1a30Sjl if (maddr_info->mi_advance) { 80925cf1a30Sjl uint64_t new_pa; 81025cf1a30Sjl 81125cf1a30Sjl if (IS_MIRROR(mcp, bank)) 81225cf1a30Sjl new_pa = pa + 64 * 2; 81325cf1a30Sjl else 81425cf1a30Sjl new_pa = pa + 64; 81525cf1a30Sjl 81625cf1a30Sjl if (!mc_valid_pa(mcp, new_pa)) { 81725cf1a30Sjl /* Isolation unit size is 64 MB */ 81825cf1a30Sjl #define MC_ISOLATION_BSIZE (64 * 1024 * 1024) 81925cf1a30Sjl MC_LOG("Invalid PA\n"); 82025cf1a30Sjl pa = roundup(new_pa + 1, MC_ISOLATION_BSIZE); 82125cf1a30Sjl } else { 82225cf1a30Sjl pp = page_numtopp_nolock(new_pa >> PAGESHIFT); 82325cf1a30Sjl if (pp != NULL) { 82425cf1a30Sjl uint64_t errors = 0; 82525cf1a30Sjl if (page_retire_check(new_pa, &errors) && 82625cf1a30Sjl (errors == 0)) { 82725cf1a30Sjl MC_LOG("Page has no error\n"); 82825cf1a30Sjl MAC_PTRL_START(mcp, bank); 82925cf1a30Sjl return (0); 83025cf1a30Sjl } 83125cf1a30Sjl /* 83225cf1a30Sjl * skip bad pages 83325cf1a30Sjl * and let the following loop to take care 83425cf1a30Sjl */ 83525cf1a30Sjl pa = roundup(new_pa + 1, PAGESIZE); 83625cf1a30Sjl MC_LOG("Skipping bad page to %lx\n", pa); 83725cf1a30Sjl } else { 83825cf1a30Sjl MC_LOG("Page has no page structure\n"); 83925cf1a30Sjl MAC_PTRL_START(mcp, bank); 84025cf1a30Sjl return (0); 84125cf1a30Sjl } 84225cf1a30Sjl } 84325cf1a30Sjl } 84425cf1a30Sjl 84525cf1a30Sjl /* 84625cf1a30Sjl * if we wrap around twice, we just give up and let 84725cf1a30Sjl * mac patrol decide. 84825cf1a30Sjl */ 84925cf1a30Sjl MC_LOG("pa is now %lx\n", pa); 85025cf1a30Sjl while (loop_count <= 1) { 85125cf1a30Sjl if (!mc_valid_pa(mcp, pa)) { 85225cf1a30Sjl MC_LOG("pa is not valid. round up to 64 MB\n"); 85325cf1a30Sjl pa = roundup(pa + 1, 64 * 1024 * 1024); 85425cf1a30Sjl } else { 85525cf1a30Sjl pp = page_numtopp_nolock(pa >> PAGESHIFT); 85625cf1a30Sjl if (pp != NULL) { 85725cf1a30Sjl uint64_t errors = 0; 85825cf1a30Sjl if (page_retire_check(pa, &errors) && 85925cf1a30Sjl (errors == 0)) { 86025cf1a30Sjl MC_LOG("Page has no error\n"); 86125cf1a30Sjl break; 86225cf1a30Sjl } 86325cf1a30Sjl /* skip bad pages */ 86425cf1a30Sjl pa = roundup(pa + 1, PAGESIZE); 86525cf1a30Sjl MC_LOG("Skipping bad page to %lx\n", pa); 86625cf1a30Sjl } else { 86725cf1a30Sjl MC_LOG("Page has no page structure\n"); 86825cf1a30Sjl break; 86925cf1a30Sjl } 87025cf1a30Sjl } 87125cf1a30Sjl if (pa >= (mcp->mc_start_address + mcp->mc_size)) { 87225cf1a30Sjl MC_LOG("Wrap around\n"); 87325cf1a30Sjl pa = mcp->mc_start_address; 87425cf1a30Sjl loop_count++; 87525cf1a30Sjl } 87625cf1a30Sjl } 87725cf1a30Sjl 87825cf1a30Sjl /* retstart MAC patrol: PA[37:6] */ 87925cf1a30Sjl MC_LOG("restart at pa = %lx\n", pa); 88025cf1a30Sjl ST_MAC_REG(MAC_RESTART_ADD(mcp, bank), MAC_RESTART_PA(pa)); 88125cf1a30Sjl MAC_PTRL_START_ADD(mcp, bank); 88225cf1a30Sjl 88325cf1a30Sjl return (0); 88425cf1a30Sjl } 88525cf1a30Sjl 88625cf1a30Sjl /* 88725cf1a30Sjl * Rewriting is used for two purposes. 88825cf1a30Sjl * - to correct the error in memory. 88925cf1a30Sjl * - to determine whether the error is permanent or intermittent. 89025cf1a30Sjl * It's done by writing the address in MAC_BANKm_REWRITE_ADD 89125cf1a30Sjl * and issuing REW_REQ command in MAC_BANKm_PTRL_CNRL. After that, 89225cf1a30Sjl * REW_END (and REW_CE/REW_UE if some error detected) is set when 89325cf1a30Sjl * rewrite operation is done. See 4.7.3 and 4.7.11 in Columbus2 PRM. 89425cf1a30Sjl * 89525cf1a30Sjl * Note that rewrite operation doesn't change RAW_UE to Marked UE. 89625cf1a30Sjl * Therefore, we use it only CE case. 89725cf1a30Sjl */ 89825cf1a30Sjl static uint32_t 89925cf1a30Sjl do_rewrite(mc_opl_t *mcp, int bank, uint32_t dimm_addr) 90025cf1a30Sjl { 90125cf1a30Sjl uint32_t cntl; 90225cf1a30Sjl int count = 0; 90325cf1a30Sjl 90425cf1a30Sjl /* first wait to make sure PTRL_STATUS is 0 */ 90525cf1a30Sjl while (count++ < MAX_MC_LOOP_COUNT) { 90625cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 90725cf1a30Sjl if (!(cntl & MAC_CNTL_PTRL_STATUS)) 90825cf1a30Sjl break; 90925cf1a30Sjl delay(drv_usectohz(10 * 1000)); /* 10 m.s. */ 91025cf1a30Sjl } 91125cf1a30Sjl if (count >= MAX_MC_LOOP_COUNT) 91225cf1a30Sjl goto bad; 91325cf1a30Sjl 91425cf1a30Sjl count = 0; 91525cf1a30Sjl 91625cf1a30Sjl ST_MAC_REG(MAC_REWRITE_ADD(mcp, bank), dimm_addr); 91725cf1a30Sjl MAC_REW_REQ(mcp, bank); 91825cf1a30Sjl 91925cf1a30Sjl do { 92025cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 92125cf1a30Sjl if (count++ >= MAX_MC_LOOP_COUNT) { 92225cf1a30Sjl goto bad; 92325cf1a30Sjl } else 92425cf1a30Sjl delay(drv_usectohz(10 * 1000)); /* 10 m.s. */ 92525cf1a30Sjl /* 92625cf1a30Sjl * If there are other MEMORY or PCI activities, this 92725cf1a30Sjl * will be BUSY, else it should be set immediately 92825cf1a30Sjl */ 92925cf1a30Sjl } while (!(cntl & MAC_CNTL_REW_END)); 93025cf1a30Sjl 93125cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 93225cf1a30Sjl return (cntl); 93325cf1a30Sjl bad: 93425cf1a30Sjl /* This is bad. Just reset the circuit */ 93525cf1a30Sjl cmn_err(CE_WARN, "mc-opl rewrite timeout on /LSB%d/B%d\n", 93625cf1a30Sjl mcp->mc_board_num, bank); 93725cf1a30Sjl cntl = MAC_CNTL_REW_END; 93825cf1a30Sjl MAC_CMD(mcp, bank, MAC_CNTL_PTRL_RESET); 93925cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, MAC_CNTL_REW_ERRS); 94025cf1a30Sjl return (cntl); 94125cf1a30Sjl } 94225cf1a30Sjl 94325cf1a30Sjl void 94425cf1a30Sjl mc_process_scf_log(mc_opl_t *mcp) 94525cf1a30Sjl { 94625cf1a30Sjl int count = 0; 94725cf1a30Sjl scf_log_t *p; 94825cf1a30Sjl int bank; 94925cf1a30Sjl 95025cf1a30Sjl while ((p = mcp->mc_scf_log) != NULL) { 95125cf1a30Sjl bank = p->sl_bank; 95225cf1a30Sjl while ((LD_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank)) 95325cf1a30Sjl & MAC_STATIC_ERR_VLD)) { 95425cf1a30Sjl if (count++ >= (MAX_MC_LOOP_COUNT)) { 95525cf1a30Sjl break; 95625cf1a30Sjl } 95725cf1a30Sjl delay(drv_usectohz(10 * 1000)); /* 10 m.s. */ 95825cf1a30Sjl } 95925cf1a30Sjl 96025cf1a30Sjl if (count < MAX_MC_LOOP_COUNT) { 96125cf1a30Sjl ST_MAC_REG(MAC_STATIC_ERR_LOG(mcp, p->sl_bank), 96225cf1a30Sjl p->sl_err_log); 96325cf1a30Sjl 96425cf1a30Sjl ST_MAC_REG(MAC_STATIC_ERR_ADD(mcp, p->sl_bank), 96525cf1a30Sjl p->sl_err_add|MAC_STATIC_ERR_VLD); 96625cf1a30Sjl mcp->mc_scf_retry[bank] = 0; 96725cf1a30Sjl } else { 96825cf1a30Sjl /* if we try too many times, just drop the req */ 96925cf1a30Sjl if (mcp->mc_scf_retry[bank]++ <= MAX_SCF_RETRY) { 97025cf1a30Sjl return; 97125cf1a30Sjl } else { 97225cf1a30Sjl cmn_err(CE_WARN, "SCF is not responding. " 97325cf1a30Sjl "Dropping the SCF LOG\n"); 97425cf1a30Sjl } 97525cf1a30Sjl } 97625cf1a30Sjl mcp->mc_scf_log = p->sl_next; 97725cf1a30Sjl mcp->mc_scf_total--; 97825cf1a30Sjl ASSERT(mcp->mc_scf_total >= 0); 97925cf1a30Sjl kmem_free(p, sizeof (scf_log_t)); 98025cf1a30Sjl } 98125cf1a30Sjl } 98225cf1a30Sjl 98325cf1a30Sjl void 98425cf1a30Sjl mc_queue_scf_log(mc_opl_t *mcp, mc_flt_stat_t *flt_stat, int bank) 98525cf1a30Sjl { 98625cf1a30Sjl scf_log_t *p; 98725cf1a30Sjl 98825cf1a30Sjl if (mcp->mc_scf_total >= MAX_SCF_LOGS) { 98925cf1a30Sjl cmn_err(CE_WARN, 99025cf1a30Sjl "Max# SCF logs excceded on /LSB%d/B%d\n", 99125cf1a30Sjl mcp->mc_board_num, bank); 99225cf1a30Sjl return; 99325cf1a30Sjl } 99425cf1a30Sjl p = kmem_zalloc(sizeof (scf_log_t), KM_SLEEP); 99525cf1a30Sjl p->sl_next = 0; 99625cf1a30Sjl p->sl_err_add = flt_stat->mf_err_add; 99725cf1a30Sjl p->sl_err_log = flt_stat->mf_err_log; 99825cf1a30Sjl p->sl_bank = bank; 99925cf1a30Sjl 100025cf1a30Sjl if (mcp->mc_scf_log == NULL) { 100125cf1a30Sjl /* 100225cf1a30Sjl * we rely on mc_scf_log to detect NULL queue. 100325cf1a30Sjl * mc_scf_log_tail is irrelevant is such case. 100425cf1a30Sjl */ 100525cf1a30Sjl mcp->mc_scf_log_tail = mcp->mc_scf_log = p; 100625cf1a30Sjl } else { 100725cf1a30Sjl mcp->mc_scf_log_tail->sl_next = p; 100825cf1a30Sjl mcp->mc_scf_log_tail = p; 100925cf1a30Sjl } 101025cf1a30Sjl mcp->mc_scf_total++; 101125cf1a30Sjl } 101225cf1a30Sjl 101325cf1a30Sjl /* 101425cf1a30Sjl * This routine determines what kind of CE happens, intermittent 101525cf1a30Sjl * or permanent as follows. (See 4.7.3 in Columbus2 PRM.) 101625cf1a30Sjl * - Do rewrite by issuing REW_REQ command to MAC_PTRL_CNTL register. 101725cf1a30Sjl * - If CE is still detected on the same address even after doing 101825cf1a30Sjl * rewrite operation twice, it is determined as permanent error. 101925cf1a30Sjl * - If error is not detected anymore, it is determined as intermittent 102025cf1a30Sjl * error. 102125cf1a30Sjl * - If UE is detected due to rewrite operation, it should be treated 102225cf1a30Sjl * as UE. 102325cf1a30Sjl */ 102425cf1a30Sjl 102525cf1a30Sjl /* ARGSUSED */ 102625cf1a30Sjl static void 102725cf1a30Sjl mc_scrub_ce(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat, int ptrl_error) 102825cf1a30Sjl { 102925cf1a30Sjl uint32_t cntl; 103025cf1a30Sjl int i; 103125cf1a30Sjl 103225cf1a30Sjl flt_stat->mf_type = FLT_TYPE_PERMANENT_CE; 103325cf1a30Sjl /* 103425cf1a30Sjl * rewrite request 1st time reads and correct error data 103525cf1a30Sjl * and write to DIMM. 2nd rewrite request must be issued 103625cf1a30Sjl * after REW_CE/UE/END is 0. When the 2nd request is completed, 103725cf1a30Sjl * if REW_CE = 1, then it is permanent CE. 103825cf1a30Sjl */ 103925cf1a30Sjl for (i = 0; i < 2; i++) { 104025cf1a30Sjl cntl = do_rewrite(mcp, bank, flt_stat->mf_err_add); 104125cf1a30Sjl /* 104225cf1a30Sjl * If the error becomes UE or CMPE 104325cf1a30Sjl * we return to the caller immediately. 104425cf1a30Sjl */ 104525cf1a30Sjl if (cntl & MAC_CNTL_REW_UE) { 104625cf1a30Sjl if (ptrl_error) 104725cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_UE; 104825cf1a30Sjl else 104925cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_UE; 105025cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 105125cf1a30Sjl return; 105225cf1a30Sjl } 105325cf1a30Sjl if (cntl & MAC_CNTL_REW_CMPE) { 105425cf1a30Sjl if (ptrl_error) 105525cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_PTRL_CMPE; 105625cf1a30Sjl else 105725cf1a30Sjl flt_stat->mf_cntl |= MAC_CNTL_MI_CMPE; 105825cf1a30Sjl flt_stat->mf_type = FLT_TYPE_CMPE; 105925cf1a30Sjl return; 106025cf1a30Sjl } 106125cf1a30Sjl } 106225cf1a30Sjl if (!(cntl & MAC_CNTL_REW_CE)) { 106325cf1a30Sjl flt_stat->mf_type = FLT_TYPE_INTERMITTENT_CE; 106425cf1a30Sjl } 106525cf1a30Sjl 106625cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 106725cf1a30Sjl /* report PERMANENT_CE to SP via SCF */ 106825cf1a30Sjl if (!(flt_stat->mf_err_log & MAC_ERR_LOG_INVALID)) { 106925cf1a30Sjl mc_queue_scf_log(mcp, flt_stat, bank); 107025cf1a30Sjl } 107125cf1a30Sjl } 107225cf1a30Sjl } 107325cf1a30Sjl 107425cf1a30Sjl #define IS_CMPE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CMPE :\ 107525cf1a30Sjl MAC_CNTL_MI_CMPE)) 107625cf1a30Sjl #define IS_UE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_UE : MAC_CNTL_MI_UE)) 107725cf1a30Sjl #define IS_CE(cntl, f) ((cntl) & ((f) ? MAC_CNTL_PTRL_CE : MAC_CNTL_MI_CE)) 107825cf1a30Sjl #define IS_OK(cntl, f) (!((cntl) & ((f) ? MAC_CNTL_PTRL_ERRS : \ 107925cf1a30Sjl MAC_CNTL_MI_ERRS))) 108025cf1a30Sjl 108125cf1a30Sjl 108225cf1a30Sjl static int 108325cf1a30Sjl IS_CE_ONLY(uint32_t cntl, int ptrl_error) 108425cf1a30Sjl { 108525cf1a30Sjl if (ptrl_error) { 108625cf1a30Sjl return ((cntl & MAC_CNTL_PTRL_ERRS) == MAC_CNTL_PTRL_CE); 108725cf1a30Sjl } else { 108825cf1a30Sjl return ((cntl & MAC_CNTL_MI_ERRS) == MAC_CNTL_MI_CE); 108925cf1a30Sjl } 109025cf1a30Sjl } 109125cf1a30Sjl 109225cf1a30Sjl void 109325cf1a30Sjl mc_write_cntl(mc_opl_t *mcp, int bank, uint32_t value) 109425cf1a30Sjl { 109525cf1a30Sjl value |= mcp->mc_bank[bank].mcb_ptrl_cntl; 109625cf1a30Sjl ST_MAC_REG(MAC_PTRL_CNTL(mcp, bank), value); 109725cf1a30Sjl } 109825cf1a30Sjl 109925cf1a30Sjl static int 110025cf1a30Sjl mc_stop(mc_opl_t *mcp, int bank) 110125cf1a30Sjl { 110225cf1a30Sjl uint32_t reg; 110325cf1a30Sjl int count = 0; 110425cf1a30Sjl 110525cf1a30Sjl reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 110625cf1a30Sjl 110725cf1a30Sjl if (reg & MAC_CNTL_PTRL_START) 110825cf1a30Sjl MAC_PTRL_STOP(mcp, bank); 110925cf1a30Sjl 111025cf1a30Sjl while (count++ <= MAX_MC_LOOP_COUNT) { 111125cf1a30Sjl reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 111225cf1a30Sjl if ((reg & MAC_CNTL_PTRL_STATUS) == 0) 111325cf1a30Sjl return (0); 111425cf1a30Sjl delay(drv_usectohz(10 * 1000)); /* 10 m.s. */ 111525cf1a30Sjl } 111625cf1a30Sjl return (-1); 111725cf1a30Sjl } 111825cf1a30Sjl 111925cf1a30Sjl static void 112025cf1a30Sjl mc_read_ptrl_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 112125cf1a30Sjl { 112225cf1a30Sjl flt_stat->mf_cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 112325cf1a30Sjl MAC_CNTL_PTRL_ERRS; 112425cf1a30Sjl flt_stat->mf_err_add = LD_MAC_REG(MAC_PTRL_ERR_ADD(mcp, bank)); 112525cf1a30Sjl flt_stat->mf_err_log = LD_MAC_REG(MAC_PTRL_ERR_LOG(mcp, bank)); 112625cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 112725cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 112825cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 112925cf1a30Sjl } 113025cf1a30Sjl 113125cf1a30Sjl static void 113225cf1a30Sjl mc_read_mi_reg(mc_opl_t *mcp, int bank, mc_flt_stat_t *flt_stat) 113325cf1a30Sjl { 113425cf1a30Sjl uint32_t status, old_status; 113525cf1a30Sjl 113625cf1a30Sjl status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 113725cf1a30Sjl MAC_CNTL_MI_ERRS; 113825cf1a30Sjl old_status = 0; 113925cf1a30Sjl 114025cf1a30Sjl /* we keep reading until the status is stable */ 114125cf1a30Sjl while (old_status != status) { 114225cf1a30Sjl old_status = status; 114325cf1a30Sjl flt_stat->mf_err_add = 114425cf1a30Sjl LD_MAC_REG(MAC_MI_ERR_ADD(mcp, bank)); 114525cf1a30Sjl flt_stat->mf_err_log = 114625cf1a30Sjl LD_MAC_REG(MAC_MI_ERR_LOG(mcp, bank)); 114725cf1a30Sjl status = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)) & 114825cf1a30Sjl MAC_CNTL_MI_ERRS; 114925cf1a30Sjl if (status == old_status) { 115025cf1a30Sjl break; 115125cf1a30Sjl } 115225cf1a30Sjl } 115325cf1a30Sjl 115425cf1a30Sjl flt_stat->mf_cntl = status; 115525cf1a30Sjl flt_stat->mf_flt_maddr.ma_bd = mcp->mc_board_num; 115625cf1a30Sjl flt_stat->mf_flt_maddr.ma_bank = bank; 115725cf1a30Sjl flt_stat->mf_flt_maddr.ma_dimm_addr = flt_stat->mf_err_add; 115825cf1a30Sjl } 115925cf1a30Sjl 116025cf1a30Sjl 116125cf1a30Sjl /* 116225cf1a30Sjl * Error philosophy for mirror mode: 116325cf1a30Sjl * 116425cf1a30Sjl * PTRL (The error address for both banks are same, since ptrl stops if it 116525cf1a30Sjl * detects error.) 116625cf1a30Sjl * - Compaire error Report CMPE. 116725cf1a30Sjl * 116825cf1a30Sjl * - UE-UE Report MUE. No rewrite. 116925cf1a30Sjl * 117025cf1a30Sjl * - UE-* UE-(CE/OK). Rewrite to scrub UE. Report SUE. 117125cf1a30Sjl * 117225cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 117325cf1a30Sjl * If CE is permanent, inform SCF. Once for each 117425cf1a30Sjl * Dimm. If CE becomes UE or CMPE, go back to above. 117525cf1a30Sjl * 117625cf1a30Sjl * 117725cf1a30Sjl * MI (The error addresses for each bank are the same or different.) 117825cf1a30Sjl * - Compair error If addresses are the same. Just CMPE. 117925cf1a30Sjl * If addresses are different (this could happen 118025cf1a30Sjl * as a result of scrubbing. Report each seperately. 118125cf1a30Sjl * Only report error info on each side. 118225cf1a30Sjl * 118325cf1a30Sjl * - UE-UE Addresses are the same. Report MUE. 118425cf1a30Sjl * Addresses are different. Report SUE on each bank. 118525cf1a30Sjl * Rewrite to clear UE. 118625cf1a30Sjl * 118725cf1a30Sjl * - UE-* UE-(CE/OK) 118825cf1a30Sjl * Rewrite to clear UE. Report SUE for the bank. 118925cf1a30Sjl * 119025cf1a30Sjl * - CE-* CE-(CE/OK). Scrub to determine if CE is permanent. 119125cf1a30Sjl * If CE becomes UE or CMPE, go back to above. 119225cf1a30Sjl * 119325cf1a30Sjl */ 119425cf1a30Sjl 119525cf1a30Sjl static int 119625cf1a30Sjl mc_process_error_mir(mc_opl_t *mcp, mc_aflt_t *mc_aflt, mc_flt_stat_t *flt_stat) 119725cf1a30Sjl { 119825cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 119925cf1a30Sjl int i; 120025cf1a30Sjl int rv = 0; 120125cf1a30Sjl 120225cf1a30Sjl MC_LOG("process mirror errors cntl[0] = %x, cntl[1] = %x\n", 120325cf1a30Sjl flt_stat[0].mf_cntl, flt_stat[1].mf_cntl); 120425cf1a30Sjl 120525cf1a30Sjl if (ptrl_error) { 120625cf1a30Sjl if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) 120725cf1a30Sjl & MAC_CNTL_PTRL_ERRS) == 0) 120825cf1a30Sjl return (0); 120925cf1a30Sjl } else { 121025cf1a30Sjl if (((flt_stat[0].mf_cntl | flt_stat[1].mf_cntl) 121125cf1a30Sjl & MAC_CNTL_MI_ERRS) == 0) 121225cf1a30Sjl return (0); 121325cf1a30Sjl } 121425cf1a30Sjl 121525cf1a30Sjl /* 121625cf1a30Sjl * First we take care of the case of CE 121725cf1a30Sjl * because they can become UE or CMPE 121825cf1a30Sjl */ 121925cf1a30Sjl for (i = 0; i < 2; i++) { 122025cf1a30Sjl if (IS_CE_ONLY(flt_stat[i].mf_cntl, ptrl_error)) { 122125cf1a30Sjl MC_LOG("CE detected on bank %d\n", 122225cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank); 122325cf1a30Sjl mc_scrub_ce(mcp, flt_stat[i].mf_flt_maddr.ma_bank, 122425cf1a30Sjl &flt_stat[i], ptrl_error); 122525cf1a30Sjl rv = 1; 122625cf1a30Sjl } 122725cf1a30Sjl } 122825cf1a30Sjl 122925cf1a30Sjl /* The above scrubbing can turn CE into UE or CMPE */ 123025cf1a30Sjl 123125cf1a30Sjl /* 123225cf1a30Sjl * Now we distinguish two cases: same address or not 123325cf1a30Sjl * the same address. It might seem more intuitive to 123425cf1a30Sjl * distinguish PTRL v.s. MI error but it is more 123525cf1a30Sjl * complicated that way. 123625cf1a30Sjl */ 123725cf1a30Sjl 123825cf1a30Sjl if (flt_stat[0].mf_err_add == flt_stat[1].mf_err_add) { 123925cf1a30Sjl 124025cf1a30Sjl if (IS_CMPE(flt_stat[0].mf_cntl, ptrl_error) || 124125cf1a30Sjl IS_CMPE(flt_stat[1].mf_cntl, ptrl_error)) { 124225cf1a30Sjl flt_stat[0].mf_type = FLT_TYPE_CMPE; 124325cf1a30Sjl flt_stat[1].mf_type = FLT_TYPE_CMPE; 124425cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 124525cf1a30Sjl MC_LOG("cmpe error detected\n"); 124625cf1a30Sjl mc_aflt->mflt_nflts = 2; 124725cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 124825cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 124925cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 125025cf1a30Sjl mc_err_drain(mc_aflt); 125125cf1a30Sjl return (1); 125225cf1a30Sjl } 125325cf1a30Sjl 125425cf1a30Sjl if (IS_UE(flt_stat[0].mf_cntl, ptrl_error) && 125525cf1a30Sjl IS_UE(flt_stat[1].mf_cntl, ptrl_error)) { 125625cf1a30Sjl /* Both side are UE's */ 125725cf1a30Sjl 125825cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[0]); 125925cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[1]); 126025cf1a30Sjl MC_LOG("MUE detected\n"); 126125cf1a30Sjl flt_stat[0].mf_type = flt_stat[1].mf_type = 126225cf1a30Sjl FLT_TYPE_MUE; 126325cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_MUE; 126425cf1a30Sjl mc_aflt->mflt_nflts = 2; 126525cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[0]; 126625cf1a30Sjl mc_aflt->mflt_stat[1] = &flt_stat[1]; 126725cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 126825cf1a30Sjl mc_err_drain(mc_aflt); 126925cf1a30Sjl return (1); 127025cf1a30Sjl } 127125cf1a30Sjl 127225cf1a30Sjl /* Now the only case is UE/CE, UE/OK, or don't care */ 127325cf1a30Sjl for (i = 0; i < 2; i++) { 127425cf1a30Sjl if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 127525cf1a30Sjl /* If we have CE, we would have done REW */ 127625cf1a30Sjl if (IS_OK(flt_stat[i^1].mf_cntl, ptrl_error)) { 127725cf1a30Sjl (void) do_rewrite(mcp, 127825cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank, 127925cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_dimm_addr); 128025cf1a30Sjl } 128125cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_UE; 128225cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[i]); 128325cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_SUE; 128425cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 128525cf1a30Sjl mc_aflt->mflt_nflts = 1; 128625cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 128725cf1a30Sjl mc_err_drain(mc_aflt); 128825cf1a30Sjl /* Once we hit a UE/CE or UE/OK case, done */ 128925cf1a30Sjl return (1); 129025cf1a30Sjl } 129125cf1a30Sjl } 129225cf1a30Sjl 129325cf1a30Sjl } else { 129425cf1a30Sjl /* 129525cf1a30Sjl * addresses are different. That means errors 129625cf1a30Sjl * on the 2 banks are not related at all. 129725cf1a30Sjl */ 129825cf1a30Sjl for (i = 0; i < 2; i++) { 129925cf1a30Sjl if (IS_CMPE(flt_stat[i].mf_cntl, ptrl_error)) { 130025cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_CMPE; 130125cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CMPE; 130225cf1a30Sjl MC_LOG("cmpe error detected\n"); 130325cf1a30Sjl mc_aflt->mflt_nflts = 1; 130425cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 130525cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 130625cf1a30Sjl mc_err_drain(mc_aflt); 130725cf1a30Sjl /* no more report on this bank */ 130825cf1a30Sjl flt_stat[i].mf_cntl = 0; 130925cf1a30Sjl rv = 1; 131025cf1a30Sjl } 131125cf1a30Sjl } 131225cf1a30Sjl 131325cf1a30Sjl for (i = 0; i < 2; i++) { 131425cf1a30Sjl if (IS_UE(flt_stat[i].mf_cntl, ptrl_error)) { 131525cf1a30Sjl (void) do_rewrite(mcp, 131625cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_bank, 131725cf1a30Sjl flt_stat[i].mf_flt_maddr.ma_dimm_addr); 131825cf1a30Sjl flt_stat[i].mf_type = FLT_TYPE_UE; 131925cf1a30Sjl MAC_SET_ERRLOG_INFO(&flt_stat[i]); 132025cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_SUE; 132125cf1a30Sjl mc_aflt->mflt_stat[0] = &flt_stat[i]; 132225cf1a30Sjl mc_aflt->mflt_nflts = 1; 132325cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 132425cf1a30Sjl mc_err_drain(mc_aflt); 132525cf1a30Sjl rv = 1; 132625cf1a30Sjl } 132725cf1a30Sjl } 132825cf1a30Sjl } 132925cf1a30Sjl return (rv); 133025cf1a30Sjl } 133125cf1a30Sjl 133225cf1a30Sjl static void 133325cf1a30Sjl mc_error_handler_mir(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr) 133425cf1a30Sjl { 133525cf1a30Sjl mc_aflt_t mc_aflt; 133625cf1a30Sjl mc_flt_stat_t flt_stat[2], mi_flt_stat[2]; 133725cf1a30Sjl int other_bank; 133825cf1a30Sjl 133925cf1a30Sjl if (mc_stop(mcp, bank)) { 134025cf1a30Sjl cmn_err(CE_WARN, "Cannot stop Memory Patrol at /LSB%d/B%d\n", 134125cf1a30Sjl mcp->mc_board_num, bank); 134225cf1a30Sjl return; 134325cf1a30Sjl } 134425cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 134525cf1a30Sjl bzero(&flt_stat, 2 * sizeof (mc_flt_stat_t)); 134625cf1a30Sjl bzero(&mi_flt_stat, 2 * sizeof (mc_flt_stat_t)); 134725cf1a30Sjl 134825cf1a30Sjl mc_aflt.mflt_mcp = mcp; 134925cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 135025cf1a30Sjl 135125cf1a30Sjl /* Now read all the registers into flt_stat */ 135225cf1a30Sjl 135325cf1a30Sjl MC_LOG("Reading registers of bank %d\n", bank); 135425cf1a30Sjl /* patrol registers */ 135525cf1a30Sjl mc_read_ptrl_reg(mcp, bank, &flt_stat[0]); 135625cf1a30Sjl 135725cf1a30Sjl ASSERT(maddr); 135825cf1a30Sjl maddr->mi_maddr = flt_stat[0].mf_flt_maddr; 135925cf1a30Sjl 136025cf1a30Sjl MC_LOG("ptrl registers cntl %x add %x log %x\n", 136125cf1a30Sjl flt_stat[0].mf_cntl, 136225cf1a30Sjl flt_stat[0].mf_err_add, 136325cf1a30Sjl flt_stat[0].mf_err_log); 136425cf1a30Sjl 136525cf1a30Sjl /* MI registers */ 136625cf1a30Sjl mc_read_mi_reg(mcp, bank, &mi_flt_stat[0]); 136725cf1a30Sjl 136825cf1a30Sjl MC_LOG("MI registers cntl %x add %x log %x\n", 136925cf1a30Sjl mi_flt_stat[0].mf_cntl, 137025cf1a30Sjl mi_flt_stat[0].mf_err_add, 137125cf1a30Sjl mi_flt_stat[0].mf_err_log); 137225cf1a30Sjl 137325cf1a30Sjl other_bank = bank^1; 137425cf1a30Sjl 137525cf1a30Sjl MC_LOG("Reading registers of bank %d\n", other_bank); 137625cf1a30Sjl 137725cf1a30Sjl ASSERT(mcp->mc_bank[other_bank].mcb_status & BANK_INSTALLED); 137825cf1a30Sjl 137925cf1a30Sjl mc_read_ptrl_reg(mcp, other_bank, &flt_stat[1]); 138025cf1a30Sjl MC_LOG("ptrl registers cntl %x add %x log %x\n", 138125cf1a30Sjl flt_stat[1].mf_cntl, 138225cf1a30Sjl flt_stat[1].mf_err_add, 138325cf1a30Sjl flt_stat[1].mf_err_log); 138425cf1a30Sjl 138525cf1a30Sjl /* MI registers */ 138625cf1a30Sjl mc_read_mi_reg(mcp, other_bank, &mi_flt_stat[1]); 138725cf1a30Sjl MC_LOG("MI registers cntl %x add %x log %x\n", 138825cf1a30Sjl mi_flt_stat[1].mf_cntl, 138925cf1a30Sjl mi_flt_stat[1].mf_err_add, 139025cf1a30Sjl mi_flt_stat[1].mf_err_log); 139125cf1a30Sjl 139225cf1a30Sjl /* clear errors once we read all the registers */ 139325cf1a30Sjl MAC_CLEAR_ERRS(mcp, other_bank, 139425cf1a30Sjl (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 139525cf1a30Sjl 139625cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 139725cf1a30Sjl 139825cf1a30Sjl /* Process PTRL errors first */ 139925cf1a30Sjl 140025cf1a30Sjl /* if not error mode, cntl1 is 0 */ 140125cf1a30Sjl if ((flt_stat[0].mf_err_add & MAC_ERR_ADD_INVALID) || 140225cf1a30Sjl (flt_stat[0].mf_err_log & MAC_ERR_LOG_INVALID)) 140325cf1a30Sjl flt_stat[0].mf_cntl = 0; 140425cf1a30Sjl 140525cf1a30Sjl if ((flt_stat[1].mf_err_add & MAC_ERR_ADD_INVALID) || 140625cf1a30Sjl (flt_stat[1].mf_err_log & MAC_ERR_LOG_INVALID)) 140725cf1a30Sjl flt_stat[1].mf_cntl = 0; 140825cf1a30Sjl 140925cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 141025cf1a30Sjl maddr->mi_valid = mc_process_error_mir(mcp, &mc_aflt, &flt_stat[0]); 141125cf1a30Sjl 141225cf1a30Sjl mc_aflt.mflt_is_ptrl = 0; 141325cf1a30Sjl mc_process_error_mir(mcp, &mc_aflt, &mi_flt_stat[0]); 141425cf1a30Sjl } 141525cf1a30Sjl 141625cf1a30Sjl static int 141725cf1a30Sjl mc_process_error(mc_opl_t *mcp, int bank, mc_aflt_t *mc_aflt, 141825cf1a30Sjl mc_flt_stat_t *flt_stat) 141925cf1a30Sjl { 142025cf1a30Sjl int ptrl_error = mc_aflt->mflt_is_ptrl; 142125cf1a30Sjl int rv = 0; 142225cf1a30Sjl 142325cf1a30Sjl mc_aflt->mflt_erpt_class = NULL; 142425cf1a30Sjl if (IS_UE(flt_stat->mf_cntl, ptrl_error)) { 142525cf1a30Sjl MC_LOG("UE deteceted\n"); 142625cf1a30Sjl flt_stat->mf_type = FLT_TYPE_UE; 142725cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 142825cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 142925cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 143025cf1a30Sjl rv = 1; 143125cf1a30Sjl } else if (IS_CE(flt_stat->mf_cntl, ptrl_error)) { 143225cf1a30Sjl MC_LOG("CE deteceted\n"); 143325cf1a30Sjl MAC_SET_ERRLOG_INFO(flt_stat); 143425cf1a30Sjl 143525cf1a30Sjl /* Error type can change after scrubing */ 143625cf1a30Sjl mc_scrub_ce(mcp, bank, flt_stat, ptrl_error); 143725cf1a30Sjl 143825cf1a30Sjl if (flt_stat->mf_type == FLT_TYPE_PERMANENT_CE) { 143925cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_CE; 144025cf1a30Sjl mc_aflt->mflt_pr = PR_MCE; 144125cf1a30Sjl } else if (flt_stat->mf_type == FLT_TYPE_UE) { 144225cf1a30Sjl mc_aflt->mflt_erpt_class = MC_OPL_UE; 144325cf1a30Sjl mc_aflt->mflt_pr = PR_UE; 144425cf1a30Sjl } 144525cf1a30Sjl rv = 1; 144625cf1a30Sjl } 144725cf1a30Sjl MC_LOG("mc_process_error: fault type %x erpt %s\n", 144825cf1a30Sjl flt_stat->mf_type, 144925cf1a30Sjl mc_aflt->mflt_erpt_class); 145025cf1a30Sjl if (mc_aflt->mflt_erpt_class) { 145125cf1a30Sjl mc_aflt->mflt_stat[0] = flt_stat; 145225cf1a30Sjl mc_aflt->mflt_nflts = 1; 145325cf1a30Sjl mc_err_drain(mc_aflt); 145425cf1a30Sjl } 145525cf1a30Sjl return (rv); 145625cf1a30Sjl } 145725cf1a30Sjl 145825cf1a30Sjl static void 145925cf1a30Sjl mc_error_handler(mc_opl_t *mcp, int bank, mc_addr_info_t *maddr) 146025cf1a30Sjl { 146125cf1a30Sjl mc_aflt_t mc_aflt; 146225cf1a30Sjl mc_flt_stat_t flt_stat, mi_flt_stat; 146325cf1a30Sjl 146425cf1a30Sjl if (mc_stop(mcp, bank)) { 146525cf1a30Sjl cmn_err(CE_WARN, "Cannot stop Memory Patrol at /LSB%d/B%d\n", 146625cf1a30Sjl mcp->mc_board_num, bank); 146725cf1a30Sjl return; 146825cf1a30Sjl } 146925cf1a30Sjl 147025cf1a30Sjl bzero(&mc_aflt, sizeof (mc_aflt_t)); 147125cf1a30Sjl bzero(&flt_stat, sizeof (mc_flt_stat_t)); 147225cf1a30Sjl bzero(&mi_flt_stat, sizeof (mc_flt_stat_t)); 147325cf1a30Sjl 147425cf1a30Sjl mc_aflt.mflt_mcp = mcp; 147525cf1a30Sjl mc_aflt.mflt_id = gethrtime(); 147625cf1a30Sjl 147725cf1a30Sjl /* patrol registers */ 147825cf1a30Sjl mc_read_ptrl_reg(mcp, bank, &flt_stat); 147925cf1a30Sjl 148025cf1a30Sjl ASSERT(maddr); 148125cf1a30Sjl maddr->mi_maddr = flt_stat.mf_flt_maddr; 148225cf1a30Sjl 148325cf1a30Sjl MC_LOG("ptrl registers cntl %x add %x log %x\n", 148425cf1a30Sjl flt_stat.mf_cntl, 148525cf1a30Sjl flt_stat.mf_err_add, 148625cf1a30Sjl flt_stat.mf_err_log); 148725cf1a30Sjl 148825cf1a30Sjl /* MI registers */ 148925cf1a30Sjl mc_read_mi_reg(mcp, bank, &mi_flt_stat); 149025cf1a30Sjl 149125cf1a30Sjl MC_LOG("MI registers cntl %x add %x log %x\n", 149225cf1a30Sjl mi_flt_stat.mf_cntl, 149325cf1a30Sjl mi_flt_stat.mf_err_add, 149425cf1a30Sjl mi_flt_stat.mf_err_log); 149525cf1a30Sjl 149625cf1a30Sjl /* clear errors once we read all the registers */ 149725cf1a30Sjl MAC_CLEAR_ERRS(mcp, bank, (MAC_CNTL_PTRL_ERRS|MAC_CNTL_MI_ERRS)); 149825cf1a30Sjl 149925cf1a30Sjl mc_aflt.mflt_is_ptrl = 1; 150025cf1a30Sjl if ((flt_stat.mf_cntl & MAC_CNTL_PTRL_ERRS) && 150125cf1a30Sjl ((flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 150225cf1a30Sjl ((flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 150325cf1a30Sjl maddr->mi_valid = mc_process_error(mcp, bank, 150425cf1a30Sjl &mc_aflt, &flt_stat); 150525cf1a30Sjl } 150625cf1a30Sjl mc_aflt.mflt_is_ptrl = 0; 150725cf1a30Sjl if ((mi_flt_stat.mf_cntl & MAC_CNTL_MI_ERRS) && 150825cf1a30Sjl ((mi_flt_stat.mf_err_add & MAC_ERR_ADD_INVALID) == 0) && 150925cf1a30Sjl ((mi_flt_stat.mf_err_log & MAC_ERR_LOG_INVALID) == 0)) { 151025cf1a30Sjl mc_process_error(mcp, bank, &mc_aflt, &mi_flt_stat); 151125cf1a30Sjl } 151225cf1a30Sjl } 151325cf1a30Sjl 151425cf1a30Sjl /* 151525cf1a30Sjl * memory patrol error handling algorithm: 151625cf1a30Sjl * timeout() is used to do periodic polling 151725cf1a30Sjl * This is the flow chart. 151825cf1a30Sjl * timeout -> 151925cf1a30Sjl * mc_check_errors() 152025cf1a30Sjl * if memory bank is installed, read the status register 152125cf1a30Sjl * if any error bit is set, 152225cf1a30Sjl * -> mc_error_handler() 152325cf1a30Sjl * -> mc_stop() 152425cf1a30Sjl * -> read all error regsiters 152525cf1a30Sjl * -> mc_process_error() 152625cf1a30Sjl * determine error type 152725cf1a30Sjl * rewrite to clear error or scrub to determine CE type 152825cf1a30Sjl * inform SCF on permanent CE 152925cf1a30Sjl * -> mc_err_drain 153025cf1a30Sjl * page offline processing 153125cf1a30Sjl * -> mc_ereport_post() 153225cf1a30Sjl */ 153325cf1a30Sjl 153425cf1a30Sjl static void 153525cf1a30Sjl mc_check_errors_func(mc_opl_t *mcp) 153625cf1a30Sjl { 153725cf1a30Sjl mc_addr_info_t maddr_info; 153825cf1a30Sjl int i, error_count = 0; 153925cf1a30Sjl uint32_t stat, cntl; 154025cf1a30Sjl 154125cf1a30Sjl /* 154225cf1a30Sjl * scan errors. 154325cf1a30Sjl */ 154425cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 154525cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 154625cf1a30Sjl stat = ldphysio(MAC_PTRL_STAT(mcp, i)); 154725cf1a30Sjl cntl = ldphysio(MAC_PTRL_CNTL(mcp, i)); 154825cf1a30Sjl if (cntl & MAC_CNTL_PTRL_ADD_MAX) { 154925cf1a30Sjl mcp->mc_period++; 155025cf1a30Sjl MC_LOG("mc period %ld on " 155125cf1a30Sjl "/LSB%d/B%d\n", mcp->mc_period, 155225cf1a30Sjl mcp->mc_board_num, i); 155325cf1a30Sjl MAC_CLEAR_MAX(mcp, i); 155425cf1a30Sjl } 155525cf1a30Sjl if (mc_debug_show_all) { 155625cf1a30Sjl MC_LOG("/LSB%d/B%d stat %x cntl %x\n", 155725cf1a30Sjl mcp->mc_board_num, i, 155825cf1a30Sjl stat, cntl); 155925cf1a30Sjl } 156025cf1a30Sjl if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) { 156125cf1a30Sjl maddr_info.mi_valid = 0; 156225cf1a30Sjl maddr_info.mi_advance = 1; 156325cf1a30Sjl if (IS_MIRROR(mcp, i)) 156425cf1a30Sjl mc_error_handler_mir(mcp, i, 156525cf1a30Sjl &maddr_info); 156625cf1a30Sjl else 156725cf1a30Sjl mc_error_handler(mcp, i, &maddr_info); 156825cf1a30Sjl 156925cf1a30Sjl error_count++; 157025cf1a30Sjl restart_patrol(mcp, i, &maddr_info); 157125cf1a30Sjl } else { 157225cf1a30Sjl restart_patrol(mcp, i, NULL); 157325cf1a30Sjl } 157425cf1a30Sjl } 157525cf1a30Sjl } 157625cf1a30Sjl mc_process_scf_log(mcp); 157725cf1a30Sjl if (error_count > 0) 157825cf1a30Sjl mcp->mc_last_error += error_count; 157925cf1a30Sjl else 158025cf1a30Sjl mcp->mc_last_error = 0; 158125cf1a30Sjl } 158225cf1a30Sjl 158325cf1a30Sjl /* this is just a wrapper for the above func */ 158425cf1a30Sjl 158525cf1a30Sjl static void 158625cf1a30Sjl mc_check_errors(void *arg) 158725cf1a30Sjl { 158825cf1a30Sjl mc_opl_t *mcp = (mc_opl_t *)arg; 158925cf1a30Sjl clock_t interval; 159025cf1a30Sjl 159125cf1a30Sjl /* 159225cf1a30Sjl * scan errors. 159325cf1a30Sjl */ 159425cf1a30Sjl mutex_enter(&mcp->mc_lock); 159525cf1a30Sjl mcp->mc_tid = 0; 159625cf1a30Sjl if ((mcp->mc_status & MC_POLL_RUNNING) && 159725cf1a30Sjl !(mcp->mc_status & MC_SOFT_SUSPENDED)) { 159825cf1a30Sjl mc_check_errors_func(mcp); 159925cf1a30Sjl 160025cf1a30Sjl if (mcp->mc_last_error > 0) { 160125cf1a30Sjl interval = (mcp->mc_interval_hz) >> mcp->mc_last_error; 160225cf1a30Sjl if (interval < 1) 160325cf1a30Sjl interval = 1; 160425cf1a30Sjl } else 160525cf1a30Sjl interval = mcp->mc_interval_hz; 160625cf1a30Sjl 160725cf1a30Sjl mcp->mc_tid = timeout(mc_check_errors, mcp, 160825cf1a30Sjl interval); 160925cf1a30Sjl } 161025cf1a30Sjl mutex_exit(&mcp->mc_lock); 161125cf1a30Sjl } 161225cf1a30Sjl 161325cf1a30Sjl static void 161425cf1a30Sjl get_ptrl_start_address(mc_opl_t *mcp, int bank, mc_addr_t *maddr) 161525cf1a30Sjl { 161625cf1a30Sjl maddr->ma_bd = mcp->mc_board_num; 161725cf1a30Sjl maddr->ma_bank = bank; 161825cf1a30Sjl maddr->ma_dimm_addr = 0; 161925cf1a30Sjl } 162025cf1a30Sjl 162125cf1a30Sjl typedef struct mc_mem_range { 162225cf1a30Sjl uint64_t addr; 162325cf1a30Sjl uint64_t size; 162425cf1a30Sjl } mc_mem_range_t; 162525cf1a30Sjl 162625cf1a30Sjl static int 162725cf1a30Sjl get_base_address(mc_opl_t *mcp) 162825cf1a30Sjl { 162925cf1a30Sjl mc_mem_range_t *mem_range; 163025cf1a30Sjl int len; 163125cf1a30Sjl 163225cf1a30Sjl if (ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 163325cf1a30Sjl "sb-mem-ranges", (caddr_t)&mem_range, &len) != DDI_SUCCESS) { 163425cf1a30Sjl return (DDI_FAILURE); 163525cf1a30Sjl } 163625cf1a30Sjl 163725cf1a30Sjl mcp->mc_start_address = mem_range->addr; 163825cf1a30Sjl mcp->mc_size = mem_range->size; 163925cf1a30Sjl 164025cf1a30Sjl kmem_free(mem_range, len); 164125cf1a30Sjl return (DDI_SUCCESS); 164225cf1a30Sjl } 164325cf1a30Sjl 164425cf1a30Sjl struct mc_addr_spec { 164525cf1a30Sjl uint32_t bank; 164625cf1a30Sjl uint32_t phys_hi; 164725cf1a30Sjl uint32_t phys_lo; 164825cf1a30Sjl }; 164925cf1a30Sjl 165025cf1a30Sjl #define REGS_PA(m, i) ((((uint64_t)m[i].phys_hi)<<32) | m[i].phys_lo) 165125cf1a30Sjl 165225cf1a30Sjl static char *mc_tbl_name[] = { 165325cf1a30Sjl "cs0-mc-pa-trans-table", 165425cf1a30Sjl "cs1-mc-pa-trans-table" 165525cf1a30Sjl }; 165625cf1a30Sjl 165725cf1a30Sjl static int 165825cf1a30Sjl mc_valid_pa(mc_opl_t *mcp, uint64_t pa) 165925cf1a30Sjl { 166025cf1a30Sjl struct memlist *ml; 166125cf1a30Sjl 166225cf1a30Sjl if (mcp->mlist == NULL) 166325cf1a30Sjl mc_get_mlist(mcp); 166425cf1a30Sjl 166525cf1a30Sjl for (ml = mcp->mlist; ml; ml = ml->next) { 166625cf1a30Sjl if (ml->address <= pa && pa < (ml->address + ml->size)) 166725cf1a30Sjl return (1); 166825cf1a30Sjl } 166925cf1a30Sjl return (0); 167025cf1a30Sjl } 167125cf1a30Sjl 167225cf1a30Sjl static void 167325cf1a30Sjl mc_memlist_delete(struct memlist *mlist) 167425cf1a30Sjl { 167525cf1a30Sjl struct memlist *ml; 167625cf1a30Sjl 167725cf1a30Sjl for (ml = mlist; ml; ml = mlist) { 167825cf1a30Sjl mlist = ml->next; 167925cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 168025cf1a30Sjl } 168125cf1a30Sjl } 168225cf1a30Sjl 168325cf1a30Sjl static struct memlist * 168425cf1a30Sjl mc_memlist_dup(struct memlist *mlist) 168525cf1a30Sjl { 168625cf1a30Sjl struct memlist *hl = NULL, *tl, **mlp; 168725cf1a30Sjl 168825cf1a30Sjl if (mlist == NULL) 168925cf1a30Sjl return (NULL); 169025cf1a30Sjl 169125cf1a30Sjl mlp = &hl; 169225cf1a30Sjl tl = *mlp; 169325cf1a30Sjl for (; mlist; mlist = mlist->next) { 169425cf1a30Sjl *mlp = kmem_alloc(sizeof (struct memlist), KM_SLEEP); 169525cf1a30Sjl (*mlp)->address = mlist->address; 169625cf1a30Sjl (*mlp)->size = mlist->size; 169725cf1a30Sjl (*mlp)->prev = tl; 169825cf1a30Sjl tl = *mlp; 169925cf1a30Sjl mlp = &((*mlp)->next); 170025cf1a30Sjl } 170125cf1a30Sjl *mlp = NULL; 170225cf1a30Sjl 170325cf1a30Sjl return (hl); 170425cf1a30Sjl } 170525cf1a30Sjl 170625cf1a30Sjl 170725cf1a30Sjl static struct memlist * 170825cf1a30Sjl mc_memlist_del_span(struct memlist *mlist, uint64_t base, uint64_t len) 170925cf1a30Sjl { 171025cf1a30Sjl uint64_t end; 171125cf1a30Sjl struct memlist *ml, *tl, *nlp; 171225cf1a30Sjl 171325cf1a30Sjl if (mlist == NULL) 171425cf1a30Sjl return (NULL); 171525cf1a30Sjl 171625cf1a30Sjl end = base + len; 171725cf1a30Sjl if ((end <= mlist->address) || (base == end)) 171825cf1a30Sjl return (mlist); 171925cf1a30Sjl 172025cf1a30Sjl for (tl = ml = mlist; ml; tl = ml, ml = nlp) { 172125cf1a30Sjl uint64_t mend; 172225cf1a30Sjl 172325cf1a30Sjl nlp = ml->next; 172425cf1a30Sjl 172525cf1a30Sjl if (end <= ml->address) 172625cf1a30Sjl break; 172725cf1a30Sjl 172825cf1a30Sjl mend = ml->address + ml->size; 172925cf1a30Sjl if (base < mend) { 173025cf1a30Sjl if (base <= ml->address) { 173125cf1a30Sjl ml->address = end; 173225cf1a30Sjl if (end >= mend) 173325cf1a30Sjl ml->size = 0ull; 173425cf1a30Sjl else 173525cf1a30Sjl ml->size = mend - ml->address; 173625cf1a30Sjl } else { 173725cf1a30Sjl ml->size = base - ml->address; 173825cf1a30Sjl if (end < mend) { 173925cf1a30Sjl struct memlist *nl; 174025cf1a30Sjl /* 174125cf1a30Sjl * splitting an memlist entry. 174225cf1a30Sjl */ 174325cf1a30Sjl nl = kmem_alloc(sizeof (struct memlist), 174425cf1a30Sjl KM_SLEEP); 174525cf1a30Sjl nl->address = end; 174625cf1a30Sjl nl->size = mend - nl->address; 174725cf1a30Sjl if ((nl->next = nlp) != NULL) 174825cf1a30Sjl nlp->prev = nl; 174925cf1a30Sjl nl->prev = ml; 175025cf1a30Sjl ml->next = nl; 175125cf1a30Sjl nlp = nl; 175225cf1a30Sjl } 175325cf1a30Sjl } 175425cf1a30Sjl if (ml->size == 0ull) { 175525cf1a30Sjl if (ml == mlist) { 175625cf1a30Sjl if ((mlist = nlp) != NULL) 175725cf1a30Sjl nlp->prev = NULL; 175825cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 175925cf1a30Sjl if (mlist == NULL) 176025cf1a30Sjl break; 176125cf1a30Sjl ml = nlp; 176225cf1a30Sjl } else { 176325cf1a30Sjl if ((tl->next = nlp) != NULL) 176425cf1a30Sjl nlp->prev = tl; 176525cf1a30Sjl kmem_free(ml, sizeof (struct memlist)); 176625cf1a30Sjl ml = tl; 176725cf1a30Sjl } 176825cf1a30Sjl } 176925cf1a30Sjl } 177025cf1a30Sjl } 177125cf1a30Sjl 177225cf1a30Sjl return (mlist); 177325cf1a30Sjl } 177425cf1a30Sjl 177525cf1a30Sjl static void 177625cf1a30Sjl mc_get_mlist(mc_opl_t *mcp) 177725cf1a30Sjl { 177825cf1a30Sjl struct memlist *mlist; 177925cf1a30Sjl 178025cf1a30Sjl memlist_read_lock(); 178125cf1a30Sjl mlist = mc_memlist_dup(phys_install); 178225cf1a30Sjl memlist_read_unlock(); 178325cf1a30Sjl 178425cf1a30Sjl if (mlist) { 178525cf1a30Sjl mlist = mc_memlist_del_span(mlist, 0ull, mcp->mc_start_address); 178625cf1a30Sjl } 178725cf1a30Sjl 178825cf1a30Sjl if (mlist) { 178925cf1a30Sjl uint64_t startpa, endpa; 179025cf1a30Sjl 179125cf1a30Sjl startpa = mcp->mc_start_address + mcp->mc_size; 179225cf1a30Sjl endpa = ptob(physmax + 1); 179325cf1a30Sjl if (endpa > startpa) { 179425cf1a30Sjl mlist = mc_memlist_del_span(mlist, 179525cf1a30Sjl startpa, endpa - startpa); 179625cf1a30Sjl } 179725cf1a30Sjl } 179825cf1a30Sjl 179925cf1a30Sjl if (mlist) { 180025cf1a30Sjl mcp->mlist = mlist; 180125cf1a30Sjl } 180225cf1a30Sjl } 180325cf1a30Sjl 180425cf1a30Sjl int 180525cf1a30Sjl mc_board_add(mc_opl_t *mcp) 180625cf1a30Sjl { 180725cf1a30Sjl struct mc_addr_spec *macaddr; 180825cf1a30Sjl int len, i, bk, cc; 180925cf1a30Sjl mc_addr_info_t maddr; 181025cf1a30Sjl uint32_t mirr; 181125cf1a30Sjl 181225cf1a30Sjl mutex_init(&mcp->mc_lock, NULL, MUTEX_DRIVER, NULL); 181325cf1a30Sjl 181425cf1a30Sjl /* 181525cf1a30Sjl * Get configurations from "pseudo-mc" node which includes: 181625cf1a30Sjl * board# : LSB number 181725cf1a30Sjl * mac-addr : physical base address of MAC registers 181825cf1a30Sjl * csX-mac-pa-trans-table: translation table from DIMM address 181925cf1a30Sjl * to physical address or vice versa. 182025cf1a30Sjl */ 182125cf1a30Sjl mcp->mc_board_num = (int)ddi_getprop(DDI_DEV_T_ANY, mcp->mc_dip, 182225cf1a30Sjl DDI_PROP_DONTPASS, "board#", -1); 182325cf1a30Sjl 182425cf1a30Sjl /* 182525cf1a30Sjl * Get start address in this CAB. It can be gotten from 182625cf1a30Sjl * "sb-mem-ranges" property. 182725cf1a30Sjl */ 182825cf1a30Sjl 182925cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 183025cf1a30Sjl mutex_destroy(&mcp->mc_lock); 183125cf1a30Sjl return (DDI_FAILURE); 183225cf1a30Sjl } 183325cf1a30Sjl /* get mac-pa trans tables */ 183425cf1a30Sjl for (i = 0; i < MC_TT_CS; i++) { 183525cf1a30Sjl len = MC_TT_ENTRIES; 183625cf1a30Sjl cc = ddi_getlongprop_buf(DDI_DEV_T_ANY, mcp->mc_dip, 183725cf1a30Sjl DDI_PROP_DONTPASS, mc_tbl_name[i], 183825cf1a30Sjl (caddr_t)mcp->mc_trans_table[i], &len); 183925cf1a30Sjl 184025cf1a30Sjl if (cc != DDI_SUCCESS) { 184125cf1a30Sjl bzero(mcp->mc_trans_table[i], MC_TT_ENTRIES); 184225cf1a30Sjl } 184325cf1a30Sjl } 184425cf1a30Sjl mcp->mlist = NULL; 184525cf1a30Sjl 184625cf1a30Sjl mc_get_mlist(mcp); 184725cf1a30Sjl 184825cf1a30Sjl /* initialize bank informations */ 184925cf1a30Sjl cc = ddi_getlongprop(DDI_DEV_T_ANY, mcp->mc_dip, DDI_PROP_DONTPASS, 185025cf1a30Sjl "mc-addr", (caddr_t)&macaddr, &len); 185125cf1a30Sjl if (cc != DDI_SUCCESS) { 185225cf1a30Sjl cmn_err(CE_WARN, "Cannot get mc-addr. err=%d\n", cc); 185325cf1a30Sjl mutex_destroy(&mcp->mc_lock); 185425cf1a30Sjl return (DDI_FAILURE); 185525cf1a30Sjl } 185625cf1a30Sjl 185725cf1a30Sjl for (i = 0; i < len / sizeof (struct mc_addr_spec); i++) { 185825cf1a30Sjl struct mc_bank *bankp; 185925cf1a30Sjl uint32_t reg; 186025cf1a30Sjl 186125cf1a30Sjl /* 186225cf1a30Sjl * setup bank 186325cf1a30Sjl */ 186425cf1a30Sjl bk = macaddr[i].bank; 186525cf1a30Sjl bankp = &(mcp->mc_bank[bk]); 186625cf1a30Sjl bankp->mcb_status = BANK_INSTALLED; 186725cf1a30Sjl bankp->mcb_reg_base = REGS_PA(macaddr, i); 186825cf1a30Sjl 186925cf1a30Sjl reg = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bk)); 187025cf1a30Sjl bankp->mcb_ptrl_cntl = (reg & MAC_CNTL_PTRL_PRESERVE_BITS); 187125cf1a30Sjl 187225cf1a30Sjl /* 187325cf1a30Sjl * check if mirror mode 187425cf1a30Sjl */ 187525cf1a30Sjl mirr = LD_MAC_REG(MAC_MIRR(mcp, bk)); 187625cf1a30Sjl 187725cf1a30Sjl if (mirr & MAC_MIRR_MIRROR_MODE) { 187825cf1a30Sjl MC_LOG("Mirror -> /LSB%d/B%d\n", 187925cf1a30Sjl mcp->mc_board_num, bk); 188025cf1a30Sjl bankp->mcb_status |= BANK_MIRROR_MODE; 188125cf1a30Sjl /* 188225cf1a30Sjl * The following bit is only used for 188325cf1a30Sjl * error injection. We should clear it 188425cf1a30Sjl */ 188525cf1a30Sjl if (mirr & MAC_MIRR_BANK_EXCLUSIVE) 188625cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bk), 188725cf1a30Sjl 0); 188825cf1a30Sjl } 188925cf1a30Sjl 189025cf1a30Sjl /* 189125cf1a30Sjl * restart if not mirror mode or the other bank 189225cf1a30Sjl * of the mirror is not running 189325cf1a30Sjl */ 189425cf1a30Sjl if (!(mirr & MAC_MIRR_MIRROR_MODE) || 189525cf1a30Sjl !(mcp->mc_bank[bk^1].mcb_status & 189625cf1a30Sjl BANK_PTRL_RUNNING)) { 189725cf1a30Sjl MC_LOG("Starting up /LSB%d/B%d\n", 189825cf1a30Sjl mcp->mc_board_num, bk); 189925cf1a30Sjl get_ptrl_start_address(mcp, bk, &maddr.mi_maddr); 190025cf1a30Sjl maddr.mi_valid = 1; 190125cf1a30Sjl maddr.mi_advance = 0; 190225cf1a30Sjl restart_patrol(mcp, bk, &maddr); 190325cf1a30Sjl } else { 190425cf1a30Sjl MC_LOG("Not starting up /LSB%d/B%d\n", 190525cf1a30Sjl mcp->mc_board_num, bk); 190625cf1a30Sjl } 190725cf1a30Sjl bankp->mcb_status |= BANK_PTRL_RUNNING; 190825cf1a30Sjl } 190925cf1a30Sjl kmem_free(macaddr, len); 191025cf1a30Sjl 191125cf1a30Sjl /* 191225cf1a30Sjl * set interval in HZ. 191325cf1a30Sjl */ 191425cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 191525cf1a30Sjl mcp->mc_scf_retry[i] = 0; 191625cf1a30Sjl } 191725cf1a30Sjl mcp->mc_last_error = 0; 191825cf1a30Sjl mcp->mc_period = 0; 191925cf1a30Sjl 192025cf1a30Sjl mcp->mc_interval_hz = drv_usectohz(mc_patrol_interval_sec * 1000000); 192125cf1a30Sjl /* restart memory patrol checking */ 192225cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 192325cf1a30Sjl mcp->mc_tid = timeout(mc_check_errors, mcp, mcp->mc_interval_hz); 192425cf1a30Sjl 192525cf1a30Sjl return (DDI_SUCCESS); 192625cf1a30Sjl } 192725cf1a30Sjl 192825cf1a30Sjl int 192925cf1a30Sjl mc_board_del(mc_opl_t *mcp) 193025cf1a30Sjl { 193125cf1a30Sjl int i; 193225cf1a30Sjl scf_log_t *p; 193325cf1a30Sjl timeout_id_t tid = 0; 193425cf1a30Sjl 193525cf1a30Sjl /* 193625cf1a30Sjl * cleanup mac state 193725cf1a30Sjl */ 193825cf1a30Sjl mutex_enter(&mcp->mc_lock); 193925cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 194025cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 194125cf1a30Sjl if (mc_stop(mcp, i)) { 194225cf1a30Sjl mutex_exit(&mcp->mc_lock); 194325cf1a30Sjl return (-1); 194425cf1a30Sjl } 194525cf1a30Sjl mcp->mc_bank[i].mcb_status &= ~BANK_INSTALLED; 194625cf1a30Sjl } 194725cf1a30Sjl } 194825cf1a30Sjl 194925cf1a30Sjl /* stop memory patrol checking */ 195025cf1a30Sjl if (mcp->mc_status & MC_POLL_RUNNING) { 195125cf1a30Sjl mcp->mc_status &= ~MC_POLL_RUNNING; 195225cf1a30Sjl tid = mcp->mc_tid; 195325cf1a30Sjl mcp->mc_tid = 0; 195425cf1a30Sjl } 195525cf1a30Sjl 195625cf1a30Sjl /* just throw away all the scf logs */ 195725cf1a30Sjl while ((p = mcp->mc_scf_log) != NULL) { 195825cf1a30Sjl mcp->mc_scf_log = p->sl_next; 195925cf1a30Sjl mcp->mc_scf_total--; 196025cf1a30Sjl kmem_free(p, sizeof (scf_log_t)); 196125cf1a30Sjl } 196225cf1a30Sjl 196325cf1a30Sjl if (mcp->mlist) 196425cf1a30Sjl mc_memlist_delete(mcp->mlist); 196525cf1a30Sjl 196625cf1a30Sjl mutex_exit(&mcp->mc_lock); 196725cf1a30Sjl if (tid) 196825cf1a30Sjl (void) untimeout(tid); 196925cf1a30Sjl 197025cf1a30Sjl mutex_destroy(&mcp->mc_lock); 197125cf1a30Sjl return (DDI_SUCCESS); 197225cf1a30Sjl } 197325cf1a30Sjl 197425cf1a30Sjl int 197525cf1a30Sjl mc_suspend(mc_opl_t *mcp, uint32_t flag) 197625cf1a30Sjl { 197725cf1a30Sjl timeout_id_t tid = 0; 197825cf1a30Sjl int i; 197925cf1a30Sjl /* stop memory patrol checking */ 198025cf1a30Sjl mutex_enter(&mcp->mc_lock); 198125cf1a30Sjl if (mcp->mc_status & MC_POLL_RUNNING) { 198225cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 198325cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 198425cf1a30Sjl if (mc_stop(mcp, i)) { 198525cf1a30Sjl mutex_exit(&mcp->mc_lock); 198625cf1a30Sjl return (-1); 198725cf1a30Sjl } 198825cf1a30Sjl } 198925cf1a30Sjl } 199025cf1a30Sjl mcp->mc_status &= ~MC_POLL_RUNNING; 199125cf1a30Sjl tid = mcp->mc_tid; 199225cf1a30Sjl } 199325cf1a30Sjl mcp->mc_status |= flag; 199425cf1a30Sjl mcp->mc_tid = 0; 199525cf1a30Sjl mutex_exit(&mcp->mc_lock); 199625cf1a30Sjl if (tid) 199725cf1a30Sjl (void) untimeout(tid); 199825cf1a30Sjl 199925cf1a30Sjl return (DDI_SUCCESS); 200025cf1a30Sjl } 200125cf1a30Sjl 200225cf1a30Sjl /* caller must clear the SUSPEND bits or this will do nothing */ 200325cf1a30Sjl 200425cf1a30Sjl int 200525cf1a30Sjl mc_resume(mc_opl_t *mcp, uint32_t flag) 200625cf1a30Sjl { 200725cf1a30Sjl int i; 200825cf1a30Sjl uint64_t basepa; 200925cf1a30Sjl 201025cf1a30Sjl mutex_enter(&mcp->mc_lock); 201125cf1a30Sjl basepa = mcp->mc_start_address; 201225cf1a30Sjl if (get_base_address(mcp) == DDI_FAILURE) { 201325cf1a30Sjl mutex_exit(&mcp->mc_lock); 201425cf1a30Sjl return (DDI_FAILURE); 201525cf1a30Sjl } 201625cf1a30Sjl 201725cf1a30Sjl if (basepa != mcp->mc_start_address) { 201825cf1a30Sjl if (mcp->mlist) 201925cf1a30Sjl mc_memlist_delete(mcp->mlist); 202025cf1a30Sjl mcp->mlist = NULL; 202125cf1a30Sjl mc_get_mlist(mcp); 202225cf1a30Sjl } 202325cf1a30Sjl 202425cf1a30Sjl mcp->mc_status &= ~flag; 202525cf1a30Sjl mcp->mc_list->mc_start_address = mcp->mc_start_address; 202625cf1a30Sjl 202725cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 202825cf1a30Sjl mutex_exit(&mcp->mc_lock); 202925cf1a30Sjl return (DDI_SUCCESS); 203025cf1a30Sjl } 203125cf1a30Sjl 203225cf1a30Sjl if (!(mcp->mc_status & MC_POLL_RUNNING)) { 203325cf1a30Sjl /* restart memory patrol checking */ 203425cf1a30Sjl mcp->mc_status |= MC_POLL_RUNNING; 203525cf1a30Sjl for (i = 0; i < BANKNUM_PER_SB; i++) { 203625cf1a30Sjl if (mcp->mc_bank[i].mcb_status & BANK_INSTALLED) { 203725cf1a30Sjl restart_patrol(mcp, i, NULL); 203825cf1a30Sjl } 203925cf1a30Sjl } 204025cf1a30Sjl /* check error asap */ 204125cf1a30Sjl mcp->mc_tid = timeout(mc_check_errors, mcp, 1); 204225cf1a30Sjl } 204325cf1a30Sjl mutex_exit(&mcp->mc_lock); 204425cf1a30Sjl 204525cf1a30Sjl return (DDI_SUCCESS); 204625cf1a30Sjl } 204725cf1a30Sjl 204825cf1a30Sjl static mc_opl_t * 204925cf1a30Sjl mc_pa_to_mcp(uint64_t pa) 205025cf1a30Sjl { 205125cf1a30Sjl mc_inst_list_t *p; 205225cf1a30Sjl ASSERT(MUTEX_HELD(&mcmutex)); 205325cf1a30Sjl for (p = mc_instances; p; p = p->next) { 205425cf1a30Sjl /* if mac patrol is suspended, we cannot rely on it */ 205525cf1a30Sjl if (!(p->mc_opl->mc_status & MC_POLL_RUNNING) || 205625cf1a30Sjl (p->mc_opl->mc_status & MC_SOFT_SUSPENDED)) 205725cf1a30Sjl continue; 205825cf1a30Sjl if ((p->mc_start_address <= pa) && 205925cf1a30Sjl (pa < (p->mc_start_address + p->mc_size))) { 206025cf1a30Sjl return (p->mc_opl); 206125cf1a30Sjl } 206225cf1a30Sjl } 206325cf1a30Sjl return (NULL); 206425cf1a30Sjl } 206525cf1a30Sjl 206625cf1a30Sjl /* 206725cf1a30Sjl * Get Physical Board number from Logical one. 206825cf1a30Sjl */ 206925cf1a30Sjl static int 207025cf1a30Sjl mc_opl_get_physical_board(int sb) 207125cf1a30Sjl { 207225cf1a30Sjl if (&opl_get_physical_board) { 207325cf1a30Sjl return (opl_get_physical_board(sb)); 207425cf1a30Sjl } 207525cf1a30Sjl 207625cf1a30Sjl cmn_err(CE_NOTE, "!opl_get_physical_board() not loaded\n"); 207725cf1a30Sjl return (-1); 207825cf1a30Sjl } 207925cf1a30Sjl 208025cf1a30Sjl /* ARGSUSED */ 208125cf1a30Sjl int 208225cf1a30Sjl mc_get_mem_unum(int synd_code, uint64_t flt_addr, char *buf, int buflen, 208325cf1a30Sjl int *lenp) 208425cf1a30Sjl { 208525cf1a30Sjl mc_opl_t *mcp; 208625cf1a30Sjl int bank; 208725cf1a30Sjl int sb; 208825cf1a30Sjl 208925cf1a30Sjl mutex_enter(&mcmutex); 209025cf1a30Sjl 209125cf1a30Sjl if (((mcp = mc_pa_to_mcp(flt_addr)) == NULL) || 209225cf1a30Sjl (!pa_is_valid(mcp, flt_addr))) { 209325cf1a30Sjl mutex_exit(&mcmutex); 209425cf1a30Sjl if (snprintf(buf, buflen, "UNKNOWN") >= buflen) { 209525cf1a30Sjl return (ENOSPC); 209625cf1a30Sjl } else { 209725cf1a30Sjl if (lenp) 209825cf1a30Sjl *lenp = strlen(buf); 209925cf1a30Sjl } 210025cf1a30Sjl return (0); 210125cf1a30Sjl } 210225cf1a30Sjl 210325cf1a30Sjl bank = pa_to_bank(mcp, flt_addr - mcp->mc_start_address); 210425cf1a30Sjl sb = mc_opl_get_physical_board(mcp->mc_board_num); 210525cf1a30Sjl 210625cf1a30Sjl if (sb == -1) { 210725cf1a30Sjl mutex_exit(&mcmutex); 210825cf1a30Sjl return (ENXIO); 210925cf1a30Sjl } 211025cf1a30Sjl 211125cf1a30Sjl if (snprintf(buf, buflen, "/CMU%d/B%d", sb, bank) >= buflen) { 211225cf1a30Sjl mutex_exit(&mcmutex); 211325cf1a30Sjl return (ENOSPC); 211425cf1a30Sjl } else { 211525cf1a30Sjl if (lenp) 211625cf1a30Sjl *lenp = strlen(buf); 211725cf1a30Sjl } 211825cf1a30Sjl mutex_exit(&mcmutex); 211925cf1a30Sjl return (0); 212025cf1a30Sjl } 212125cf1a30Sjl 212225cf1a30Sjl int 212325cf1a30Sjl opl_mc_suspend() 212425cf1a30Sjl { 212525cf1a30Sjl mc_opl_t *mcp; 212625cf1a30Sjl mc_inst_list_t *p; 212725cf1a30Sjl 212825cf1a30Sjl mutex_enter(&mcmutex); 212925cf1a30Sjl 213025cf1a30Sjl for (p = mc_instances; p; p = p->next) { 213125cf1a30Sjl mcp = p->mc_opl; 213225cf1a30Sjl (void) mc_suspend(mcp, MC_SOFT_SUSPENDED); 213325cf1a30Sjl } 213425cf1a30Sjl 213525cf1a30Sjl mutex_exit(&mcmutex); 213625cf1a30Sjl return (0); 213725cf1a30Sjl } 213825cf1a30Sjl 213925cf1a30Sjl int 214025cf1a30Sjl opl_mc_resume() 214125cf1a30Sjl { 214225cf1a30Sjl mc_opl_t *mcp; 214325cf1a30Sjl mc_inst_list_t *p; 214425cf1a30Sjl 214525cf1a30Sjl mutex_enter(&mcmutex); 214625cf1a30Sjl 214725cf1a30Sjl for (p = mc_instances; p; p = p->next) { 214825cf1a30Sjl mcp = p->mc_opl; 214925cf1a30Sjl (void) mc_resume(mcp, MC_SOFT_SUSPENDED); 215025cf1a30Sjl } 215125cf1a30Sjl 215225cf1a30Sjl mutex_exit(&mcmutex); 215325cf1a30Sjl return (0); 215425cf1a30Sjl } 215525cf1a30Sjl 215625cf1a30Sjl static void 215725cf1a30Sjl insert_mcp(mc_opl_t *mcp) 215825cf1a30Sjl { 215925cf1a30Sjl mc_inst_list_t *p; 216025cf1a30Sjl 216125cf1a30Sjl p = kmem_zalloc(sizeof (mc_inst_list_t), KM_SLEEP); 216225cf1a30Sjl p->mc_opl = mcp; 216325cf1a30Sjl p->mc_board_num = mcp->mc_board_num; 216425cf1a30Sjl p->mc_start_address = mcp->mc_start_address; 216525cf1a30Sjl p->mc_size = mcp->mc_size; 216625cf1a30Sjl mcp->mc_list = p; 216725cf1a30Sjl 216825cf1a30Sjl mutex_enter(&mcmutex); 216925cf1a30Sjl 217025cf1a30Sjl p->next = mc_instances; 217125cf1a30Sjl mc_instances = p; 217225cf1a30Sjl 217325cf1a30Sjl mutex_exit(&mcmutex); 217425cf1a30Sjl } 217525cf1a30Sjl 217625cf1a30Sjl static void 217725cf1a30Sjl delete_mcp(mc_opl_t *mcp) 217825cf1a30Sjl { 217925cf1a30Sjl mc_inst_list_t *prev, *current; 218025cf1a30Sjl mc_inst_list_t *p; 218125cf1a30Sjl 218225cf1a30Sjl p = mcp->mc_list; 218325cf1a30Sjl 218425cf1a30Sjl if (mc_instances == p) { 218525cf1a30Sjl mc_instances = p->next; 218625cf1a30Sjl kmem_free(p, sizeof (mc_inst_list_t)); 218725cf1a30Sjl return; 218825cf1a30Sjl } 218925cf1a30Sjl prev = mc_instances; 219025cf1a30Sjl for (current = mc_instances; current != NULL; current = current->next) { 219125cf1a30Sjl if (current == p) { 219225cf1a30Sjl prev->next = p->next; 219325cf1a30Sjl kmem_free(p, sizeof (mc_inst_list_t)); 219425cf1a30Sjl return; 219525cf1a30Sjl } 219625cf1a30Sjl prev = current; 219725cf1a30Sjl } 219825cf1a30Sjl } 219925cf1a30Sjl 220025cf1a30Sjl /* Error injection interface */ 220125cf1a30Sjl 220225cf1a30Sjl /* ARGSUSED */ 220325cf1a30Sjl int 220425cf1a30Sjl mc_inject_error(int error_type, uint64_t pa, uint32_t flags) 220525cf1a30Sjl { 220625cf1a30Sjl mc_opl_t *mcp; 220725cf1a30Sjl int bank; 220825cf1a30Sjl uint32_t dimm_addr; 220925cf1a30Sjl uint32_t cntl; 221025cf1a30Sjl mc_addr_info_t maddr; 221125cf1a30Sjl uint32_t data, stat; 221225cf1a30Sjl int both_sides = 0; 221325cf1a30Sjl uint64_t pa0; 221425cf1a30Sjl on_trap_data_t otd; 221525cf1a30Sjl extern void cpu_flush_ecache(void); 221625cf1a30Sjl 221725cf1a30Sjl MC_LOG("HW mc_inject_error(%x, %lx, %x)\n", error_type, pa, flags); 221825cf1a30Sjl 221925cf1a30Sjl mutex_enter(&mcmutex); 222025cf1a30Sjl 222125cf1a30Sjl if ((mcp = mc_pa_to_mcp(pa)) == NULL) { 222225cf1a30Sjl mutex_exit(&mcmutex); 222325cf1a30Sjl MC_LOG("mc_inject_error: invalid pa\n"); 222425cf1a30Sjl return (ENOTSUP); 222525cf1a30Sjl } 222625cf1a30Sjl 222725cf1a30Sjl mutex_enter(&mcp->mc_lock); 222825cf1a30Sjl mutex_exit(&mcmutex); 222925cf1a30Sjl 223025cf1a30Sjl if (mcp->mc_status & (MC_SOFT_SUSPENDED | MC_DRIVER_SUSPENDED)) { 223125cf1a30Sjl mutex_exit(&mcp->mc_lock); 223225cf1a30Sjl MC_LOG("mc-opl has been suspended. No error injection.\n"); 223325cf1a30Sjl return (EBUSY); 223425cf1a30Sjl } 223525cf1a30Sjl 223625cf1a30Sjl /* convert pa to offset within the board */ 223725cf1a30Sjl MC_LOG("pa %lx, offset %lx\n", pa, pa - mcp->mc_start_address); 223825cf1a30Sjl 223925cf1a30Sjl if (!pa_is_valid(mcp, pa)) { 224025cf1a30Sjl mutex_exit(&mcp->mc_lock); 224125cf1a30Sjl return (EINVAL); 224225cf1a30Sjl } 224325cf1a30Sjl 224425cf1a30Sjl pa0 = pa - mcp->mc_start_address; 224525cf1a30Sjl 224625cf1a30Sjl bank = pa_to_bank(mcp, pa0); 224725cf1a30Sjl 224825cf1a30Sjl if (flags & MC_INJECT_FLAG_OTHER) 224925cf1a30Sjl bank = bank ^ 1; 225025cf1a30Sjl 225125cf1a30Sjl if (MC_INJECT_MIRROR(error_type) && !IS_MIRROR(mcp, bank)) { 225225cf1a30Sjl mutex_exit(&mcp->mc_lock); 225325cf1a30Sjl MC_LOG("Not mirror mode\n"); 225425cf1a30Sjl return (EINVAL); 225525cf1a30Sjl } 225625cf1a30Sjl 225725cf1a30Sjl dimm_addr = pa_to_dimm(mcp, pa0); 225825cf1a30Sjl 225925cf1a30Sjl MC_LOG("injecting error to /LSB%d/B%d/D%x\n", 226025cf1a30Sjl mcp->mc_board_num, bank, dimm_addr); 226125cf1a30Sjl 226225cf1a30Sjl 226325cf1a30Sjl switch (error_type) { 226425cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 226525cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 226625cf1a30Sjl case MC_INJECT_MUE: 226725cf1a30Sjl both_sides = 1; 226825cf1a30Sjl } 226925cf1a30Sjl 227025cf1a30Sjl if (flags & MC_INJECT_FLAG_RESET) 227125cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), 0); 227225cf1a30Sjl 227325cf1a30Sjl ST_MAC_REG(MAC_EG_ADD(mcp, bank), dimm_addr & MAC_EG_ADD_MASK); 227425cf1a30Sjl 227525cf1a30Sjl if (both_sides) { 227625cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), 0); 227725cf1a30Sjl ST_MAC_REG(MAC_EG_ADD(mcp, bank^1), 227825cf1a30Sjl dimm_addr & MAC_EG_ADD_MASK); 227925cf1a30Sjl } 228025cf1a30Sjl 228125cf1a30Sjl switch (error_type) { 228225cf1a30Sjl case MC_INJECT_UE: 228325cf1a30Sjl case MC_INJECT_SUE: 228425cf1a30Sjl case MC_INJECT_MUE: 228525cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 228625cf1a30Sjl cntl = MAC_EG_ADD_FIX 228725cf1a30Sjl |MAC_EG_FORCE_READ00|MAC_EG_FORCE_READ16 228825cf1a30Sjl |MAC_EG_DERR_ONCE; 228925cf1a30Sjl } else { 229025cf1a30Sjl cntl = MAC_EG_ADD_FIX|MAC_EG_FORCE_DERR00 229125cf1a30Sjl |MAC_EG_FORCE_DERR16|MAC_EG_DERR_ONCE; 229225cf1a30Sjl } 229325cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 229425cf1a30Sjl break; 229525cf1a30Sjl case MC_INJECT_INTERMITTENT_CE: 229625cf1a30Sjl case MC_INJECT_INTERMITTENT_MCE: 229725cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 229825cf1a30Sjl cntl = MAC_EG_ADD_FIX 229925cf1a30Sjl |MAC_EG_FORCE_READ00 230025cf1a30Sjl |MAC_EG_DERR_ONCE; 230125cf1a30Sjl } else { 230225cf1a30Sjl cntl = MAC_EG_ADD_FIX 230325cf1a30Sjl |MAC_EG_FORCE_DERR16 230425cf1a30Sjl |MAC_EG_DERR_ONCE; 230525cf1a30Sjl } 230625cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 230725cf1a30Sjl break; 230825cf1a30Sjl case MC_INJECT_PERMANENT_CE: 230925cf1a30Sjl case MC_INJECT_PERMANENT_MCE: 231025cf1a30Sjl if (flags & MC_INJECT_FLAG_PATH) { 231125cf1a30Sjl cntl = MAC_EG_ADD_FIX 231225cf1a30Sjl |MAC_EG_FORCE_READ00 231325cf1a30Sjl |MAC_EG_DERR_ALWAYS; 231425cf1a30Sjl } else { 231525cf1a30Sjl cntl = MAC_EG_ADD_FIX 231625cf1a30Sjl |MAC_EG_FORCE_DERR16 231725cf1a30Sjl |MAC_EG_DERR_ALWAYS; 231825cf1a30Sjl } 231925cf1a30Sjl flags |= MC_INJECT_FLAG_ST; 232025cf1a30Sjl break; 232125cf1a30Sjl case MC_INJECT_CMPE: 232225cf1a30Sjl data = 0xabcdefab; 232325cf1a30Sjl stphys(pa, data); 232425cf1a30Sjl cpu_flush_ecache(); 232525cf1a30Sjl MC_LOG("CMPE: writing data %x to %lx\n", data, pa); 232625cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), MAC_MIRR_BANK_EXCLUSIVE); 232725cf1a30Sjl stphys(pa, data ^ 0xffffffff); 232825cf1a30Sjl cpu_flush_ecache(); 232925cf1a30Sjl ST_MAC_REG(MAC_MIRR(mcp, bank), 0); 233025cf1a30Sjl MC_LOG("CMPE: write new data %xto %lx\n", data, pa); 233125cf1a30Sjl cntl = 0; 233225cf1a30Sjl break; 233325cf1a30Sjl case MC_INJECT_NOP: 233425cf1a30Sjl cntl = 0; 233525cf1a30Sjl break; 233625cf1a30Sjl default: 233725cf1a30Sjl MC_LOG("mc_inject_error: invalid option\n"); 233825cf1a30Sjl cntl = 0; 233925cf1a30Sjl } 234025cf1a30Sjl 234125cf1a30Sjl if (cntl) { 234225cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl & MAC_EG_SETUP_MASK); 234325cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank), cntl); 234425cf1a30Sjl 234525cf1a30Sjl if (both_sides) { 234625cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl & 234725cf1a30Sjl MAC_EG_SETUP_MASK); 234825cf1a30Sjl ST_MAC_REG(MAC_EG_CNTL(mcp, bank^1), cntl); 234925cf1a30Sjl } 235025cf1a30Sjl } 235125cf1a30Sjl 235225cf1a30Sjl /* 235325cf1a30Sjl * For all injection cases except compare error, we 235425cf1a30Sjl * must write to the PA to trigger the error. 235525cf1a30Sjl */ 235625cf1a30Sjl 235725cf1a30Sjl if (flags & MC_INJECT_FLAG_ST) { 235825cf1a30Sjl data = 0xf0e0d0c0; 235925cf1a30Sjl MC_LOG("Writing %x to %lx\n", data, pa); 236025cf1a30Sjl stphys(pa, data); 236125cf1a30Sjl cpu_flush_ecache(); 236225cf1a30Sjl } 236325cf1a30Sjl 236425cf1a30Sjl delay(inject_op_delay * drv_usectohz(1000 * 1000)); 236525cf1a30Sjl 236625cf1a30Sjl 236725cf1a30Sjl if (flags & MC_INJECT_FLAG_LD) { 236825cf1a30Sjl if (flags & MC_INJECT_FLAG_NO_TRAP) { 236925cf1a30Sjl if (on_trap(&otd, OT_DATA_EC)) { 237025cf1a30Sjl no_trap(); 237125cf1a30Sjl MC_LOG("Trap occurred\n"); 237225cf1a30Sjl } else { 237325cf1a30Sjl MC_LOG("On-trap Reading from %lx\n", pa); 237425cf1a30Sjl data = ldphys(pa); 237525cf1a30Sjl no_trap(); 237625cf1a30Sjl MC_LOG("data = %x\n", data); 237725cf1a30Sjl } 237825cf1a30Sjl } else { 237925cf1a30Sjl MC_LOG("Reading from %lx\n", pa); 238025cf1a30Sjl data = ldphys(pa); 238125cf1a30Sjl MC_LOG("data = %x\n", data); 238225cf1a30Sjl } 238325cf1a30Sjl } 238425cf1a30Sjl 238525cf1a30Sjl if (flags & MC_INJECT_FLAG_RESTART) { 238625cf1a30Sjl delay(inject_op_delay * drv_usectohz(1000 * 1000)); 238725cf1a30Sjl 238825cf1a30Sjl MC_LOG("Restart patrol\n"); 238925cf1a30Sjl if (mc_stop(mcp, bank)) { 239025cf1a30Sjl cmn_err(CE_WARN, "Cannot stop Memory Patrol at " 239125cf1a30Sjl "/LSB%d/B%d\n", mcp->mc_board_num, bank); 239225cf1a30Sjl mutex_exit(&mcp->mc_lock); 239325cf1a30Sjl return (EIO); 239425cf1a30Sjl } 239525cf1a30Sjl maddr.mi_maddr.ma_bd = mcp->mc_board_num; 239625cf1a30Sjl maddr.mi_maddr.ma_bank = bank; 239725cf1a30Sjl maddr.mi_maddr.ma_dimm_addr = dimm_addr; 239825cf1a30Sjl maddr.mi_valid = 1; 239925cf1a30Sjl maddr.mi_advance = 0; 240025cf1a30Sjl restart_patrol(mcp, bank, &maddr); 240125cf1a30Sjl } 240225cf1a30Sjl 240325cf1a30Sjl if (flags & MC_INJECT_FLAG_POLL) { 240425cf1a30Sjl delay(inject_op_delay * drv_usectohz(1000 * 1000)); 240525cf1a30Sjl 240625cf1a30Sjl MC_LOG("Poll patrol error\n"); 240725cf1a30Sjl stat = LD_MAC_REG(MAC_PTRL_STAT(mcp, bank)); 240825cf1a30Sjl cntl = LD_MAC_REG(MAC_PTRL_CNTL(mcp, bank)); 240925cf1a30Sjl if (stat & (MAC_STAT_PTRL_ERRS|MAC_STAT_MI_ERRS)) { 241025cf1a30Sjl maddr.mi_valid = 0; 241125cf1a30Sjl maddr.mi_advance = 1; 241225cf1a30Sjl if (IS_MIRROR(mcp, bank)) 241325cf1a30Sjl mc_error_handler_mir(mcp, bank, 241425cf1a30Sjl &maddr); 241525cf1a30Sjl else 241625cf1a30Sjl mc_error_handler(mcp, bank, &maddr); 241725cf1a30Sjl 241825cf1a30Sjl restart_patrol(mcp, bank, &maddr); 241925cf1a30Sjl } else 242025cf1a30Sjl restart_patrol(mcp, bank, NULL); 242125cf1a30Sjl } 242225cf1a30Sjl 242325cf1a30Sjl mutex_exit(&mcp->mc_lock); 242425cf1a30Sjl return (0); 242525cf1a30Sjl } 242625cf1a30Sjl 242725cf1a30Sjl void 242825cf1a30Sjl mc_stphysio(uint64_t pa, uint32_t data) 242925cf1a30Sjl { 243025cf1a30Sjl MC_LOG("0x%x -> pa(%lx)\n", data, pa); 243125cf1a30Sjl stphysio(pa, data); 243225cf1a30Sjl } 243325cf1a30Sjl 243425cf1a30Sjl uint32_t 243525cf1a30Sjl mc_ldphysio(uint64_t pa) 243625cf1a30Sjl { 243725cf1a30Sjl uint32_t rv; 243825cf1a30Sjl 243925cf1a30Sjl rv = ldphysio(pa); 244025cf1a30Sjl MC_LOG("pa(%lx) = 0x%x\n", pa, rv); 244125cf1a30Sjl return (rv); 244225cf1a30Sjl } 2443