103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 525cf1a30Sjl139090 * Common Development and Distribution License (the "License"). 625cf1a30Sjl139090 * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2107d06da5SSurya Prakki 2203831d35Sstevel /* 2356f33205SJonathan Adams * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel #include <sys/note.h> 2803831d35Sstevel #include <sys/debug.h> 2903831d35Sstevel #include <sys/types.h> 3003831d35Sstevel #include <sys/varargs.h> 3103831d35Sstevel #include <sys/errno.h> 3203831d35Sstevel #include <sys/cred.h> 3303831d35Sstevel #include <sys/dditypes.h> 3403831d35Sstevel #include <sys/devops.h> 3503831d35Sstevel #include <sys/modctl.h> 3603831d35Sstevel #include <sys/poll.h> 3703831d35Sstevel #include <sys/conf.h> 3803831d35Sstevel #include <sys/ddi.h> 3903831d35Sstevel #include <sys/sunddi.h> 4003831d35Sstevel #include <sys/sunndi.h> 4103831d35Sstevel #include <sys/ndi_impldefs.h> 4203831d35Sstevel #include <sys/stat.h> 4303831d35Sstevel #include <sys/kmem.h> 4403831d35Sstevel #include <sys/vmem.h> 4503831d35Sstevel #include <sys/disp.h> 4603831d35Sstevel #include <sys/processor.h> 4703831d35Sstevel #include <sys/cheetahregs.h> 4803831d35Sstevel #include <sys/cpuvar.h> 4903831d35Sstevel #include <sys/mem_config.h> 5003831d35Sstevel #include <sys/ddi_impldefs.h> 5103831d35Sstevel #include <sys/systm.h> 5203831d35Sstevel #include <sys/machsystm.h> 5303831d35Sstevel #include <sys/autoconf.h> 5403831d35Sstevel #include <sys/cmn_err.h> 5503831d35Sstevel #include <sys/sysmacros.h> 5603831d35Sstevel #include <sys/x_call.h> 5703831d35Sstevel #include <sys/promif.h> 5803831d35Sstevel #include <sys/prom_plat.h> 5903831d35Sstevel #include <sys/membar.h> 6003831d35Sstevel #include <vm/seg_kmem.h> 6103831d35Sstevel #include <sys/mem_cage.h> 6203831d35Sstevel #include <sys/stack.h> 6303831d35Sstevel #include <sys/archsystm.h> 6403831d35Sstevel #include <vm/hat_sfmmu.h> 6503831d35Sstevel #include <sys/pte.h> 6603831d35Sstevel #include <sys/mmu.h> 6703831d35Sstevel #include <sys/cpu_module.h> 6803831d35Sstevel #include <sys/obpdefs.h> 6903831d35Sstevel #include <sys/mboxsc.h> 7003831d35Sstevel #include <sys/plat_ecc_dimm.h> 7103831d35Sstevel 7203831d35Sstevel #include <sys/hotplug/hpctrl.h> /* XXX should be included by schpc.h */ 7303831d35Sstevel #include <sys/schpc.h> 7403831d35Sstevel #include <sys/pci.h> 7503831d35Sstevel 7603831d35Sstevel #include <sys/starcat.h> 7703831d35Sstevel #include <sys/cpu_sgnblk_defs.h> 7803831d35Sstevel #include <sys/drmach.h> 7903831d35Sstevel #include <sys/dr_util.h> 8003831d35Sstevel #include <sys/dr_mbx.h> 8103831d35Sstevel #include <sys/sc_gptwocfg.h> 8203831d35Sstevel #include <sys/iosramreg.h> 8303831d35Sstevel #include <sys/iosramio.h> 8403831d35Sstevel #include <sys/iosramvar.h> 8503831d35Sstevel #include <sys/axq.h> 8603831d35Sstevel #include <sys/post/scat_dcd.h> 8703831d35Sstevel #include <sys/kobj.h> 8803831d35Sstevel #include <sys/taskq.h> 8903831d35Sstevel #include <sys/cmp.h> 9003831d35Sstevel #include <sys/sbd_ioctl.h> 9103831d35Sstevel 9203831d35Sstevel #include <sys/sysevent.h> 9303831d35Sstevel #include <sys/sysevent/dr.h> 9403831d35Sstevel #include <sys/sysevent/eventdefs.h> 9503831d35Sstevel 9603831d35Sstevel #include <sys/pci/pcisch.h> 9703831d35Sstevel #include <sys/pci/pci_regs.h> 9803831d35Sstevel 9903831d35Sstevel #include <sys/ontrap.h> 10003831d35Sstevel 10103831d35Sstevel /* defined in ../ml/drmach.il.cpp */ 10203831d35Sstevel extern void bcopy32_il(uint64_t, uint64_t); 10303831d35Sstevel extern void flush_ecache_il(int64_t physaddr, int size, int linesz); 10403831d35Sstevel extern void flush_dcache_il(void); 10503831d35Sstevel extern void flush_icache_il(void); 10603831d35Sstevel extern void flush_pcache_il(void); 10703831d35Sstevel 10803831d35Sstevel /* defined in ../ml/drmach_asm.s */ 10903831d35Sstevel extern uint64_t lddmcdecode(uint64_t physaddr); 11003831d35Sstevel extern uint64_t lddsafconfig(void); 11103831d35Sstevel 11203831d35Sstevel /* XXX here until provided by sys/dman.h */ 11303831d35Sstevel extern int man_dr_attach(dev_info_t *); 11403831d35Sstevel extern int man_dr_detach(dev_info_t *); 11503831d35Sstevel 11603831d35Sstevel #define DRMACH_BNUM2EXP(bnum) ((bnum) >> 1) 11703831d35Sstevel #define DRMACH_BNUM2SLOT(bnum) ((bnum) & 1) 11803831d35Sstevel #define DRMACH_EXPSLOT2BNUM(exp, slot) (((exp) << 1) + (slot)) 11903831d35Sstevel 12003831d35Sstevel #define DRMACH_SLICE_MASK 0x1Full 12103831d35Sstevel #define DRMACH_SLICE_TO_PA(s) (((s) & DRMACH_SLICE_MASK) << 37) 12203831d35Sstevel #define DRMACH_PA_TO_SLICE(a) (((a) >> 37) & DRMACH_SLICE_MASK) 12303831d35Sstevel 12403831d35Sstevel /* 12503831d35Sstevel * DRMACH_MEM_SLICE_SIZE and DRMACH_MEM_USABLE_SLICE_SIZE define the 12603831d35Sstevel * available address space and the usable address space for every slice. 12703831d35Sstevel * There must be a distinction between the available and usable do to a 12803831d35Sstevel * restriction imposed by CDC memory size. 12903831d35Sstevel */ 13003831d35Sstevel 13103831d35Sstevel #define DRMACH_MEM_SLICE_SIZE (1ull << 37) /* 128GB */ 13203831d35Sstevel #define DRMACH_MEM_USABLE_SLICE_SIZE (1ull << 36) /* 64GB */ 13303831d35Sstevel 13403831d35Sstevel #define DRMACH_MC_NBANKS 4 13503831d35Sstevel 13603831d35Sstevel #define DRMACH_MC_ADDR(mp, bank) ((mp)->madr_pa + 16 + 8 * (bank)) 13703831d35Sstevel #define DRMACH_MC_ASI_ADDR(mp, bank) (DRMACH_MC_ADDR(mp, bank) & 0xFF) 13803831d35Sstevel 13903831d35Sstevel #define DRMACH_EMU_ACT_STATUS_OFFSET 0x50 14003831d35Sstevel #define DRMACH_EMU_ACT_STATUS_ADDR(mp) \ 14103831d35Sstevel ((mp)->madr_pa + DRMACH_EMU_ACT_STATUS_OFFSET) 14203831d35Sstevel 14303831d35Sstevel /* 14403831d35Sstevel * The Cheetah's Safari Configuration Register and the Schizo's 14503831d35Sstevel * Safari Control/Status Register place the LPA base and bound fields in 14603831d35Sstevel * same bit locations with in their register word. This source code takes 14703831d35Sstevel * advantage of this by defining only one set of LPA encoding/decoding macros 14803831d35Sstevel * which are shared by various Cheetah and Schizo drmach routines. 14903831d35Sstevel */ 15003831d35Sstevel #define DRMACH_LPA_BASE_MASK (0x3Full << 3) 15103831d35Sstevel #define DRMACH_LPA_BND_MASK (0x3Full << 9) 15203831d35Sstevel 15303831d35Sstevel #define DRMACH_LPA_BASE_TO_PA(scr) (((scr) & DRMACH_LPA_BASE_MASK) << 34) 15403831d35Sstevel #define DRMACH_LPA_BND_TO_PA(scr) (((scr) & DRMACH_LPA_BND_MASK) << 28) 15503831d35Sstevel #define DRMACH_PA_TO_LPA_BASE(pa) (((pa) >> 34) & DRMACH_LPA_BASE_MASK) 15603831d35Sstevel #define DRMACH_PA_TO_LPA_BND(pa) (((pa) >> 28) & DRMACH_LPA_BND_MASK) 15703831d35Sstevel 15803831d35Sstevel #define DRMACH_L1_SET_LPA(b) \ 15903831d35Sstevel (((b)->flags & DRMACH_NULL_PROC_LPA) == 0) 16003831d35Sstevel 16103831d35Sstevel #define DRMACH_CPU_SRAM_ADDR 0x7fff0900000ull 16203831d35Sstevel #define DRMACH_CPU_SRAM_SIZE 0x20000ull 16303831d35Sstevel 16403831d35Sstevel /* 16503831d35Sstevel * Name properties for frequently accessed device nodes. 16603831d35Sstevel */ 16703831d35Sstevel #define DRMACH_CPU_NAMEPROP "cpu" 16803831d35Sstevel #define DRMACH_CMP_NAMEPROP "cmp" 16903831d35Sstevel #define DRMACH_AXQ_NAMEPROP "address-extender-queue" 17003831d35Sstevel #define DRMACH_PCI_NAMEPROP "pci" 17103831d35Sstevel 17203831d35Sstevel /* 17303831d35Sstevel * Maximum value of processor Safari Timeout Log (TOL) field of 17403831d35Sstevel * Safari Config reg (7 secs). 17503831d35Sstevel */ 17603831d35Sstevel #define DRMACH_SAF_TOL_MAX 7 * 1000000 17703831d35Sstevel 17803831d35Sstevel /* 17903831d35Sstevel * drmach_board_t flag definitions 18003831d35Sstevel */ 18103831d35Sstevel #define DRMACH_NULL_PROC_LPA 0x1 18203831d35Sstevel 18303831d35Sstevel typedef struct { 18403831d35Sstevel uint32_t reg_addr_hi; 18503831d35Sstevel uint32_t reg_addr_lo; 18603831d35Sstevel uint32_t reg_size_hi; 18703831d35Sstevel uint32_t reg_size_lo; 18803831d35Sstevel } drmach_reg_t; 18903831d35Sstevel 19003831d35Sstevel typedef struct { 19103831d35Sstevel struct drmach_node *node; 19203831d35Sstevel void *data; 19303831d35Sstevel } drmach_node_walk_args_t; 19403831d35Sstevel 19503831d35Sstevel typedef struct drmach_node { 19603831d35Sstevel void *here; 19703831d35Sstevel 19803831d35Sstevel pnode_t (*get_dnode)(struct drmach_node *node); 19903831d35Sstevel int (*walk)(struct drmach_node *node, void *data, 20003831d35Sstevel int (*cb)(drmach_node_walk_args_t *args)); 20103831d35Sstevel dev_info_t *(*n_getdip)(struct drmach_node *node); 20203831d35Sstevel int (*n_getproplen)(struct drmach_node *node, char *name, 20303831d35Sstevel int *len); 20403831d35Sstevel int (*n_getprop)(struct drmach_node *node, char *name, 20503831d35Sstevel void *buf, int len); 20603831d35Sstevel int (*get_parent)(struct drmach_node *node, 20703831d35Sstevel struct drmach_node *pnode); 20803831d35Sstevel } drmach_node_t; 20903831d35Sstevel 21003831d35Sstevel typedef struct { 21103831d35Sstevel int min_index; 21203831d35Sstevel int max_index; 21303831d35Sstevel int arr_sz; 21403831d35Sstevel drmachid_t *arr; 21503831d35Sstevel } drmach_array_t; 21603831d35Sstevel 21703831d35Sstevel typedef struct { 21803831d35Sstevel void *isa; 21903831d35Sstevel 22003831d35Sstevel void (*dispose)(drmachid_t); 22103831d35Sstevel sbd_error_t *(*release)(drmachid_t); 22203831d35Sstevel sbd_error_t *(*status)(drmachid_t, drmach_status_t *); 22303831d35Sstevel 22403831d35Sstevel char name[MAXNAMELEN]; 22503831d35Sstevel } drmach_common_t; 22603831d35Sstevel 22703831d35Sstevel struct drmach_board; 22803831d35Sstevel typedef struct drmach_board drmach_board_t; 22903831d35Sstevel 23003831d35Sstevel typedef struct { 23103831d35Sstevel drmach_common_t cm; 23203831d35Sstevel const char *type; 23303831d35Sstevel drmach_board_t *bp; 23403831d35Sstevel drmach_node_t *node; 23503831d35Sstevel int portid; 23603831d35Sstevel int unum; 23703831d35Sstevel int busy; 23803831d35Sstevel int powered; 23903831d35Sstevel } drmach_device_t; 24003831d35Sstevel 24103831d35Sstevel typedef struct drmach_cpu { 24203831d35Sstevel drmach_device_t dev; 24303831d35Sstevel uint64_t scr_pa; 24403831d35Sstevel processorid_t cpuid; 24503831d35Sstevel int coreid; 24603831d35Sstevel } drmach_cpu_t; 24703831d35Sstevel 24803831d35Sstevel typedef struct drmach_mem { 24903831d35Sstevel drmach_device_t dev; 25003831d35Sstevel struct drmach_mem *next; 25103831d35Sstevel uint64_t nbytes; 25203831d35Sstevel uint64_t madr_pa; 25303831d35Sstevel } drmach_mem_t; 25403831d35Sstevel 25503831d35Sstevel typedef struct drmach_io { 25603831d35Sstevel drmach_device_t dev; 25703831d35Sstevel uint64_t scsr_pa; /* PA of Schizo Control/Status Register */ 25803831d35Sstevel } drmach_io_t; 25903831d35Sstevel 26003831d35Sstevel struct drmach_board { 26103831d35Sstevel drmach_common_t cm; 26203831d35Sstevel int bnum; 26303831d35Sstevel int assigned; 26403831d35Sstevel int powered; 26503831d35Sstevel int connected; 26603831d35Sstevel int empty; 26703831d35Sstevel int cond; 26803831d35Sstevel uint_t cpu_impl; 26903831d35Sstevel uint_t flags; 27003831d35Sstevel drmach_node_t *tree; 27103831d35Sstevel drmach_array_t *devices; 27203831d35Sstevel drmach_mem_t *mem; 27303831d35Sstevel uint64_t stardrb_offset; 27403831d35Sstevel char type[BD_TYPELEN]; 27503831d35Sstevel }; 27603831d35Sstevel 27703831d35Sstevel typedef struct { 27803831d35Sstevel int flags; 27903831d35Sstevel drmach_device_t *dp; 28003831d35Sstevel sbd_error_t *err; 28103831d35Sstevel dev_info_t *fdip; 28203831d35Sstevel } drmach_config_args_t; 28303831d35Sstevel 28403831d35Sstevel typedef struct { 28503831d35Sstevel drmach_board_t *obj; 28603831d35Sstevel int ndevs; 28703831d35Sstevel void *a; 28803831d35Sstevel sbd_error_t *(*found)(void *a, const char *, int, drmachid_t); 28903831d35Sstevel sbd_error_t *err; 29003831d35Sstevel } drmach_board_cb_data_t; 29103831d35Sstevel 29203831d35Sstevel typedef struct drmach_casmslot { 29303831d35Sstevel int valid; 29403831d35Sstevel int slice; 29503831d35Sstevel } drmach_casmslot_t; 29603831d35Sstevel 29703831d35Sstevel typedef enum { 29803831d35Sstevel DRMACH_CR_OK, 29903831d35Sstevel DRMACH_CR_MC_IDLE_ERR, 30003831d35Sstevel DRMACH_CR_IOPAUSE_ERR, 30103831d35Sstevel DRMACH_CR_ONTRAP_ERR 30203831d35Sstevel } drmach_cr_err_t; 30303831d35Sstevel 30403831d35Sstevel typedef struct { 30503831d35Sstevel void *isa; 30603831d35Sstevel caddr_t data; 30703831d35Sstevel drmach_mem_t *s_mp; 30803831d35Sstevel drmach_mem_t *t_mp; 30903831d35Sstevel struct memlist *c_ml; 31003831d35Sstevel uint64_t s_copybasepa; 31103831d35Sstevel uint64_t t_copybasepa; 31203831d35Sstevel drmach_cr_err_t ecode; 31303831d35Sstevel void *earg; 31403831d35Sstevel } drmach_copy_rename_t; 31503831d35Sstevel 31603831d35Sstevel /* 31703831d35Sstevel * The following global is read as a boolean value, non-zero is true. 31803831d35Sstevel * If zero, DR copy-rename and cpu poweron will not set the processor 31903831d35Sstevel * LPA settings (CBASE, CBND of Safari config register) to correspond 32003831d35Sstevel * to the current memory slice map. LPAs of processors present at boot 32103831d35Sstevel * will remain as programmed by POST. LPAs of processors on boards added 32203831d35Sstevel * by DR will remain NULL, as programmed by POST. This can be used to 32303831d35Sstevel * to override the per-board L1SSFLG_THIS_L1_NULL_PROC_LPA flag set by 32403831d35Sstevel * POST in the LDCD (and copied to the GDCD by SMS). 32503831d35Sstevel * 32603831d35Sstevel * drmach_reprogram_lpa and L1SSFLG_THIS_L1_NULL_PROC_LPA do not apply 32703831d35Sstevel * to Schizo device LPAs. These are always set by DR. 32803831d35Sstevel */ 32903831d35Sstevel static int drmach_reprogram_lpa = 1; 33003831d35Sstevel 33103831d35Sstevel /* 33203831d35Sstevel * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0) 33303831d35Sstevel * can fail to receive an XIR. To workaround this issue until a hardware 33403831d35Sstevel * fix is implemented, we will exclude the selection of these CPUs. 33503831d35Sstevel * Setting this to 0 will allow their selection again. 33603831d35Sstevel */ 33703831d35Sstevel static int drmach_iocage_exclude_jaguar_port_zero = 1; 33803831d35Sstevel 33903831d35Sstevel static int drmach_initialized; 34003831d35Sstevel static drmach_array_t *drmach_boards; 34103831d35Sstevel 34203831d35Sstevel static int drmach_cpu_delay = 1000; 34303831d35Sstevel static int drmach_cpu_ntries = 50000; 34403831d35Sstevel 34503831d35Sstevel static uint32_t drmach_slice_table[AXQ_MAX_EXP]; 34603831d35Sstevel static kmutex_t drmach_slice_table_lock; 34703831d35Sstevel 34803831d35Sstevel tte_t drmach_cpu_sram_tte[NCPU]; 34903831d35Sstevel caddr_t drmach_cpu_sram_va; 35003831d35Sstevel 35103831d35Sstevel /* 35203831d35Sstevel * Setting to non-zero will enable delay before all disconnect ops. 35303831d35Sstevel */ 35403831d35Sstevel static int drmach_unclaim_delay_all; 35503831d35Sstevel /* 35603831d35Sstevel * Default delay is slightly greater than the max processor Safari timeout. 35703831d35Sstevel * This delay is intended to ensure the outstanding Safari activity has 35803831d35Sstevel * retired on this board prior to a board disconnect. 35903831d35Sstevel */ 36003831d35Sstevel static clock_t drmach_unclaim_usec_delay = DRMACH_SAF_TOL_MAX + 10; 36103831d35Sstevel 36203831d35Sstevel /* 36303831d35Sstevel * By default, DR of non-Panther procs is not allowed into a Panther 36403831d35Sstevel * domain with large page sizes enabled. Setting this to 0 will remove 36503831d35Sstevel * the restriction. 36603831d35Sstevel */ 36703831d35Sstevel static int drmach_large_page_restriction = 1; 36803831d35Sstevel 36903831d35Sstevel /* 37003831d35Sstevel * Used to pass updated LPA values to procs. 37103831d35Sstevel * Protocol is to clear the array before use. 37203831d35Sstevel */ 37303831d35Sstevel volatile uchar_t *drmach_xt_mb; 37403831d35Sstevel volatile uint64_t drmach_xt_ready; 37503831d35Sstevel static kmutex_t drmach_xt_mb_lock; 37603831d35Sstevel static int drmach_xt_mb_size; 37703831d35Sstevel 37803831d35Sstevel uint64_t drmach_bus_sync_list[18 * 4 * 4 + 1]; 37903831d35Sstevel static kmutex_t drmach_bus_sync_lock; 38003831d35Sstevel 381*8682d1efSRichard Lowe static void drmach_fini(void); 382*8682d1efSRichard Lowe 38303831d35Sstevel static sbd_error_t *drmach_device_new(drmach_node_t *, 38403831d35Sstevel drmach_board_t *, int, drmachid_t *); 38503831d35Sstevel static sbd_error_t *drmach_cpu_new(drmach_device_t *, drmachid_t *); 38603831d35Sstevel static sbd_error_t *drmach_mem_new(drmach_device_t *, drmachid_t *); 38703831d35Sstevel static sbd_error_t *drmach_pci_new(drmach_device_t *, drmachid_t *); 38803831d35Sstevel static sbd_error_t *drmach_io_new(drmach_device_t *, drmachid_t *); 38903831d35Sstevel 390*8682d1efSRichard Lowe static sbd_error_t *drmach_board_release(drmachid_t); 391*8682d1efSRichard Lowe static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *); 392*8682d1efSRichard Lowe 393*8682d1efSRichard Lowe static void drmach_cpu_dispose(drmachid_t); 394*8682d1efSRichard Lowe static sbd_error_t *drmach_cpu_release(drmachid_t); 395*8682d1efSRichard Lowe static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *); 396*8682d1efSRichard Lowe 397*8682d1efSRichard Lowe static void drmach_mem_dispose(drmachid_t); 398*8682d1efSRichard Lowe static sbd_error_t *drmach_mem_release(drmachid_t); 399*8682d1efSRichard Lowe static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *); 400*8682d1efSRichard Lowe 40103831d35Sstevel static dev_info_t *drmach_node_ddi_get_dip(drmach_node_t *np); 40203831d35Sstevel static int drmach_node_ddi_get_prop(drmach_node_t *np, 40303831d35Sstevel char *name, void *buf, int len); 40403831d35Sstevel static int drmach_node_ddi_get_proplen(drmach_node_t *np, 40503831d35Sstevel char *name, int *len); 40603831d35Sstevel 40703831d35Sstevel static dev_info_t *drmach_node_obp_get_dip(drmach_node_t *np); 40803831d35Sstevel static int drmach_node_obp_get_prop(drmach_node_t *np, 40903831d35Sstevel char *name, void *buf, int len); 41003831d35Sstevel static int drmach_node_obp_get_proplen(drmach_node_t *np, 41103831d35Sstevel char *name, int *len); 41203831d35Sstevel 41303831d35Sstevel static sbd_error_t *drmach_mbox_trans(uint8_t msgtype, int bnum, 41403831d35Sstevel caddr_t obufp, int olen, 41503831d35Sstevel caddr_t ibufp, int ilen); 41603831d35Sstevel 41703831d35Sstevel sbd_error_t *drmach_io_post_attach(drmachid_t id); 41803831d35Sstevel sbd_error_t *drmach_io_post_release(drmachid_t id); 41903831d35Sstevel 42003831d35Sstevel static sbd_error_t *drmach_iocage_setup(dr_testboard_req_t *, 42103831d35Sstevel drmach_device_t **dpp, cpu_flag_t *oflags); 42203831d35Sstevel static int drmach_iocage_cpu_return(drmach_device_t *dp, 42303831d35Sstevel cpu_flag_t oflags); 42403831d35Sstevel static sbd_error_t *drmach_iocage_mem_return(dr_testboard_reply_t *tbr); 42503831d35Sstevel void drmach_iocage_mem_scrub(uint64_t nbytes); 42603831d35Sstevel 42703831d35Sstevel static sbd_error_t *drmach_i_status(drmachid_t id, drmach_status_t *stat); 42803831d35Sstevel 42903831d35Sstevel static void drmach_slot1_lpa_set(drmach_board_t *bp); 43003831d35Sstevel 43103831d35Sstevel static void drmach_cpu_read(uint64_t arg1, uint64_t arg2); 43203831d35Sstevel static int drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr); 43303831d35Sstevel 43403831d35Sstevel static void drmach_bus_sync_list_update(void); 43503831d35Sstevel static void drmach_slice_table_update(drmach_board_t *, int); 43603831d35Sstevel static int drmach_portid2bnum(int); 43703831d35Sstevel 43803831d35Sstevel static void drmach_msg_memslice_init(dr_memslice_t slice_arr[]); 43903831d35Sstevel static void drmach_msg_memregs_init(dr_memregs_t regs_arr[]); 44003831d35Sstevel 44103831d35Sstevel static int drmach_panther_boards(void); 44203831d35Sstevel 44303831d35Sstevel static int drmach_name2type_idx(char *); 44403831d35Sstevel 44503831d35Sstevel #ifdef DEBUG 44603831d35Sstevel 44703831d35Sstevel #define DRMACH_PR if (drmach_debug) printf 44803831d35Sstevel #define DRMACH_MEMLIST_DUMP if (drmach_debug) MEMLIST_DUMP 44903831d35Sstevel int drmach_debug = 0; /* set to non-zero to enable debug messages */ 45003831d35Sstevel #else 45103831d35Sstevel 45203831d35Sstevel #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf 45303831d35Sstevel #define DRMACH_MEMLIST_DUMP _NOTE(CONSTANTCONDITION) if (0) MEMLIST_DUMP 45403831d35Sstevel #endif /* DEBUG */ 45503831d35Sstevel 45603831d35Sstevel #define DRMACH_OBJ(id) ((drmach_common_t *)id) 45703831d35Sstevel 45803831d35Sstevel #define DRMACH_IS_BOARD_ID(id) \ 45903831d35Sstevel ((id != 0) && \ 46003831d35Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_board_new)) 46103831d35Sstevel 46203831d35Sstevel #define DRMACH_IS_CPU_ID(id) \ 46303831d35Sstevel ((id != 0) && \ 46403831d35Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new)) 46503831d35Sstevel 46603831d35Sstevel #define DRMACH_IS_MEM_ID(id) \ 46703831d35Sstevel ((id != 0) && \ 46803831d35Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new)) 46903831d35Sstevel 47003831d35Sstevel #define DRMACH_IS_IO_ID(id) \ 47103831d35Sstevel ((id != 0) && \ 47203831d35Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 47303831d35Sstevel 47403831d35Sstevel #define DRMACH_IS_DEVICE_ID(id) \ 47503831d35Sstevel ((id != 0) && \ 47603831d35Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 47703831d35Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 47803831d35Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 47903831d35Sstevel 48003831d35Sstevel #define DRMACH_IS_ID(id) \ 48103831d35Sstevel ((id != 0) && \ 48203831d35Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \ 48303831d35Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \ 48403831d35Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \ 48503831d35Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_io_new)) 48603831d35Sstevel 48703831d35Sstevel #define DRMACH_INTERNAL_ERROR() \ 48803831d35Sstevel drerr_new(1, ESTC_INTERNAL, drmach_ie_fmt, __LINE__) 48903831d35Sstevel static char *drmach_ie_fmt = "drmach.c %d"; 49003831d35Sstevel 49103831d35Sstevel static struct { 49203831d35Sstevel const char *name; 49303831d35Sstevel const char *type; 49403831d35Sstevel sbd_error_t *(*new)(drmach_device_t *, drmachid_t *); 49503831d35Sstevel } drmach_name2type[] = { 49603831d35Sstevel {"cmp", DRMACH_DEVTYPE_CMP, NULL }, 49703831d35Sstevel {"cpu", DRMACH_DEVTYPE_CPU, drmach_cpu_new }, 49803831d35Sstevel {"SUNW,UltraSPARC-III", DRMACH_DEVTYPE_CPU, drmach_cpu_new }, 49903831d35Sstevel {"SUNW,UltraSPARC-III+", DRMACH_DEVTYPE_CPU, drmach_cpu_new }, 50003831d35Sstevel {"memory-controller", DRMACH_DEVTYPE_MEM, drmach_mem_new }, 50103831d35Sstevel {"pci", DRMACH_DEVTYPE_PCI, drmach_pci_new }, 50203831d35Sstevel {"SUNW,wci", DRMACH_DEVTYPE_WCI, drmach_io_new }, 50303831d35Sstevel }; 50403831d35Sstevel 50503831d35Sstevel /* 50603831d35Sstevel * drmach autoconfiguration data structures and interfaces 50703831d35Sstevel */ 50803831d35Sstevel 50903831d35Sstevel extern struct mod_ops mod_miscops; 51003831d35Sstevel 51103831d35Sstevel static struct modlmisc modlmisc = { 51203831d35Sstevel &mod_miscops, 513f500b196SRichard Bean "Sun Fire 15000 DR" 51403831d35Sstevel }; 51503831d35Sstevel 51603831d35Sstevel static struct modlinkage modlinkage = { 51703831d35Sstevel MODREV_1, 51803831d35Sstevel (void *)&modlmisc, 51903831d35Sstevel NULL 52003831d35Sstevel }; 52103831d35Sstevel 52203831d35Sstevel /* 52303831d35Sstevel * drmach_boards_rwlock is used to synchronize read/write 52403831d35Sstevel * access to drmach_boards array between status and board lookup 52503831d35Sstevel * as READERS, and assign, and unassign threads as WRITERS. 52603831d35Sstevel */ 52703831d35Sstevel static krwlock_t drmach_boards_rwlock; 52803831d35Sstevel 52903831d35Sstevel static kmutex_t drmach_i_lock; 53003831d35Sstevel static kmutex_t drmach_iocage_lock; 53103831d35Sstevel static kcondvar_t drmach_iocage_cv; 53203831d35Sstevel static int drmach_iocage_is_busy = 0; 53303831d35Sstevel uint64_t drmach_iocage_paddr; 53403831d35Sstevel static caddr_t drmach_iocage_vaddr; 53503831d35Sstevel static int drmach_iocage_size = 0; 53603831d35Sstevel static int drmach_is_cheetah = -1; 53703831d35Sstevel 53803831d35Sstevel int 53903831d35Sstevel _init(void) 54003831d35Sstevel { 54103831d35Sstevel int err; 54203831d35Sstevel 54303831d35Sstevel mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL); 54403831d35Sstevel rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL); 54503831d35Sstevel drmach_xt_mb_size = NCPU * sizeof (uchar_t); 54603831d35Sstevel drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena, 54703831d35Sstevel drmach_xt_mb_size, VM_SLEEP); 54803831d35Sstevel bzero((void *)drmach_xt_mb, drmach_xt_mb_size); 54903831d35Sstevel if ((err = mod_install(&modlinkage)) != 0) { 55003831d35Sstevel mutex_destroy(&drmach_i_lock); 55103831d35Sstevel rw_destroy(&drmach_boards_rwlock); 55203831d35Sstevel vmem_free(static_alloc_arena, (void *)drmach_xt_mb, 55303831d35Sstevel drmach_xt_mb_size); 55403831d35Sstevel } 55503831d35Sstevel 55603831d35Sstevel return (err); 55703831d35Sstevel } 55803831d35Sstevel 55903831d35Sstevel int 56003831d35Sstevel _fini(void) 56103831d35Sstevel { 56203831d35Sstevel int err; 56303831d35Sstevel 56403831d35Sstevel if ((err = mod_remove(&modlinkage)) == 0) 56503831d35Sstevel drmach_fini(); 56603831d35Sstevel 56703831d35Sstevel return (err); 56803831d35Sstevel } 56903831d35Sstevel 57003831d35Sstevel int 57103831d35Sstevel _info(struct modinfo *modinfop) 57203831d35Sstevel { 57303831d35Sstevel return (mod_info(&modlinkage, modinfop)); 57403831d35Sstevel } 57503831d35Sstevel 57603831d35Sstevel /* 57703831d35Sstevel * drmach_node_* routines serve the purpose of separating the 57803831d35Sstevel * rest of the code from the device tree and OBP. This is necessary 57903831d35Sstevel * because of In-Kernel-Probing. Devices probed after stod, are probed 58003831d35Sstevel * by the in-kernel-prober, not OBP. These devices, therefore, do not 58103831d35Sstevel * have dnode ids. 58203831d35Sstevel */ 58303831d35Sstevel 58403831d35Sstevel static int 58503831d35Sstevel drmach_node_obp_get_parent(drmach_node_t *np, drmach_node_t *pp) 58603831d35Sstevel { 58703831d35Sstevel pnode_t nodeid; 58803831d35Sstevel static char *fn = "drmach_node_obp_get_parent"; 58903831d35Sstevel 59003831d35Sstevel nodeid = np->get_dnode(np); 59103831d35Sstevel if (nodeid == OBP_NONODE) { 59203831d35Sstevel cmn_err(CE_WARN, "%s: invalid dnode", fn); 59303831d35Sstevel return (-1); 59403831d35Sstevel } 59503831d35Sstevel 59603831d35Sstevel bcopy(np, pp, sizeof (drmach_node_t)); 59703831d35Sstevel 59803831d35Sstevel pp->here = (void *)(uintptr_t)prom_parentnode(nodeid); 59903831d35Sstevel if (pp->here == OBP_NONODE) { 60003831d35Sstevel cmn_err(CE_WARN, "%s: invalid parent dnode", fn); 60103831d35Sstevel return (-1); 60203831d35Sstevel } 60303831d35Sstevel 60403831d35Sstevel return (0); 60503831d35Sstevel } 60603831d35Sstevel 60703831d35Sstevel static pnode_t 60803831d35Sstevel drmach_node_obp_get_dnode(drmach_node_t *np) 60903831d35Sstevel { 61003831d35Sstevel return ((pnode_t)(uintptr_t)np->here); 61103831d35Sstevel } 61203831d35Sstevel 61303831d35Sstevel typedef struct { 61403831d35Sstevel drmach_node_walk_args_t *nwargs; 61503831d35Sstevel int (*cb)(drmach_node_walk_args_t *args); 61603831d35Sstevel int err; 61703831d35Sstevel } drmach_node_ddi_walk_args_t; 61803831d35Sstevel 61903831d35Sstevel int 62003831d35Sstevel drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg) 62103831d35Sstevel { 62203831d35Sstevel drmach_node_ddi_walk_args_t *nargs; 62303831d35Sstevel 62403831d35Sstevel nargs = (drmach_node_ddi_walk_args_t *)arg; 62503831d35Sstevel 62603831d35Sstevel /* 62703831d35Sstevel * dip doesn't have to be held here as we are called 62803831d35Sstevel * from ddi_walk_devs() which holds the dip. 62903831d35Sstevel */ 63003831d35Sstevel nargs->nwargs->node->here = (void *)dip; 63103831d35Sstevel 63203831d35Sstevel nargs->err = nargs->cb(nargs->nwargs); 63303831d35Sstevel 63403831d35Sstevel /* 63503831d35Sstevel * Set "here" to NULL so that unheld dip is not accessible 63603831d35Sstevel * outside ddi_walk_devs() 63703831d35Sstevel */ 63803831d35Sstevel nargs->nwargs->node->here = NULL; 63903831d35Sstevel 64003831d35Sstevel if (nargs->err) 64103831d35Sstevel return (DDI_WALK_TERMINATE); 64203831d35Sstevel else 64303831d35Sstevel return (DDI_WALK_CONTINUE); 64403831d35Sstevel } 64503831d35Sstevel 64603831d35Sstevel static int 64703831d35Sstevel drmach_node_ddi_walk(drmach_node_t *np, void *data, 64803831d35Sstevel int (*cb)(drmach_node_walk_args_t *args)) 64903831d35Sstevel { 65003831d35Sstevel drmach_node_walk_args_t args; 65103831d35Sstevel drmach_node_ddi_walk_args_t nargs; 65203831d35Sstevel 65303831d35Sstevel /* initialized args structure for callback */ 65403831d35Sstevel args.node = np; 65503831d35Sstevel args.data = data; 65603831d35Sstevel 65703831d35Sstevel nargs.nwargs = &args; 65803831d35Sstevel nargs.cb = cb; 65903831d35Sstevel nargs.err = 0; 66003831d35Sstevel 66103831d35Sstevel /* 66203831d35Sstevel * Root node doesn't have to be held in any way. 66303831d35Sstevel */ 664d3d50737SRafael Vanoni ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs); 66503831d35Sstevel 66603831d35Sstevel return (nargs.err); 66703831d35Sstevel } 66803831d35Sstevel 66903831d35Sstevel static int 67003831d35Sstevel drmach_node_obp_walk(drmach_node_t *np, void *data, 67103831d35Sstevel int (*cb)(drmach_node_walk_args_t *args)) 67203831d35Sstevel { 67303831d35Sstevel pnode_t nodeid; 67403831d35Sstevel int rv; 67503831d35Sstevel drmach_node_walk_args_t args; 67603831d35Sstevel 67703831d35Sstevel /* initialized args structure for callback */ 67803831d35Sstevel args.node = np; 67903831d35Sstevel args.data = data; 68003831d35Sstevel 68103831d35Sstevel nodeid = prom_childnode(prom_rootnode()); 68203831d35Sstevel 68303831d35Sstevel /* save our new position within the tree */ 68403831d35Sstevel np->here = (void *)(uintptr_t)nodeid; 68503831d35Sstevel 68603831d35Sstevel rv = 0; 68703831d35Sstevel while (nodeid != OBP_NONODE) { 68803831d35Sstevel 68903831d35Sstevel pnode_t child; 69003831d35Sstevel 69103831d35Sstevel rv = (*cb)(&args); 69203831d35Sstevel if (rv) 69303831d35Sstevel break; 69403831d35Sstevel 69503831d35Sstevel child = prom_childnode(nodeid); 69603831d35Sstevel np->here = (void *)(uintptr_t)child; 69703831d35Sstevel 69803831d35Sstevel while (child != OBP_NONODE) { 69903831d35Sstevel rv = (*cb)(&args); 70003831d35Sstevel if (rv) 70103831d35Sstevel break; 70203831d35Sstevel 70303831d35Sstevel child = prom_nextnode(child); 70403831d35Sstevel np->here = (void *)(uintptr_t)child; 70503831d35Sstevel } 70603831d35Sstevel 70703831d35Sstevel nodeid = prom_nextnode(nodeid); 70803831d35Sstevel 70903831d35Sstevel /* save our new position within the tree */ 71003831d35Sstevel np->here = (void *)(uintptr_t)nodeid; 71103831d35Sstevel } 71203831d35Sstevel 71303831d35Sstevel return (rv); 71403831d35Sstevel } 71503831d35Sstevel 71603831d35Sstevel static int 71703831d35Sstevel drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp) 71803831d35Sstevel { 71903831d35Sstevel dev_info_t *ndip; 72003831d35Sstevel static char *fn = "drmach_node_ddi_get_parent"; 72103831d35Sstevel 72203831d35Sstevel ndip = np->n_getdip(np); 72303831d35Sstevel if (ndip == NULL) { 72403831d35Sstevel cmn_err(CE_WARN, "%s: NULL dip", fn); 72503831d35Sstevel return (-1); 72603831d35Sstevel } 72703831d35Sstevel 72803831d35Sstevel bcopy(np, pp, sizeof (drmach_node_t)); 72903831d35Sstevel 73003831d35Sstevel pp->here = (void *)ddi_get_parent(ndip); 73103831d35Sstevel if (pp->here == NULL) { 73203831d35Sstevel cmn_err(CE_WARN, "%s: NULL parent dip", fn); 73303831d35Sstevel return (-1); 73403831d35Sstevel } 73503831d35Sstevel 73603831d35Sstevel return (0); 73703831d35Sstevel } 73803831d35Sstevel 73903831d35Sstevel /*ARGSUSED*/ 74003831d35Sstevel static pnode_t 74103831d35Sstevel drmach_node_ddi_get_dnode(drmach_node_t *np) 74203831d35Sstevel { 74303831d35Sstevel return ((pnode_t)NULL); 74403831d35Sstevel } 74503831d35Sstevel 74603831d35Sstevel static drmach_node_t * 74703831d35Sstevel drmach_node_new(void) 74803831d35Sstevel { 74903831d35Sstevel drmach_node_t *np; 75003831d35Sstevel 75103831d35Sstevel np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP); 75203831d35Sstevel 75303831d35Sstevel if (drmach_initialized) { 75403831d35Sstevel np->get_dnode = drmach_node_ddi_get_dnode; 75503831d35Sstevel np->walk = drmach_node_ddi_walk; 75603831d35Sstevel np->n_getdip = drmach_node_ddi_get_dip; 75703831d35Sstevel np->n_getproplen = drmach_node_ddi_get_proplen; 75803831d35Sstevel np->n_getprop = drmach_node_ddi_get_prop; 75903831d35Sstevel np->get_parent = drmach_node_ddi_get_parent; 76003831d35Sstevel } else { 76103831d35Sstevel np->get_dnode = drmach_node_obp_get_dnode; 76203831d35Sstevel np->walk = drmach_node_obp_walk; 76303831d35Sstevel np->n_getdip = drmach_node_obp_get_dip; 76403831d35Sstevel np->n_getproplen = drmach_node_obp_get_proplen; 76503831d35Sstevel np->n_getprop = drmach_node_obp_get_prop; 76603831d35Sstevel np->get_parent = drmach_node_obp_get_parent; 76703831d35Sstevel } 76803831d35Sstevel 76903831d35Sstevel return (np); 77003831d35Sstevel } 77103831d35Sstevel 77203831d35Sstevel static void 77303831d35Sstevel drmach_node_dispose(drmach_node_t *np) 77403831d35Sstevel { 77503831d35Sstevel kmem_free(np, sizeof (*np)); 77603831d35Sstevel } 77703831d35Sstevel 77803831d35Sstevel /* 77903831d35Sstevel * Check if a CPU node is part of a CMP. 78003831d35Sstevel */ 78103831d35Sstevel static int 78203831d35Sstevel drmach_is_cmp_child(dev_info_t *dip) 78303831d35Sstevel { 78403831d35Sstevel dev_info_t *pdip; 78503831d35Sstevel 78603831d35Sstevel if (strcmp(ddi_node_name(dip), DRMACH_CPU_NAMEPROP) != 0) { 78703831d35Sstevel return (0); 78803831d35Sstevel } 78903831d35Sstevel 79003831d35Sstevel pdip = ddi_get_parent(dip); 79103831d35Sstevel 79203831d35Sstevel ASSERT(pdip); 79303831d35Sstevel 79403831d35Sstevel if (strcmp(ddi_node_name(pdip), DRMACH_CMP_NAMEPROP) == 0) { 79503831d35Sstevel return (1); 79603831d35Sstevel } 79703831d35Sstevel 79803831d35Sstevel return (0); 79903831d35Sstevel } 80003831d35Sstevel 80103831d35Sstevel static dev_info_t * 80203831d35Sstevel drmach_node_obp_get_dip(drmach_node_t *np) 80303831d35Sstevel { 80403831d35Sstevel pnode_t nodeid; 80503831d35Sstevel dev_info_t *dip; 80603831d35Sstevel 80703831d35Sstevel nodeid = np->get_dnode(np); 80803831d35Sstevel if (nodeid == OBP_NONODE) 80903831d35Sstevel return (NULL); 81003831d35Sstevel 81103831d35Sstevel dip = e_ddi_nodeid_to_dip(nodeid); 81203831d35Sstevel if (dip) { 81303831d35Sstevel /* 81403831d35Sstevel * The branch rooted at dip will have been previously 81503831d35Sstevel * held, or it will be the child of a CMP. In either 81603831d35Sstevel * case, the hold acquired in e_ddi_nodeid_to_dip() 81703831d35Sstevel * is not needed. 81803831d35Sstevel */ 81903831d35Sstevel ddi_release_devi(dip); 82003831d35Sstevel ASSERT(drmach_is_cmp_child(dip) || e_ddi_branch_held(dip)); 82103831d35Sstevel } 82203831d35Sstevel 82303831d35Sstevel return (dip); 82403831d35Sstevel } 82503831d35Sstevel 82603831d35Sstevel static dev_info_t * 82703831d35Sstevel drmach_node_ddi_get_dip(drmach_node_t *np) 82803831d35Sstevel { 82903831d35Sstevel return ((dev_info_t *)np->here); 83003831d35Sstevel } 83103831d35Sstevel 83203831d35Sstevel static int 83303831d35Sstevel drmach_node_walk(drmach_node_t *np, void *param, 83403831d35Sstevel int (*cb)(drmach_node_walk_args_t *args)) 83503831d35Sstevel { 83603831d35Sstevel return (np->walk(np, param, cb)); 83703831d35Sstevel } 83803831d35Sstevel 83903831d35Sstevel static int 84003831d35Sstevel drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len) 84103831d35Sstevel { 84203831d35Sstevel int rv = 0; 84303831d35Sstevel dev_info_t *ndip; 84403831d35Sstevel static char *fn = "drmach_node_ddi_get_prop"; 84503831d35Sstevel 84603831d35Sstevel ndip = np->n_getdip(np); 84703831d35Sstevel if (ndip == NULL) { 84803831d35Sstevel cmn_err(CE_WARN, "%s: NULL dip", fn); 84903831d35Sstevel rv = -1; 85003831d35Sstevel } else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip, 85103831d35Sstevel DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, name, 85203831d35Sstevel (caddr_t)buf, &len) != DDI_PROP_SUCCESS) { 85303831d35Sstevel rv = -1; 85403831d35Sstevel } 85503831d35Sstevel 85603831d35Sstevel return (rv); 85703831d35Sstevel } 85803831d35Sstevel 85903831d35Sstevel /* ARGSUSED */ 86003831d35Sstevel static int 86103831d35Sstevel drmach_node_obp_get_prop(drmach_node_t *np, char *name, void *buf, int len) 86203831d35Sstevel { 86303831d35Sstevel int rv = 0; 86403831d35Sstevel pnode_t nodeid; 86503831d35Sstevel static char *fn = "drmach_node_obp_get_prop"; 86603831d35Sstevel 86703831d35Sstevel nodeid = np->get_dnode(np); 86803831d35Sstevel if (nodeid == OBP_NONODE) { 86903831d35Sstevel cmn_err(CE_WARN, "%s: invalid dnode", fn); 87003831d35Sstevel rv = -1; 87103831d35Sstevel } else if (prom_getproplen(nodeid, (caddr_t)name) < 0) { 87203831d35Sstevel rv = -1; 87303831d35Sstevel } else { 87403831d35Sstevel (void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf); 87503831d35Sstevel } 87603831d35Sstevel 87703831d35Sstevel return (rv); 87803831d35Sstevel } 87903831d35Sstevel 88003831d35Sstevel static int 88103831d35Sstevel drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len) 88203831d35Sstevel { 88303831d35Sstevel int rv = 0; 88403831d35Sstevel dev_info_t *ndip; 88503831d35Sstevel 88603831d35Sstevel ndip = np->n_getdip(np); 88703831d35Sstevel if (ndip == NULL) { 88803831d35Sstevel rv = -1; 88903831d35Sstevel } else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS, 89003831d35Sstevel name, len) != DDI_PROP_SUCCESS) { 89103831d35Sstevel rv = -1; 89203831d35Sstevel } 89303831d35Sstevel 89403831d35Sstevel return (rv); 89503831d35Sstevel } 89603831d35Sstevel 89703831d35Sstevel static int 89803831d35Sstevel drmach_node_obp_get_proplen(drmach_node_t *np, char *name, int *len) 89903831d35Sstevel { 90003831d35Sstevel pnode_t nodeid; 90103831d35Sstevel int rv; 90203831d35Sstevel 90303831d35Sstevel nodeid = np->get_dnode(np); 90403831d35Sstevel if (nodeid == OBP_NONODE) 90503831d35Sstevel rv = -1; 90603831d35Sstevel else { 90703831d35Sstevel *len = prom_getproplen(nodeid, (caddr_t)name); 90803831d35Sstevel rv = (*len < 0 ? -1 : 0); 90903831d35Sstevel } 91003831d35Sstevel 91103831d35Sstevel return (rv); 91203831d35Sstevel } 91303831d35Sstevel 91403831d35Sstevel static drmachid_t 91503831d35Sstevel drmach_node_dup(drmach_node_t *np) 91603831d35Sstevel { 91703831d35Sstevel drmach_node_t *dup; 91803831d35Sstevel 91903831d35Sstevel dup = drmach_node_new(); 92003831d35Sstevel dup->here = np->here; 92103831d35Sstevel dup->get_dnode = np->get_dnode; 92203831d35Sstevel dup->walk = np->walk; 92303831d35Sstevel dup->n_getdip = np->n_getdip; 92403831d35Sstevel dup->n_getproplen = np->n_getproplen; 92503831d35Sstevel dup->n_getprop = np->n_getprop; 92603831d35Sstevel dup->get_parent = np->get_parent; 92703831d35Sstevel 92803831d35Sstevel return (dup); 92903831d35Sstevel } 93003831d35Sstevel 93103831d35Sstevel /* 93203831d35Sstevel * drmach_array provides convenient array construction, access, 93303831d35Sstevel * bounds checking and array destruction logic. 93403831d35Sstevel */ 93503831d35Sstevel 93603831d35Sstevel static drmach_array_t * 93703831d35Sstevel drmach_array_new(int min_index, int max_index) 93803831d35Sstevel { 93903831d35Sstevel drmach_array_t *arr; 94003831d35Sstevel 94103831d35Sstevel arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP); 94203831d35Sstevel 94303831d35Sstevel arr->arr_sz = (max_index - min_index + 1) * sizeof (void *); 94403831d35Sstevel if (arr->arr_sz > 0) { 94503831d35Sstevel arr->min_index = min_index; 94603831d35Sstevel arr->max_index = max_index; 94703831d35Sstevel 94803831d35Sstevel arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP); 94903831d35Sstevel return (arr); 95003831d35Sstevel } else { 95103831d35Sstevel kmem_free(arr, sizeof (*arr)); 95203831d35Sstevel return (0); 95303831d35Sstevel } 95403831d35Sstevel } 95503831d35Sstevel 95603831d35Sstevel static int 95703831d35Sstevel drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val) 95803831d35Sstevel { 95903831d35Sstevel if (idx < arr->min_index || idx > arr->max_index) 96003831d35Sstevel return (-1); 96103831d35Sstevel else { 96203831d35Sstevel arr->arr[idx - arr->min_index] = val; 96303831d35Sstevel return (0); 96403831d35Sstevel } 96503831d35Sstevel /*NOTREACHED*/ 96603831d35Sstevel } 96703831d35Sstevel 96803831d35Sstevel static int 96903831d35Sstevel drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val) 97003831d35Sstevel { 97103831d35Sstevel if (idx < arr->min_index || idx > arr->max_index) 97203831d35Sstevel return (-1); 97303831d35Sstevel else { 97403831d35Sstevel *val = arr->arr[idx - arr->min_index]; 97503831d35Sstevel return (0); 97603831d35Sstevel } 97703831d35Sstevel /*NOTREACHED*/ 97803831d35Sstevel } 97903831d35Sstevel 98003831d35Sstevel static int 98103831d35Sstevel drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val) 98203831d35Sstevel { 98303831d35Sstevel int rv; 98403831d35Sstevel 98503831d35Sstevel *idx = arr->min_index; 98603831d35Sstevel while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 98703831d35Sstevel *idx += 1; 98803831d35Sstevel 98903831d35Sstevel return (rv); 99003831d35Sstevel } 99103831d35Sstevel 99203831d35Sstevel static int 99303831d35Sstevel drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val) 99403831d35Sstevel { 99503831d35Sstevel int rv; 99603831d35Sstevel 99703831d35Sstevel *idx += 1; 99803831d35Sstevel while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL) 99903831d35Sstevel *idx += 1; 100003831d35Sstevel 100103831d35Sstevel return (rv); 100203831d35Sstevel } 100303831d35Sstevel 100403831d35Sstevel static void 100503831d35Sstevel drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t)) 100603831d35Sstevel { 100703831d35Sstevel drmachid_t val; 100803831d35Sstevel int idx; 100903831d35Sstevel int rv; 101003831d35Sstevel 101103831d35Sstevel rv = drmach_array_first(arr, &idx, &val); 101203831d35Sstevel while (rv == 0) { 101303831d35Sstevel (*disposer)(val); 101403831d35Sstevel 101503831d35Sstevel /* clear the array entry */ 101603831d35Sstevel rv = drmach_array_set(arr, idx, NULL); 101703831d35Sstevel ASSERT(rv == 0); 101803831d35Sstevel 101903831d35Sstevel rv = drmach_array_next(arr, &idx, &val); 102003831d35Sstevel } 102103831d35Sstevel 102203831d35Sstevel kmem_free(arr->arr, arr->arr_sz); 102303831d35Sstevel kmem_free(arr, sizeof (*arr)); 102403831d35Sstevel } 102503831d35Sstevel 102603831d35Sstevel 102703831d35Sstevel static gdcd_t * 102803831d35Sstevel drmach_gdcd_new() 102903831d35Sstevel { 103003831d35Sstevel gdcd_t *gdcd; 103103831d35Sstevel 103203831d35Sstevel gdcd = kmem_zalloc(sizeof (gdcd_t), KM_SLEEP); 103303831d35Sstevel 103403831d35Sstevel /* read the gdcd, bail if magic or ver #s are not what is expected */ 103503831d35Sstevel if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) { 103603831d35Sstevel bail: 103703831d35Sstevel kmem_free(gdcd, sizeof (gdcd_t)); 103803831d35Sstevel return (NULL); 103903831d35Sstevel } else if (gdcd->h.dcd_magic != GDCD_MAGIC) { 104003831d35Sstevel goto bail; 104103831d35Sstevel } else if (gdcd->h.dcd_version != DCD_VERSION) { 104203831d35Sstevel goto bail; 104303831d35Sstevel } 104403831d35Sstevel 104503831d35Sstevel return (gdcd); 104603831d35Sstevel } 104703831d35Sstevel 104803831d35Sstevel static void 104903831d35Sstevel drmach_gdcd_dispose(gdcd_t *gdcd) 105003831d35Sstevel { 105103831d35Sstevel kmem_free(gdcd, sizeof (gdcd_t)); 105203831d35Sstevel } 105303831d35Sstevel 105403831d35Sstevel /*ARGSUSED*/ 105503831d35Sstevel sbd_error_t * 105603831d35Sstevel drmach_configure(drmachid_t id, int flags) 105703831d35Sstevel { 105803831d35Sstevel drmach_device_t *dp; 105903831d35Sstevel dev_info_t *rdip; 106003831d35Sstevel sbd_error_t *err = NULL; 106103831d35Sstevel 106203831d35Sstevel /* 106303831d35Sstevel * On Starcat, there is no CPU driver, so it is 106403831d35Sstevel * not necessary to configure any CPU nodes. 106503831d35Sstevel */ 106603831d35Sstevel if (DRMACH_IS_CPU_ID(id)) { 106703831d35Sstevel return (NULL); 106803831d35Sstevel } 106903831d35Sstevel 107003831d35Sstevel for (; id; ) { 107103831d35Sstevel dev_info_t *fdip = NULL; 107203831d35Sstevel 107303831d35Sstevel if (!DRMACH_IS_DEVICE_ID(id)) 107403831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 107503831d35Sstevel dp = id; 107603831d35Sstevel 107703831d35Sstevel rdip = dp->node->n_getdip(dp->node); 107803831d35Sstevel 107903831d35Sstevel /* 108003831d35Sstevel * We held this branch earlier, so at a minimum its 108103831d35Sstevel * root should still be present in the device tree. 108203831d35Sstevel */ 108303831d35Sstevel ASSERT(rdip); 108403831d35Sstevel 108503831d35Sstevel DRMACH_PR("drmach_configure: configuring DDI branch"); 108603831d35Sstevel 108703831d35Sstevel ASSERT(e_ddi_branch_held(rdip)); 108803831d35Sstevel if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) { 108903831d35Sstevel if (err == NULL) { 109003831d35Sstevel /* 109103831d35Sstevel * Record first failure but don't stop 109203831d35Sstevel */ 109303831d35Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 109403831d35Sstevel dev_info_t *dip = (fdip != NULL) ? fdip : rdip; 109503831d35Sstevel 109603831d35Sstevel (void) ddi_pathname(dip, path); 109703831d35Sstevel err = drerr_new(1, ESTC_DRVFAIL, path); 109803831d35Sstevel 109903831d35Sstevel kmem_free(path, MAXPATHLEN); 110003831d35Sstevel } 110103831d35Sstevel 110203831d35Sstevel /* 110303831d35Sstevel * If non-NULL, fdip is returned held and must be 110403831d35Sstevel * released. 110503831d35Sstevel */ 110603831d35Sstevel if (fdip != NULL) { 110703831d35Sstevel ddi_release_devi(fdip); 110803831d35Sstevel } 110903831d35Sstevel } 111003831d35Sstevel 111103831d35Sstevel if (DRMACH_IS_MEM_ID(id)) { 111203831d35Sstevel drmach_mem_t *mp = id; 111303831d35Sstevel id = mp->next; 111403831d35Sstevel } else { 111503831d35Sstevel id = NULL; 111603831d35Sstevel } 111703831d35Sstevel } 111803831d35Sstevel 111903831d35Sstevel return (err); 112003831d35Sstevel } 112103831d35Sstevel 112203831d35Sstevel static sbd_error_t * 112303831d35Sstevel drmach_device_new(drmach_node_t *node, 112403831d35Sstevel drmach_board_t *bp, int portid, drmachid_t *idp) 112503831d35Sstevel { 112603831d35Sstevel int i, rv, device_id, unum; 112703831d35Sstevel char name[OBP_MAXDRVNAME]; 112803831d35Sstevel drmach_device_t proto; 112903831d35Sstevel 113003831d35Sstevel rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME); 113103831d35Sstevel if (rv) { 113203831d35Sstevel sbd_error_t *err; 113303831d35Sstevel 113403831d35Sstevel /* every node is expected to have a name */ 113503831d35Sstevel err = drerr_new(1, ESTC_GETPROP, 113603831d35Sstevel "dip: 0x%p: property %s", 113703831d35Sstevel node->n_getdip(node), OBP_NAME); 113803831d35Sstevel 113903831d35Sstevel return (err); 114003831d35Sstevel } 114103831d35Sstevel 114203831d35Sstevel i = drmach_name2type_idx(name); 114303831d35Sstevel 114403831d35Sstevel if (i < 0 || strcmp(name, "cmp") == 0) { 114503831d35Sstevel /* 114603831d35Sstevel * Not a node of interest to dr - including "cmp", 114703831d35Sstevel * but it is in drmach_name2type[], which lets gptwocfg 114803831d35Sstevel * driver to check if node is OBP created. 114903831d35Sstevel */ 115003831d35Sstevel *idp = (drmachid_t)0; 115103831d35Sstevel return (NULL); 115203831d35Sstevel } 115303831d35Sstevel 115403831d35Sstevel /* 115503831d35Sstevel * Derive a best-guess unit number from the portid value. 115603831d35Sstevel * Some drmach_*_new constructors (drmach_pci_new, for example) 115703831d35Sstevel * will overwrite the prototype unum value with one that is more 115803831d35Sstevel * appropriate for the device. 115903831d35Sstevel */ 116003831d35Sstevel device_id = portid & 0x1f; 116103831d35Sstevel if (device_id < 4) 116203831d35Sstevel unum = device_id; 116303831d35Sstevel else if (device_id == 8) { 116403831d35Sstevel unum = 0; 116503831d35Sstevel } else if (device_id == 9) { 116603831d35Sstevel unum = 1; 116703831d35Sstevel } else if (device_id == 0x1c) { 116803831d35Sstevel unum = 0; 116903831d35Sstevel } else if (device_id == 0x1d) { 117003831d35Sstevel unum = 1; 117103831d35Sstevel } else { 117203831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 117303831d35Sstevel } 117403831d35Sstevel 117503831d35Sstevel bzero(&proto, sizeof (proto)); 117603831d35Sstevel proto.type = drmach_name2type[i].type; 117703831d35Sstevel proto.bp = bp; 117803831d35Sstevel proto.node = node; 117903831d35Sstevel proto.portid = portid; 118003831d35Sstevel proto.unum = unum; 118103831d35Sstevel 118203831d35Sstevel return (drmach_name2type[i].new(&proto, idp)); 118303831d35Sstevel } 118403831d35Sstevel 118503831d35Sstevel static void 118603831d35Sstevel drmach_device_dispose(drmachid_t id) 118703831d35Sstevel { 118803831d35Sstevel drmach_device_t *self = id; 118903831d35Sstevel 119003831d35Sstevel self->cm.dispose(id); 119103831d35Sstevel } 119203831d35Sstevel 119303831d35Sstevel static drmach_board_t * 119403831d35Sstevel drmach_board_new(int bnum) 119503831d35Sstevel { 119603831d35Sstevel drmach_board_t *bp; 119703831d35Sstevel 119803831d35Sstevel bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP); 119903831d35Sstevel 120003831d35Sstevel bp->cm.isa = (void *)drmach_board_new; 120103831d35Sstevel bp->cm.release = drmach_board_release; 120203831d35Sstevel bp->cm.status = drmach_board_status; 120303831d35Sstevel 120403831d35Sstevel (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name)); 120503831d35Sstevel 120603831d35Sstevel bp->bnum = bnum; 120703831d35Sstevel bp->devices = NULL; 120803831d35Sstevel bp->tree = drmach_node_new(); 120903831d35Sstevel 121007d06da5SSurya Prakki (void) drmach_array_set(drmach_boards, bnum, bp); 121103831d35Sstevel return (bp); 121203831d35Sstevel } 121303831d35Sstevel 121403831d35Sstevel static void 121503831d35Sstevel drmach_board_dispose(drmachid_t id) 121603831d35Sstevel { 121703831d35Sstevel drmach_board_t *bp; 121803831d35Sstevel 121903831d35Sstevel ASSERT(DRMACH_IS_BOARD_ID(id)); 122003831d35Sstevel bp = id; 122103831d35Sstevel 122203831d35Sstevel if (bp->tree) 122303831d35Sstevel drmach_node_dispose(bp->tree); 122403831d35Sstevel 122503831d35Sstevel if (bp->devices) 122603831d35Sstevel drmach_array_dispose(bp->devices, drmach_device_dispose); 122703831d35Sstevel 122803831d35Sstevel kmem_free(bp, sizeof (*bp)); 122903831d35Sstevel } 123003831d35Sstevel 123103831d35Sstevel static sbd_error_t * 123203831d35Sstevel drmach_board_status(drmachid_t id, drmach_status_t *stat) 123303831d35Sstevel { 123403831d35Sstevel sbd_error_t *err = NULL; 123503831d35Sstevel drmach_board_t *bp; 123603831d35Sstevel caddr_t obufp; 123703831d35Sstevel dr_showboard_t shb; 123803831d35Sstevel 123903831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 124003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 124103831d35Sstevel 124203831d35Sstevel bp = id; 124303831d35Sstevel 124403831d35Sstevel /* 124503831d35Sstevel * we need to know if the board's connected before 124603831d35Sstevel * issuing a showboard message. If it's connected, we just 124703831d35Sstevel * reply with status composed of cached info 124803831d35Sstevel */ 124903831d35Sstevel 125003831d35Sstevel if (!bp->connected) { 125103831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 125203831d35Sstevel err = drmach_mbox_trans(DRMSG_SHOWBOARD, bp->bnum, obufp, 125303831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)&shb, 125403831d35Sstevel sizeof (dr_showboard_t)); 125503831d35Sstevel 125603831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 125703831d35Sstevel if (err) 125803831d35Sstevel return (err); 125903831d35Sstevel 126003831d35Sstevel bp->connected = (shb.bd_assigned && shb.bd_active); 126107d06da5SSurya Prakki (void) strncpy(bp->type, shb.board_type, sizeof (bp->type)); 126203831d35Sstevel stat->assigned = bp->assigned = shb.bd_assigned; 126303831d35Sstevel stat->powered = bp->powered = shb.power_on; 126403831d35Sstevel stat->empty = bp->empty = shb.slot_empty; 126503831d35Sstevel 126603831d35Sstevel switch (shb.test_status) { 126703831d35Sstevel case DR_TEST_STATUS_UNKNOWN: 126803831d35Sstevel case DR_TEST_STATUS_IPOST: 126903831d35Sstevel case DR_TEST_STATUS_ABORTED: 127003831d35Sstevel stat->cond = bp->cond = SBD_COND_UNKNOWN; 127103831d35Sstevel break; 127203831d35Sstevel case DR_TEST_STATUS_PASSED: 127303831d35Sstevel stat->cond = bp->cond = SBD_COND_OK; 127403831d35Sstevel break; 127503831d35Sstevel case DR_TEST_STATUS_FAILED: 127603831d35Sstevel stat->cond = bp->cond = SBD_COND_FAILED; 127703831d35Sstevel break; 127803831d35Sstevel default: 127903831d35Sstevel stat->cond = bp->cond = SBD_COND_UNKNOWN; 128003831d35Sstevel DRMACH_PR("Unknown test status=0x%x from SC\n", 128103831d35Sstevel shb.test_status); 128203831d35Sstevel break; 128303831d35Sstevel 128403831d35Sstevel } 128503831d35Sstevel 128607d06da5SSurya Prakki (void) strncpy(stat->type, shb.board_type, sizeof (stat->type)); 128707d06da5SSurya Prakki (void) snprintf(stat->info, sizeof (stat->info), 128807d06da5SSurya Prakki "Test Level=%d", shb.test_level); 128903831d35Sstevel } else { 129003831d35Sstevel stat->assigned = bp->assigned; 129103831d35Sstevel stat->powered = bp->powered; 129203831d35Sstevel stat->empty = bp->empty; 129303831d35Sstevel stat->cond = bp->cond; 129407d06da5SSurya Prakki (void) strncpy(stat->type, bp->type, sizeof (stat->type)); 129503831d35Sstevel } 129603831d35Sstevel 129703831d35Sstevel stat->busy = 0; /* assume not busy */ 129803831d35Sstevel stat->configured = 0; /* assume not configured */ 129903831d35Sstevel if (bp->devices) { 130003831d35Sstevel int rv; 130103831d35Sstevel int d_idx; 130203831d35Sstevel drmachid_t d_id; 130303831d35Sstevel 130403831d35Sstevel rv = drmach_array_first(bp->devices, &d_idx, &d_id); 130503831d35Sstevel while (rv == 0) { 130603831d35Sstevel drmach_status_t d_stat; 130703831d35Sstevel 130803831d35Sstevel err = drmach_i_status(d_id, &d_stat); 130903831d35Sstevel if (err) 131003831d35Sstevel break; 131103831d35Sstevel 131203831d35Sstevel stat->busy |= d_stat.busy; 131303831d35Sstevel stat->configured |= d_stat.configured; 131403831d35Sstevel 131503831d35Sstevel rv = drmach_array_next(bp->devices, &d_idx, &d_id); 131603831d35Sstevel } 131703831d35Sstevel } 131803831d35Sstevel 131903831d35Sstevel return (err); 132003831d35Sstevel } 132103831d35Sstevel 132203831d35Sstevel typedef struct drmach_msglist { 132303831d35Sstevel kcondvar_t s_cv; /* condvar for sending msg */ 132403831d35Sstevel kmutex_t s_lock; /* mutex for sending */ 132503831d35Sstevel kcondvar_t g_cv; /* condvar for getting reply */ 132603831d35Sstevel kmutex_t g_lock; /* mutex for getting reply */ 132703831d35Sstevel struct drmach_msglist *prev; /* link to previous entry */ 132803831d35Sstevel struct drmach_msglist *next; /* link to next entry */ 132903831d35Sstevel struct drmach_msglist *link; /* link to related entry */ 133003831d35Sstevel caddr_t o_buf; /* address of output buffer */ 133103831d35Sstevel caddr_t i_buf; /* address of input buffer */ 133203831d35Sstevel uint32_t o_buflen; /* output buffer length */ 133303831d35Sstevel uint32_t i_buflen; /* input buffer length */ 133403831d35Sstevel uint32_t msgid; /* message identifier */ 133503831d35Sstevel int o_nretry; /* number of sending retries */ 133603831d35Sstevel int f_error; /* mailbox framework error */ 133703831d35Sstevel uint8_t e_code; /* error code returned by SC */ 133803831d35Sstevel uint8_t p_flag :1, /* successfully putmsg */ 133903831d35Sstevel m_reply :1, /* msg reply received */ 134003831d35Sstevel unused :6; 134103831d35Sstevel } drmach_msglist_t; 134203831d35Sstevel 134303831d35Sstevel kmutex_t drmach_g_mbox_mutex; /* mutex for mailbox globals */ 134403831d35Sstevel kmutex_t drmach_ri_mbox_mutex; /* mutex for mailbox reinit */ 134503831d35Sstevel kmutex_t drmach_msglist_mutex; /* mutex for message list */ 134603831d35Sstevel drmach_msglist_t *drmach_msglist_first; /* first entry in msg list */ 134703831d35Sstevel drmach_msglist_t *drmach_msglist_last; /* last entry in msg list */ 134803831d35Sstevel uint32_t drmach_msgid; /* current message id */ 134903831d35Sstevel kthread_t *drmach_getmsg_thread; /* ptr to getmsg thread */ 135003831d35Sstevel volatile int drmach_getmsg_thread_run; /* run flag for getmsg thr */ 135103831d35Sstevel kmutex_t drmach_sendmsg_mutex; /* mutex for sendmsg cv */ 135203831d35Sstevel kcondvar_t drmach_sendmsg_cv; /* signaled to send new msg */ 135303831d35Sstevel kthread_t *drmach_sendmsg_thread; /* ptr to sendmsg thread */ 135403831d35Sstevel volatile int drmach_sendmsg_thread_run; /* run flag for sendmsg */ 135503831d35Sstevel int drmach_mbox_istate; /* mailbox init state */ 135603831d35Sstevel int drmach_mbox_iflag; /* set if init'd with SC */ 135703831d35Sstevel int drmach_mbox_ipending; /* set if reinit scheduled */ 135803831d35Sstevel 135903831d35Sstevel /* 136003831d35Sstevel * Timeout values (in seconds) used when waiting for replies (from the SC) to 136103831d35Sstevel * requests that we sent. Since we only receive boardevent messages, and they 136203831d35Sstevel * are events rather than replies, there is no boardevent timeout. 136303831d35Sstevel */ 136403831d35Sstevel int drmach_to_mbxinit = 60; /* 1 minute */ 136503831d35Sstevel int drmach_to_assign = 60; /* 1 minute */ 136603831d35Sstevel int drmach_to_unassign = 60; /* 1 minute */ 136703831d35Sstevel int drmach_to_claim = 3600; /* 1 hour */ 136803831d35Sstevel int drmach_to_unclaim = 3600; /* 1 hour */ 136903831d35Sstevel int drmach_to_poweron = 480; /* 8 minutes */ 137003831d35Sstevel int drmach_to_poweroff = 480; /* 8 minutes */ 137103831d35Sstevel int drmach_to_testboard = 43200; /* 12 hours */ 137203831d35Sstevel int drmach_to_aborttest = 180; /* 3 minutes */ 137303831d35Sstevel int drmach_to_showboard = 180; /* 3 minutes */ 137403831d35Sstevel int drmach_to_unconfig = 180; /* 3 minutes */ 137503831d35Sstevel 137603831d35Sstevel /* 137703831d35Sstevel * Delay (in seconds) used after receiving a non-transient error indication from 137803831d35Sstevel * an mboxsc_getmsg call in the thread that loops waiting for incoming messages. 137903831d35Sstevel */ 138003831d35Sstevel int drmach_mbxerr_delay = 15; /* 15 seconds */ 138103831d35Sstevel 138203831d35Sstevel /* 138303831d35Sstevel * Timeout values (in milliseconds) for mboxsc_putmsg and mboxsc_getmsg calls. 138403831d35Sstevel */ 138503831d35Sstevel clock_t drmach_to_putmsg; /* set in drmach_mbox_init */ 138603831d35Sstevel clock_t drmach_to_getmsg = 31000; /* 31 seconds */ 138703831d35Sstevel 138803831d35Sstevel /* 138903831d35Sstevel * Normally, drmach_to_putmsg is set dynamically during initialization in 139003831d35Sstevel * drmach_mbox_init. This has the potentially undesirable side effect of 139103831d35Sstevel * clobbering any value that might have been set in /etc/system. To prevent 139203831d35Sstevel * dynamic setting of drmach_to_putmsg (thereby allowing it to be tuned in 139303831d35Sstevel * /etc/system), set drmach_use_tuned_putmsg_to to 1. 139403831d35Sstevel */ 139503831d35Sstevel int drmach_use_tuned_putmsg_to = 0; 139603831d35Sstevel 139703831d35Sstevel 139803831d35Sstevel /* maximum conceivable message size for future mailbox protocol versions */ 139903831d35Sstevel #define DRMACH_MAX_MBOX_MSG_SIZE 4096 140003831d35Sstevel 140103831d35Sstevel /*ARGSUSED*/ 140203831d35Sstevel void 140303831d35Sstevel drmach_mbox_prmsg(dr_mbox_msg_t *mbp, int dir) 140403831d35Sstevel { 140503831d35Sstevel int i, j; 140603831d35Sstevel dr_memregs_t *memregs; 140703831d35Sstevel dr_proto_hdr_t *php = &mbp->p_hdr; 140803831d35Sstevel dr_msg_t *mp = &mbp->msgdata; 140903831d35Sstevel 141003831d35Sstevel #ifdef DEBUG 141103831d35Sstevel switch (php->command) { 141203831d35Sstevel case DRMSG_BOARDEVENT: 141303831d35Sstevel if (dir) { 141403831d35Sstevel DRMACH_PR("ERROR!! outgoing BOARDEVENT\n"); 141503831d35Sstevel } else { 141603831d35Sstevel DRMACH_PR("BOARDEVENT received:\n"); 141703831d35Sstevel DRMACH_PR("init=%d ins=%d rem=%d asgn=%d\n", 141803831d35Sstevel mp->dm_be.initialized, 141903831d35Sstevel mp->dm_be.board_insertion, 142003831d35Sstevel mp->dm_be.board_removal, 142103831d35Sstevel mp->dm_be.slot_assign); 142203831d35Sstevel DRMACH_PR("unasgn=%d avail=%d unavail=%d\n", 142303831d35Sstevel mp->dm_be.slot_unassign, 142403831d35Sstevel mp->dm_be.slot_avail, 142503831d35Sstevel mp->dm_be.slot_unavail); 142603831d35Sstevel } 142703831d35Sstevel break; 142803831d35Sstevel case DRMSG_MBOX_INIT: 142903831d35Sstevel if (dir) { 143003831d35Sstevel DRMACH_PR("MBOX_INIT Request:\n"); 143103831d35Sstevel } else { 143203831d35Sstevel DRMACH_PR("MBOX_INIT Reply:\n"); 143303831d35Sstevel } 143403831d35Sstevel break; 143503831d35Sstevel case DRMSG_ASSIGN: 143603831d35Sstevel if (dir) { 143703831d35Sstevel DRMACH_PR("ASSIGN Request:\n"); 143803831d35Sstevel } else { 143903831d35Sstevel DRMACH_PR("ASSIGN Reply:\n"); 144003831d35Sstevel } 144103831d35Sstevel break; 144203831d35Sstevel case DRMSG_UNASSIGN: 144303831d35Sstevel if (dir) { 144403831d35Sstevel DRMACH_PR("UNASSIGN Request:\n"); 144503831d35Sstevel } else { 144603831d35Sstevel DRMACH_PR("UNASSIGN Reply:\n"); 144703831d35Sstevel } 144803831d35Sstevel break; 144903831d35Sstevel case DRMSG_CLAIM: 145003831d35Sstevel if (!dir) { 145103831d35Sstevel DRMACH_PR("CLAIM Reply:\n"); 145203831d35Sstevel break; 145303831d35Sstevel } 145403831d35Sstevel 145503831d35Sstevel DRMACH_PR("CLAIM Request:\n"); 145603831d35Sstevel for (i = 0; i < 18; ++i) { 145703831d35Sstevel DRMACH_PR("exp%d: val=%d slice=0x%x\n", i, 145803831d35Sstevel mp->dm_cr.mem_slice[i].valid, 145903831d35Sstevel mp->dm_cr.mem_slice[i].slice); 146003831d35Sstevel memregs = &(mp->dm_cr.mem_regs[i]); 146103831d35Sstevel for (j = 0; j < S0_LPORT_COUNT; j++) { 146203831d35Sstevel DRMACH_PR(" MC %2d: " 146303831d35Sstevel "MADR[%d] = 0x%lx, " 146403831d35Sstevel "MADR[%d] = 0x%lx\n", j, 146503831d35Sstevel 0, DRMACH_MCREG_TO_U64( 146603831d35Sstevel memregs->madr[j][0]), 146703831d35Sstevel 1, DRMACH_MCREG_TO_U64( 146803831d35Sstevel memregs->madr[j][1])); 146903831d35Sstevel DRMACH_PR(" : " 147003831d35Sstevel "MADR[%d] = 0x%lx, " 147103831d35Sstevel "MADR[%d] = 0x%lx\n", 147203831d35Sstevel 2, DRMACH_MCREG_TO_U64( 147303831d35Sstevel memregs->madr[j][2]), 147403831d35Sstevel 3, DRMACH_MCREG_TO_U64( 147503831d35Sstevel memregs->madr[j][3])); 147603831d35Sstevel } 147703831d35Sstevel } 147803831d35Sstevel break; 147903831d35Sstevel case DRMSG_UNCLAIM: 148003831d35Sstevel if (!dir) { 148103831d35Sstevel DRMACH_PR("UNCLAIM Reply:\n"); 148203831d35Sstevel break; 148303831d35Sstevel } 148403831d35Sstevel 148503831d35Sstevel DRMACH_PR("UNCLAIM Request:\n"); 148603831d35Sstevel for (i = 0; i < 18; ++i) { 148703831d35Sstevel DRMACH_PR("exp%d: val=%d slice=0x%x\n", i, 148803831d35Sstevel mp->dm_ur.mem_slice[i].valid, 148903831d35Sstevel mp->dm_ur.mem_slice[i].slice); 149003831d35Sstevel memregs = &(mp->dm_ur.mem_regs[i]); 149103831d35Sstevel for (j = 0; j < S0_LPORT_COUNT; j++) { 149203831d35Sstevel DRMACH_PR(" MC %2d: " 149303831d35Sstevel "MADR[%d] = 0x%lx, " 149403831d35Sstevel "MADR[%d] = 0x%lx\n", j, 149503831d35Sstevel 0, DRMACH_MCREG_TO_U64( 149603831d35Sstevel memregs->madr[j][0]), 149703831d35Sstevel 1, DRMACH_MCREG_TO_U64( 149803831d35Sstevel memregs->madr[j][1])); 149903831d35Sstevel DRMACH_PR(" : " 150003831d35Sstevel "MADR[%d] = 0x%lx, " 150103831d35Sstevel "MADR[%d] = 0x%lx\n", 150203831d35Sstevel 2, DRMACH_MCREG_TO_U64( 150303831d35Sstevel memregs->madr[j][2]), 150403831d35Sstevel 3, DRMACH_MCREG_TO_U64( 150503831d35Sstevel memregs->madr[j][3])); 150603831d35Sstevel } 150703831d35Sstevel } 150803831d35Sstevel DRMACH_PR(" mem_clear=%d\n", mp->dm_ur.mem_clear); 150903831d35Sstevel break; 151003831d35Sstevel case DRMSG_UNCONFIG: 151103831d35Sstevel if (!dir) { 151203831d35Sstevel DRMACH_PR("UNCONFIG Reply:\n"); 151303831d35Sstevel break; 151403831d35Sstevel } 151503831d35Sstevel 151603831d35Sstevel DRMACH_PR("UNCONFIG Request:\n"); 151703831d35Sstevel for (i = 0; i < 18; ++i) { 151803831d35Sstevel DRMACH_PR("exp%d: val=%d slice=0x%x\n", i, 151903831d35Sstevel mp->dm_uc.mem_slice[i].valid, 152003831d35Sstevel mp->dm_uc.mem_slice[i].slice); 152103831d35Sstevel memregs = &(mp->dm_uc.mem_regs[i]); 152203831d35Sstevel for (j = 0; j < S0_LPORT_COUNT; j++) { 152303831d35Sstevel DRMACH_PR(" MC %2d: " 152403831d35Sstevel "MADR[%d] = 0x%lx, " 152503831d35Sstevel "MADR[%d] = 0x%lx\n", j, 152603831d35Sstevel 0, DRMACH_MCREG_TO_U64( 152703831d35Sstevel memregs->madr[j][0]), 152803831d35Sstevel 1, DRMACH_MCREG_TO_U64( 152903831d35Sstevel memregs->madr[j][1])); 153003831d35Sstevel DRMACH_PR(" : " 153103831d35Sstevel "MADR[%d] = 0x%lx, " 153203831d35Sstevel "MADR[%d] = 0x%lx\n", 153303831d35Sstevel 2, DRMACH_MCREG_TO_U64( 153403831d35Sstevel memregs->madr[j][2]), 153503831d35Sstevel 3, DRMACH_MCREG_TO_U64( 153603831d35Sstevel memregs->madr[j][3])); 153703831d35Sstevel } 153803831d35Sstevel } 153903831d35Sstevel break; 154003831d35Sstevel case DRMSG_POWERON: 154103831d35Sstevel if (dir) { 154203831d35Sstevel DRMACH_PR("POWERON Request:\n"); 154303831d35Sstevel } else { 154403831d35Sstevel DRMACH_PR("POWERON Reply:\n"); 154503831d35Sstevel } 154603831d35Sstevel break; 154703831d35Sstevel case DRMSG_POWEROFF: 154803831d35Sstevel if (dir) { 154903831d35Sstevel DRMACH_PR("POWEROFF Request:\n"); 155003831d35Sstevel } else { 155103831d35Sstevel DRMACH_PR("POWEROFF Reply:\n"); 155203831d35Sstevel } 155303831d35Sstevel break; 155403831d35Sstevel case DRMSG_TESTBOARD: 155503831d35Sstevel if (dir) { 155603831d35Sstevel DRMACH_PR("TESTBOARD Request:\n"); 155703831d35Sstevel DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ", 155803831d35Sstevel mp->dm_tb.memaddrhi, 155903831d35Sstevel mp->dm_tb.memaddrlo); 156003831d35Sstevel DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n", 156103831d35Sstevel mp->dm_tb.memlen, mp->dm_tb.cpu_portid); 156203831d35Sstevel DRMACH_PR("\tforce=0x%x imm=0x%x\n", 156303831d35Sstevel mp->dm_tb.force, mp->dm_tb.immediate); 156403831d35Sstevel } else { 156503831d35Sstevel DRMACH_PR("TESTBOARD Reply:\n"); 156603831d35Sstevel DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ", 156703831d35Sstevel mp->dm_tr.memaddrhi, 156803831d35Sstevel mp->dm_tr.memaddrlo); 156903831d35Sstevel DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n", 157003831d35Sstevel mp->dm_tr.memlen, mp->dm_tr.cpu_portid); 157103831d35Sstevel DRMACH_PR("\trecovered=0x%x test status=0x%x\n", 157203831d35Sstevel mp->dm_tr.cpu_recovered, 157303831d35Sstevel mp->dm_tr.test_status); 157403831d35Sstevel 157503831d35Sstevel } 157603831d35Sstevel break; 157703831d35Sstevel case DRMSG_ABORT_TEST: 157803831d35Sstevel if (dir) { 157903831d35Sstevel DRMACH_PR("ABORT_TEST Request:\n"); 158003831d35Sstevel } else { 158103831d35Sstevel DRMACH_PR("ABORT_TEST Reply:\n"); 158203831d35Sstevel } 158303831d35Sstevel 158403831d35Sstevel DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ", 158503831d35Sstevel mp->dm_ta.memaddrhi, 158603831d35Sstevel mp->dm_ta.memaddrlo); 158703831d35Sstevel DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n", 158803831d35Sstevel mp->dm_ta.memlen, mp->dm_ta.cpu_portid); 158903831d35Sstevel break; 159003831d35Sstevel case DRMSG_SHOWBOARD: 159103831d35Sstevel if (dir) { 159203831d35Sstevel DRMACH_PR("SHOWBOARD Request:\n"); 159303831d35Sstevel } else { 159403831d35Sstevel DRMACH_PR("SHOWBOARD Reply:\n"); 159503831d35Sstevel 159603831d35Sstevel DRMACH_PR(": empty=%d power=%d assigned=%d", 159703831d35Sstevel mp->dm_sb.slot_empty, 159803831d35Sstevel mp->dm_sb.power_on, 159903831d35Sstevel mp->dm_sb.bd_assigned); 160003831d35Sstevel DRMACH_PR(": active=%d t_status=%d t_level=%d ", 160103831d35Sstevel mp->dm_sb.bd_active, 160203831d35Sstevel mp->dm_sb.test_status, 160303831d35Sstevel mp->dm_sb.test_level); 160403831d35Sstevel DRMACH_PR(": type=%s ", mp->dm_sb.board_type); 160503831d35Sstevel } 160603831d35Sstevel break; 160703831d35Sstevel default: 160803831d35Sstevel DRMACH_PR("Unknown message type\n"); 160903831d35Sstevel break; 161003831d35Sstevel } 161103831d35Sstevel 161203831d35Sstevel DRMACH_PR("dr hdr:\n\tid=0x%x vers=0x%x cmd=0x%x exp=0x%x slot=0x%x\n", 161303831d35Sstevel php->message_id, php->drproto_version, php->command, 161403831d35Sstevel php->expbrd, php->slot); 161503831d35Sstevel #endif 161603831d35Sstevel DRMACH_PR("\treply_status=0x%x error_code=0x%x\n", php->reply_status, 161703831d35Sstevel php->error_code); 161803831d35Sstevel } 161903831d35Sstevel 162003831d35Sstevel /* 162103831d35Sstevel * Callback function passed to taskq_dispatch when a mailbox reinitialization 162203831d35Sstevel * handshake needs to be scheduled. The handshake can't be performed by the 162303831d35Sstevel * thread that determines it is needed, in most cases, so this function is 162403831d35Sstevel * dispatched on the system-wide taskq pool of threads. Failure is reported but 162503831d35Sstevel * otherwise ignored, since any situation that requires a mailbox initialization 162603831d35Sstevel * handshake will continue to request the handshake until it succeeds. 162703831d35Sstevel */ 162803831d35Sstevel static void 162903831d35Sstevel drmach_mbox_reinit(void *unused) 163003831d35Sstevel { 163103831d35Sstevel _NOTE(ARGUNUSED(unused)) 163203831d35Sstevel 163303831d35Sstevel caddr_t obufp = NULL; 163403831d35Sstevel sbd_error_t *serr = NULL; 163503831d35Sstevel 163603831d35Sstevel DRMACH_PR("scheduled mailbox reinit running\n"); 163703831d35Sstevel 163803831d35Sstevel mutex_enter(&drmach_ri_mbox_mutex); 163903831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 164003831d35Sstevel if (drmach_mbox_iflag == 0) { 164103831d35Sstevel /* need to initialize the mailbox */ 164203831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 164303831d35Sstevel 164403831d35Sstevel cmn_err(CE_NOTE, "!reinitializing DR mailbox"); 164503831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 164603831d35Sstevel serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp, 164703831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0); 164803831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 164903831d35Sstevel 165003831d35Sstevel if (serr) { 165103831d35Sstevel cmn_err(CE_WARN, 165203831d35Sstevel "mbox_init: MBOX_INIT failed ecode=0x%x", 165303831d35Sstevel serr->e_code); 165403831d35Sstevel sbd_err_clear(&serr); 165503831d35Sstevel } 165603831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 165703831d35Sstevel if (!serr) { 165803831d35Sstevel drmach_mbox_iflag = 1; 165903831d35Sstevel } 166003831d35Sstevel } 166103831d35Sstevel drmach_mbox_ipending = 0; 166203831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 166303831d35Sstevel mutex_exit(&drmach_ri_mbox_mutex); 166403831d35Sstevel } 166503831d35Sstevel 166603831d35Sstevel /* 166703831d35Sstevel * To ensure sufficient compatibility with future versions of the DR mailbox 166803831d35Sstevel * protocol, we use a buffer that is large enough to receive the largest message 166903831d35Sstevel * that could possibly be sent to us. However, since that ends up being fairly 167003831d35Sstevel * large, allocating it on the stack is a bad idea. Fortunately, this function 167103831d35Sstevel * does not need to be MT-safe since it is only invoked by the mailbox 167203831d35Sstevel * framework, which will never invoke it multiple times concurrently. Since 167303831d35Sstevel * that is the case, we can use a static buffer. 167403831d35Sstevel */ 167503831d35Sstevel void 167603831d35Sstevel drmach_mbox_event(void) 167703831d35Sstevel { 167803831d35Sstevel static uint8_t buf[DRMACH_MAX_MBOX_MSG_SIZE]; 167903831d35Sstevel dr_mbox_msg_t *msg = (dr_mbox_msg_t *)buf; 168003831d35Sstevel int err; 168103831d35Sstevel uint32_t type = MBOXSC_MSG_EVENT; 168203831d35Sstevel uint32_t command = DRMSG_BOARDEVENT; 168303831d35Sstevel uint64_t transid = 0; 168403831d35Sstevel uint32_t length = DRMACH_MAX_MBOX_MSG_SIZE; 168503831d35Sstevel char *hint = ""; 168603831d35Sstevel int logsys = 0; 168703831d35Sstevel 168803831d35Sstevel do { 1689d3d50737SRafael Vanoni err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid, 1690d3d50737SRafael Vanoni &length, (void *)msg, 0); 169103831d35Sstevel } while (err == EAGAIN); 169203831d35Sstevel 169303831d35Sstevel /* don't try to interpret anything with the wrong version number */ 169403831d35Sstevel if ((err == 0) && (msg->p_hdr.drproto_version != DRMBX_VERSION)) { 169503831d35Sstevel cmn_err(CE_WARN, "mailbox version mismatch 0x%x vs 0x%x", 169603831d35Sstevel msg->p_hdr.drproto_version, DRMBX_VERSION); 169703831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 169803831d35Sstevel drmach_mbox_iflag = 0; 169903831d35Sstevel /* schedule a reinit handshake if one isn't pending */ 170003831d35Sstevel if (!drmach_mbox_ipending) { 170103831d35Sstevel if (taskq_dispatch(system_taskq, drmach_mbox_reinit, 170203831d35Sstevel NULL, TQ_NOSLEEP) != NULL) { 170303831d35Sstevel drmach_mbox_ipending = 1; 170403831d35Sstevel } else { 170503831d35Sstevel cmn_err(CE_WARN, 170603831d35Sstevel "failed to schedule mailbox reinit"); 170703831d35Sstevel } 170803831d35Sstevel } 170903831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 171003831d35Sstevel return; 171103831d35Sstevel } 171203831d35Sstevel 171303831d35Sstevel if ((err != 0) || (msg->p_hdr.reply_status != DRMSG_REPLY_OK)) { 171403831d35Sstevel cmn_err(CE_WARN, 171503831d35Sstevel "Unsolicited mboxsc_getmsg failed: err=0x%x code=0x%x", 171603831d35Sstevel err, msg->p_hdr.error_code); 171703831d35Sstevel } else { 171803831d35Sstevel dr_boardevent_t *be; 171903831d35Sstevel be = (dr_boardevent_t *)&msg->msgdata; 172003831d35Sstevel 172103831d35Sstevel /* check for initialization event */ 172203831d35Sstevel if (be->initialized) { 172303831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 172403831d35Sstevel drmach_mbox_iflag = 0; 172503831d35Sstevel /* schedule a reinit handshake if one isn't pending */ 172603831d35Sstevel if (!drmach_mbox_ipending) { 172703831d35Sstevel if (taskq_dispatch(system_taskq, 172803831d35Sstevel drmach_mbox_reinit, NULL, TQ_NOSLEEP) 172903831d35Sstevel != NULL) { 173003831d35Sstevel drmach_mbox_ipending = 1; 173103831d35Sstevel } else { 1732d3d50737SRafael Vanoni cmn_err(CE_WARN, "failed to schedule " 1733d3d50737SRafael Vanoni "mailbox reinit"); 173403831d35Sstevel } 173503831d35Sstevel } 173603831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 173703831d35Sstevel cmn_err(CE_NOTE, "!Mailbox Init event received"); 173803831d35Sstevel } 173903831d35Sstevel 174003831d35Sstevel /* anything else will be a log_sysevent call */ 174103831d35Sstevel 174203831d35Sstevel if (be->board_insertion) { 174303831d35Sstevel DRMACH_PR("Board Insertion event received"); 174403831d35Sstevel hint = DR_HINT_INSERT; 174503831d35Sstevel logsys++; 174603831d35Sstevel } 174703831d35Sstevel if (be->board_removal) { 174803831d35Sstevel DRMACH_PR("Board Removal event received"); 174903831d35Sstevel hint = DR_HINT_REMOVE; 175003831d35Sstevel logsys++; 175103831d35Sstevel } 175203831d35Sstevel if (be->slot_assign) { 175303831d35Sstevel DRMACH_PR("Slot Assign event received"); 175403831d35Sstevel logsys++; 175503831d35Sstevel } 175603831d35Sstevel if (be->slot_unassign) { 175703831d35Sstevel DRMACH_PR("Slot Unassign event received"); 175803831d35Sstevel logsys++; 175903831d35Sstevel } 176003831d35Sstevel if (be->slot_avail) { 176103831d35Sstevel DRMACH_PR("Slot Available event received"); 176203831d35Sstevel logsys++; 176303831d35Sstevel } 176403831d35Sstevel if (be->slot_unavail) { 176503831d35Sstevel DRMACH_PR("Slot Unavailable event received"); 176603831d35Sstevel logsys++; 176703831d35Sstevel } 176803831d35Sstevel if (be->power_on) { 176903831d35Sstevel DRMACH_PR("Power ON event received"); 177003831d35Sstevel logsys++; 177103831d35Sstevel } 177203831d35Sstevel if (be->power_off) { 177303831d35Sstevel DRMACH_PR("Power OFF event received"); 177403831d35Sstevel logsys++; 177503831d35Sstevel } 177603831d35Sstevel 177703831d35Sstevel if (logsys) 177807d06da5SSurya Prakki (void) drmach_log_sysevent( 177903831d35Sstevel DRMACH_EXPSLOT2BNUM(msg->p_hdr.expbrd, 1780d3d50737SRafael Vanoni msg->p_hdr.slot), hint, SE_NOSLEEP, 1); 178103831d35Sstevel } 178203831d35Sstevel } 178303831d35Sstevel 178403831d35Sstevel static uint32_t 178503831d35Sstevel drmach_get_msgid() 178603831d35Sstevel { 178703831d35Sstevel uint32_t rv; 178803831d35Sstevel mutex_enter(&drmach_msglist_mutex); 178903831d35Sstevel if (!(++drmach_msgid)) 179003831d35Sstevel ++drmach_msgid; 179103831d35Sstevel rv = drmach_msgid; 179203831d35Sstevel mutex_exit(&drmach_msglist_mutex); 179303831d35Sstevel return (rv); 179403831d35Sstevel } 179503831d35Sstevel 179603831d35Sstevel /* 179703831d35Sstevel * unlink an entry from the message transaction list 179803831d35Sstevel * 179903831d35Sstevel * caller must hold drmach_msglist_mutex 180003831d35Sstevel */ 180103831d35Sstevel void 180203831d35Sstevel drmach_msglist_unlink(drmach_msglist_t *entry) 180303831d35Sstevel { 180403831d35Sstevel ASSERT(mutex_owned(&drmach_msglist_mutex)); 180503831d35Sstevel if (entry->prev) { 180603831d35Sstevel entry->prev->next = entry->next; 180703831d35Sstevel if (entry->next) 180803831d35Sstevel entry->next->prev = entry->prev; 180903831d35Sstevel } else { 181003831d35Sstevel drmach_msglist_first = entry->next; 181103831d35Sstevel if (entry->next) 181203831d35Sstevel entry->next->prev = NULL; 181303831d35Sstevel } 181403831d35Sstevel if (entry == drmach_msglist_last) { 181503831d35Sstevel drmach_msglist_last = entry->prev; 181603831d35Sstevel } 181703831d35Sstevel } 181803831d35Sstevel 181903831d35Sstevel void 182003831d35Sstevel drmach_msglist_link(drmach_msglist_t *entry) 182103831d35Sstevel { 182203831d35Sstevel mutex_enter(&drmach_msglist_mutex); 182303831d35Sstevel if (drmach_msglist_last) { 182403831d35Sstevel entry->prev = drmach_msglist_last; 182503831d35Sstevel drmach_msglist_last->next = entry; 182603831d35Sstevel drmach_msglist_last = entry; 182703831d35Sstevel } else { 182803831d35Sstevel drmach_msglist_last = drmach_msglist_first = entry; 182903831d35Sstevel } 183003831d35Sstevel mutex_exit(&drmach_msglist_mutex); 183103831d35Sstevel } 183203831d35Sstevel 183303831d35Sstevel void 183403831d35Sstevel drmach_mbox_getmsg() 183503831d35Sstevel { 183603831d35Sstevel int err; 183703831d35Sstevel register int msgid; 183803831d35Sstevel static uint8_t buf[DRMACH_MAX_MBOX_MSG_SIZE]; 183903831d35Sstevel dr_mbox_msg_t *msg = (dr_mbox_msg_t *)buf; 184003831d35Sstevel dr_proto_hdr_t *php; 184103831d35Sstevel drmach_msglist_t *found, *entry; 184203831d35Sstevel uint32_t type = MBOXSC_MSG_REPLY; 184303831d35Sstevel uint32_t command; 184403831d35Sstevel uint64_t transid; 184503831d35Sstevel uint32_t length; 184603831d35Sstevel 184703831d35Sstevel php = &msg->p_hdr; 184803831d35Sstevel 184903831d35Sstevel while (drmach_getmsg_thread_run != 0) { 185003831d35Sstevel /* get a reply message */ 185103831d35Sstevel command = 0; 185203831d35Sstevel transid = 0; 185303831d35Sstevel length = DRMACH_MAX_MBOX_MSG_SIZE; 1854d3d50737SRafael Vanoni err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid, 1855d3d50737SRafael Vanoni &length, (void *)msg, drmach_to_getmsg); 185603831d35Sstevel 185703831d35Sstevel if (err) { 185803831d35Sstevel /* 185903831d35Sstevel * If mboxsc_getmsg returns ETIMEDOUT or EAGAIN, then 186003831d35Sstevel * the "error" is really just a normal, transient 186103831d35Sstevel * condition and we can retry the operation right away. 186203831d35Sstevel * Any other error suggests a more serious problem, 186303831d35Sstevel * ranging from a message being too big for our buffer 186403831d35Sstevel * (EMSGSIZE) to total failure of the mailbox layer. 186503831d35Sstevel * This second class of errors is much less "transient", 186603831d35Sstevel * so rather than retrying over and over (and getting 186703831d35Sstevel * the same error over and over) as fast as we can, 186803831d35Sstevel * we'll sleep for a while before retrying. 186903831d35Sstevel */ 187003831d35Sstevel if ((err != ETIMEDOUT) && (err != EAGAIN)) { 187103831d35Sstevel cmn_err(CE_WARN, 187203831d35Sstevel "mboxsc_getmsg failed, err=0x%x", err); 187303831d35Sstevel delay(drmach_mbxerr_delay * hz); 187403831d35Sstevel } 187503831d35Sstevel continue; 187603831d35Sstevel } 187703831d35Sstevel 187803831d35Sstevel drmach_mbox_prmsg(msg, 0); 187903831d35Sstevel 188003831d35Sstevel if (php->drproto_version != DRMBX_VERSION) { 188103831d35Sstevel cmn_err(CE_WARN, 188203831d35Sstevel "mailbox version mismatch 0x%x vs 0x%x", 188303831d35Sstevel php->drproto_version, DRMBX_VERSION); 188403831d35Sstevel 188503831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 188603831d35Sstevel drmach_mbox_iflag = 0; 188703831d35Sstevel /* schedule a reinit handshake if one isn't pending */ 188803831d35Sstevel if (!drmach_mbox_ipending) { 188903831d35Sstevel if (taskq_dispatch(system_taskq, 189003831d35Sstevel drmach_mbox_reinit, NULL, TQ_NOSLEEP) 189103831d35Sstevel != NULL) { 189203831d35Sstevel drmach_mbox_ipending = 1; 189303831d35Sstevel } else { 1894d3d50737SRafael Vanoni cmn_err(CE_WARN, "failed to schedule " 1895d3d50737SRafael Vanoni "mailbox reinit"); 189603831d35Sstevel } 189703831d35Sstevel } 189803831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 189903831d35Sstevel 190003831d35Sstevel continue; 190103831d35Sstevel } 190203831d35Sstevel 190303831d35Sstevel msgid = php->message_id; 190403831d35Sstevel found = NULL; 190503831d35Sstevel mutex_enter(&drmach_msglist_mutex); 190603831d35Sstevel entry = drmach_msglist_first; 190703831d35Sstevel while (entry != NULL) { 190803831d35Sstevel if (entry->msgid == msgid) { 190903831d35Sstevel found = entry; 191003831d35Sstevel drmach_msglist_unlink(entry); 191103831d35Sstevel entry = NULL; 191203831d35Sstevel } else 191303831d35Sstevel entry = entry->next; 191403831d35Sstevel } 191503831d35Sstevel 191603831d35Sstevel if (found) { 191703831d35Sstevel mutex_enter(&found->g_lock); 191803831d35Sstevel 191903831d35Sstevel found->e_code = php->error_code; 192003831d35Sstevel if (found->i_buflen > 0) 192103831d35Sstevel bcopy((caddr_t)&msg->msgdata, found->i_buf, 192203831d35Sstevel found->i_buflen); 192303831d35Sstevel found->m_reply = 1; 192403831d35Sstevel 192503831d35Sstevel cv_signal(&found->g_cv); 192603831d35Sstevel mutex_exit(&found->g_lock); 192703831d35Sstevel } else { 192803831d35Sstevel cmn_err(CE_WARN, "!mbox_getmsg: no match for id 0x%x", 192903831d35Sstevel msgid); 193003831d35Sstevel cmn_err(CE_WARN, "! cmd = 0x%x, exb = %d, slot = %d", 193103831d35Sstevel php->command, php->expbrd, php->slot); 193203831d35Sstevel } 193303831d35Sstevel 193403831d35Sstevel mutex_exit(&drmach_msglist_mutex); 193503831d35Sstevel } 193603831d35Sstevel cmn_err(CE_WARN, "mbox_getmsg: exiting"); 193703831d35Sstevel mutex_enter(&drmach_msglist_mutex); 193803831d35Sstevel entry = drmach_msglist_first; 193903831d35Sstevel while (entry != NULL) { 194003831d35Sstevel if (entry->p_flag == 1) { 194103831d35Sstevel entry->f_error = -1; 194203831d35Sstevel mutex_enter(&entry->g_lock); 194303831d35Sstevel cv_signal(&entry->g_cv); 194403831d35Sstevel mutex_exit(&entry->g_lock); 194503831d35Sstevel drmach_msglist_unlink(entry); 194603831d35Sstevel } 194703831d35Sstevel entry = entry->next; 194803831d35Sstevel } 194903831d35Sstevel mutex_exit(&drmach_msglist_mutex); 195003831d35Sstevel drmach_getmsg_thread_run = -1; 195103831d35Sstevel thread_exit(); 195203831d35Sstevel } 195303831d35Sstevel 195403831d35Sstevel void 195503831d35Sstevel drmach_mbox_sendmsg() 195603831d35Sstevel { 195703831d35Sstevel int err, retry; 195803831d35Sstevel drmach_msglist_t *entry; 195903831d35Sstevel dr_mbox_msg_t *mp; 196003831d35Sstevel dr_proto_hdr_t *php; 196103831d35Sstevel 196203831d35Sstevel while (drmach_sendmsg_thread_run != 0) { 196303831d35Sstevel /* 196403831d35Sstevel * Search through the list to find entries awaiting 196503831d35Sstevel * transmission to the SC 196603831d35Sstevel */ 196703831d35Sstevel mutex_enter(&drmach_msglist_mutex); 196803831d35Sstevel entry = drmach_msglist_first; 196903831d35Sstevel retry = 0; 197003831d35Sstevel while (entry != NULL) { 197103831d35Sstevel if (entry->p_flag == 1) { 197203831d35Sstevel entry = entry->next; 197303831d35Sstevel continue; 197403831d35Sstevel } 197503831d35Sstevel 197603831d35Sstevel mutex_exit(&drmach_msglist_mutex); 197703831d35Sstevel 197803831d35Sstevel if (!retry) 197903831d35Sstevel mutex_enter(&entry->s_lock); 198003831d35Sstevel mp = (dr_mbox_msg_t *)entry->o_buf; 198103831d35Sstevel php = &mp->p_hdr; 198203831d35Sstevel 198303831d35Sstevel drmach_mbox_prmsg(mp, 1); 198403831d35Sstevel 198503831d35Sstevel err = mboxsc_putmsg(KEY_DRSC, MBOXSC_MSG_REQUEST, 198603831d35Sstevel php->command, NULL, entry->o_buflen, (void *)mp, 198703831d35Sstevel drmach_to_putmsg); 198803831d35Sstevel 198903831d35Sstevel if (err) { 199003831d35Sstevel switch (err) { 199103831d35Sstevel 199203831d35Sstevel case EAGAIN: 199303831d35Sstevel case EBUSY: 199403831d35Sstevel ++retry; 199503831d35Sstevel mutex_enter(&drmach_msglist_mutex); 199603831d35Sstevel continue; 199703831d35Sstevel 199803831d35Sstevel case ETIMEDOUT: 199903831d35Sstevel if (--entry->o_nretry <= 0) { 200003831d35Sstevel mutex_enter( 200103831d35Sstevel &drmach_msglist_mutex); 200203831d35Sstevel drmach_msglist_unlink(entry); 200303831d35Sstevel mutex_exit( 200403831d35Sstevel &drmach_msglist_mutex); 200503831d35Sstevel entry->f_error = err; 200603831d35Sstevel entry->p_flag = 1; 200703831d35Sstevel cv_signal(&entry->s_cv); 200803831d35Sstevel } else { 200903831d35Sstevel ++retry; 201003831d35Sstevel mutex_enter( 201103831d35Sstevel &drmach_msglist_mutex); 201203831d35Sstevel continue; 201303831d35Sstevel } 201403831d35Sstevel break; 201503831d35Sstevel default: 201603831d35Sstevel mutex_enter(&drmach_msglist_mutex); 201703831d35Sstevel drmach_msglist_unlink(entry); 201803831d35Sstevel mutex_exit(&drmach_msglist_mutex); 201903831d35Sstevel entry->f_error = err; 202003831d35Sstevel entry->p_flag = 1; 202103831d35Sstevel cv_signal(&entry->s_cv); 202203831d35Sstevel break; 202303831d35Sstevel } 202403831d35Sstevel } else { 202503831d35Sstevel entry->p_flag = 1; 202603831d35Sstevel cv_signal(&entry->s_cv); 202703831d35Sstevel } 202803831d35Sstevel 202903831d35Sstevel mutex_exit(&entry->s_lock); 203003831d35Sstevel retry = 0; 203103831d35Sstevel mutex_enter(&drmach_msglist_mutex); 203203831d35Sstevel entry = drmach_msglist_first; 203303831d35Sstevel } 203403831d35Sstevel mutex_exit(&drmach_msglist_mutex); 203503831d35Sstevel 203603831d35Sstevel mutex_enter(&drmach_sendmsg_mutex); 2037d3d50737SRafael Vanoni (void) cv_reltimedwait(&drmach_sendmsg_cv, 2038d3d50737SRafael Vanoni &drmach_sendmsg_mutex, (5 * hz), TR_CLOCK_TICK); 203903831d35Sstevel mutex_exit(&drmach_sendmsg_mutex); 204003831d35Sstevel } 204103831d35Sstevel cmn_err(CE_WARN, "mbox_sendmsg: exiting"); 204203831d35Sstevel mutex_enter(&drmach_msglist_mutex); 204303831d35Sstevel entry = drmach_msglist_first; 204403831d35Sstevel while (entry != NULL) { 204503831d35Sstevel if (entry->p_flag == 0) { 204603831d35Sstevel entry->f_error = -1; 204703831d35Sstevel mutex_enter(&entry->s_lock); 204803831d35Sstevel cv_signal(&entry->s_cv); 204903831d35Sstevel mutex_exit(&entry->s_lock); 205003831d35Sstevel drmach_msglist_unlink(entry); 205103831d35Sstevel } 205203831d35Sstevel entry = entry->next; 205303831d35Sstevel } 205403831d35Sstevel mutex_exit(&drmach_msglist_mutex); 205503831d35Sstevel cv_destroy(&drmach_sendmsg_cv); 205603831d35Sstevel mutex_destroy(&drmach_sendmsg_mutex); 205703831d35Sstevel 205803831d35Sstevel drmach_sendmsg_thread_run = -1; 205903831d35Sstevel thread_exit(); 206003831d35Sstevel } 206103831d35Sstevel 206203831d35Sstevel void 206303831d35Sstevel drmach_msglist_destroy(drmach_msglist_t *listp) 206403831d35Sstevel { 206503831d35Sstevel if (listp != NULL) { 206603831d35Sstevel drmach_msglist_t *entry; 206703831d35Sstevel 206803831d35Sstevel mutex_enter(&drmach_msglist_mutex); 206903831d35Sstevel entry = drmach_msglist_first; 207003831d35Sstevel while (entry) { 207103831d35Sstevel if (listp == entry) { 207203831d35Sstevel drmach_msglist_unlink(listp); 207303831d35Sstevel entry = NULL; 207403831d35Sstevel } else 207503831d35Sstevel entry = entry->next; 207603831d35Sstevel } 207703831d35Sstevel 207803831d35Sstevel mutex_destroy(&listp->s_lock); 207903831d35Sstevel cv_destroy(&listp->s_cv); 208003831d35Sstevel mutex_destroy(&listp->g_lock); 208103831d35Sstevel cv_destroy(&listp->g_cv); 208203831d35Sstevel kmem_free(listp, sizeof (drmach_msglist_t)); 208303831d35Sstevel 208403831d35Sstevel mutex_exit(&drmach_msglist_mutex); 208503831d35Sstevel } 208603831d35Sstevel } 208703831d35Sstevel 208803831d35Sstevel static drmach_msglist_t * 208903831d35Sstevel drmach_msglist_new(caddr_t ibufp, uint32_t ilen, dr_proto_hdr_t *hdrp, 209003831d35Sstevel uint32_t olen, int nrtry) 209103831d35Sstevel { 209203831d35Sstevel drmach_msglist_t *listp; 209303831d35Sstevel 209403831d35Sstevel listp = kmem_zalloc(sizeof (drmach_msglist_t), KM_SLEEP); 209503831d35Sstevel mutex_init(&listp->s_lock, NULL, MUTEX_DRIVER, NULL); 209603831d35Sstevel cv_init(&listp->s_cv, NULL, CV_DRIVER, NULL); 209703831d35Sstevel mutex_init(&listp->g_lock, NULL, MUTEX_DRIVER, NULL); 209803831d35Sstevel cv_init(&listp->g_cv, NULL, CV_DRIVER, NULL); 209903831d35Sstevel listp->o_buf = (caddr_t)hdrp; 210003831d35Sstevel listp->o_buflen = olen; 210103831d35Sstevel listp->i_buf = ibufp; 210203831d35Sstevel listp->i_buflen = ilen; 210303831d35Sstevel listp->o_nretry = nrtry; 210403831d35Sstevel listp->msgid = hdrp->message_id; 210503831d35Sstevel 210603831d35Sstevel return (listp); 210703831d35Sstevel } 210803831d35Sstevel 210903831d35Sstevel static drmach_msglist_t * 211003831d35Sstevel drmach_mbox_req_rply(dr_proto_hdr_t *hdrp, uint32_t olen, caddr_t ibufp, 211103831d35Sstevel uint32_t ilen, int timeout, int nrtry, int nosig, 211203831d35Sstevel drmach_msglist_t *link) 211303831d35Sstevel { 211403831d35Sstevel int crv; 211503831d35Sstevel drmach_msglist_t *listp; 211603831d35Sstevel clock_t to_val; 211703831d35Sstevel dr_proto_hdr_t *php; 211803831d35Sstevel 211903831d35Sstevel /* setup transaction list entry */ 212003831d35Sstevel listp = drmach_msglist_new(ibufp, ilen, hdrp, olen, nrtry); 212103831d35Sstevel 212203831d35Sstevel /* send mailbox message, await reply */ 212303831d35Sstevel mutex_enter(&listp->s_lock); 212403831d35Sstevel mutex_enter(&listp->g_lock); 212503831d35Sstevel 212603831d35Sstevel listp->link = link; 212703831d35Sstevel drmach_msglist_link(listp); 212803831d35Sstevel 212903831d35Sstevel mutex_enter(&drmach_sendmsg_mutex); 213003831d35Sstevel cv_signal(&drmach_sendmsg_cv); 213103831d35Sstevel mutex_exit(&drmach_sendmsg_mutex); 213203831d35Sstevel 213303831d35Sstevel while (listp->p_flag == 0) { 213403831d35Sstevel cv_wait(&listp->s_cv, &listp->s_lock); 213503831d35Sstevel } 213603831d35Sstevel 213703831d35Sstevel to_val = ddi_get_lbolt() + (timeout * hz); 213803831d35Sstevel 213903831d35Sstevel if (listp->f_error) { 214003831d35Sstevel listp->p_flag = 0; 2141d3d50737SRafael Vanoni cmn_err(CE_WARN, "!mboxsc_putmsg failed: 0x%x", listp->f_error); 214203831d35Sstevel php = (dr_proto_hdr_t *)listp->o_buf; 214303831d35Sstevel cmn_err(CE_WARN, "! cmd = 0x%x, exb = %d, slot = %d", 214403831d35Sstevel php->command, php->expbrd, php->slot); 214503831d35Sstevel } else { 214603831d35Sstevel while (listp->m_reply == 0 && listp->f_error == 0) { 214703831d35Sstevel if (nosig) 214803831d35Sstevel crv = cv_timedwait(&listp->g_cv, &listp->g_lock, 214903831d35Sstevel to_val); 215003831d35Sstevel else 215103831d35Sstevel crv = cv_timedwait_sig(&listp->g_cv, 215203831d35Sstevel &listp->g_lock, to_val); 215303831d35Sstevel switch (crv) { 215403831d35Sstevel case -1: /* timed out */ 215503831d35Sstevel cmn_err(CE_WARN, 215603831d35Sstevel "!msgid=0x%x reply timed out", 215703831d35Sstevel hdrp->message_id); 215803831d35Sstevel php = (dr_proto_hdr_t *)listp->o_buf; 215903831d35Sstevel cmn_err(CE_WARN, "! cmd = 0x%x, " 216003831d35Sstevel "exb = %d, slot = %d", php->command, 216103831d35Sstevel php->expbrd, php->slot); 216203831d35Sstevel listp->f_error = ETIMEDOUT; 216303831d35Sstevel break; 216403831d35Sstevel case 0: /* signal received */ 216503831d35Sstevel cmn_err(CE_WARN, 216603831d35Sstevel "operation interrupted by signal"); 216703831d35Sstevel listp->f_error = EINTR; 216803831d35Sstevel break; 216903831d35Sstevel default: 217003831d35Sstevel break; 217103831d35Sstevel } 217203831d35Sstevel } 217303831d35Sstevel 217403831d35Sstevel /* 217503831d35Sstevel * If link is set for this entry, check to see if 217603831d35Sstevel * the linked entry has been replied to. If not, 217703831d35Sstevel * wait for the response. 217803831d35Sstevel * Currently, this is only used for ABORT_TEST functionality, 217903831d35Sstevel * wherein a check is made for the TESTBOARD reply when 218003831d35Sstevel * the ABORT_TEST reply is received. 218103831d35Sstevel */ 218203831d35Sstevel 218303831d35Sstevel if (link) { 218403831d35Sstevel mutex_enter(&link->g_lock); 218503831d35Sstevel /* 218603831d35Sstevel * If the reply to the linked entry hasn't been 218703831d35Sstevel * received, clear the existing link->f_error, 218803831d35Sstevel * and await the reply. 218903831d35Sstevel */ 219003831d35Sstevel if (link->m_reply == 0) { 219103831d35Sstevel link->f_error = 0; 219203831d35Sstevel } 219303831d35Sstevel to_val = ddi_get_lbolt() + (timeout * hz); 219403831d35Sstevel while (link->m_reply == 0 && link->f_error == 0) { 219503831d35Sstevel crv = cv_timedwait(&link->g_cv, &link->g_lock, 219603831d35Sstevel to_val); 219703831d35Sstevel switch (crv) { 219803831d35Sstevel case -1: /* timed out */ 219903831d35Sstevel cmn_err(CE_NOTE, 220003831d35Sstevel "!link msgid=0x%x reply timed out", 220103831d35Sstevel link->msgid); 220203831d35Sstevel link->f_error = ETIMEDOUT; 220303831d35Sstevel break; 220403831d35Sstevel default: 220503831d35Sstevel break; 220603831d35Sstevel } 220703831d35Sstevel } 220803831d35Sstevel mutex_exit(&link->g_lock); 220903831d35Sstevel } 221003831d35Sstevel } 221103831d35Sstevel mutex_exit(&listp->g_lock); 221203831d35Sstevel mutex_exit(&listp->s_lock); 221303831d35Sstevel return (listp); 221403831d35Sstevel } 221503831d35Sstevel 221603831d35Sstevel static sbd_error_t * 221703831d35Sstevel drmach_mbx2sbderr(drmach_msglist_t *mlp) 221803831d35Sstevel { 221903831d35Sstevel char a_pnt[MAXNAMELEN]; 222003831d35Sstevel dr_proto_hdr_t *php; 222103831d35Sstevel int bnum; 222203831d35Sstevel 222303831d35Sstevel if (mlp->f_error) { 222403831d35Sstevel /* 222503831d35Sstevel * If framework failure is due to signal, return "no error" 222603831d35Sstevel * error. 222703831d35Sstevel */ 222803831d35Sstevel if (mlp->f_error == EINTR) 222903831d35Sstevel return (drerr_new(0, ESTC_NONE, NULL)); 223003831d35Sstevel 223103831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 223203831d35Sstevel drmach_mbox_iflag = 0; 223303831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 223403831d35Sstevel if (!mlp->p_flag) 223503831d35Sstevel return (drerr_new(1, ESTC_MBXRQST, NULL)); 223603831d35Sstevel else 223703831d35Sstevel return (drerr_new(1, ESTC_MBXRPLY, NULL)); 223803831d35Sstevel } 223903831d35Sstevel php = (dr_proto_hdr_t *)mlp->o_buf; 224003831d35Sstevel bnum = 2 * php->expbrd + php->slot; 224103831d35Sstevel a_pnt[0] = '\0'; 224203831d35Sstevel (void) drmach_board_name(bnum, a_pnt, MAXNAMELEN); 224303831d35Sstevel 224403831d35Sstevel switch (mlp->e_code) { 224503831d35Sstevel case 0: 224603831d35Sstevel return (NULL); 224703831d35Sstevel case DRERR_NOACL: 224803831d35Sstevel return (drerr_new(0, ESTC_NOACL, "%s", a_pnt)); 224903831d35Sstevel case DRERR_NOT_ASSIGNED: 225003831d35Sstevel return (drerr_new(0, ESTC_NOT_ASSIGNED, "%s", a_pnt)); 225103831d35Sstevel case DRERR_NOT_ACTIVE: 225203831d35Sstevel return (drerr_new(0, ESTC_NOT_ACTIVE, "%s", a_pnt)); 225303831d35Sstevel case DRERR_EMPTY_SLOT: 225403831d35Sstevel return (drerr_new(0, ESTC_EMPTY_SLOT, "%s", a_pnt)); 225503831d35Sstevel case DRERR_POWER_OFF: 225603831d35Sstevel return (drerr_new(0, ESTC_POWER_OFF, "%s", a_pnt)); 225703831d35Sstevel case DRERR_TEST_IN_PROGRESS: 2258d3d50737SRafael Vanoni return (drerr_new(0, ESTC_TEST_IN_PROGRESS, "%s", 2259d3d50737SRafael Vanoni a_pnt)); 226003831d35Sstevel case DRERR_TESTING_BUSY: 226103831d35Sstevel return (drerr_new(0, ESTC_TESTING_BUSY, "%s", a_pnt)); 226203831d35Sstevel case DRERR_TEST_REQUIRED: 226303831d35Sstevel return (drerr_new(0, ESTC_TEST_REQUIRED, "%s", a_pnt)); 226403831d35Sstevel case DRERR_UNAVAILABLE: 226503831d35Sstevel return (drerr_new(0, ESTC_UNAVAILABLE, "%s", a_pnt)); 226603831d35Sstevel case DRERR_RECOVERABLE: 2267d3d50737SRafael Vanoni return (drerr_new(0, ESTC_SMS_ERR_RECOVERABLE, "%s", 2268d3d50737SRafael Vanoni a_pnt)); 226903831d35Sstevel case DRERR_UNRECOVERABLE: 2270d3d50737SRafael Vanoni return (drerr_new(1, ESTC_SMS_ERR_UNRECOVERABLE, "%s", 2271d3d50737SRafael Vanoni a_pnt)); 227203831d35Sstevel default: 227303831d35Sstevel return (drerr_new(1, ESTC_MBOX_UNKNOWN, NULL)); 227403831d35Sstevel } 227503831d35Sstevel } 227603831d35Sstevel 227703831d35Sstevel static sbd_error_t * 227803831d35Sstevel drmach_mbox_trans(uint8_t msgtype, int bnum, caddr_t obufp, int olen, 227903831d35Sstevel caddr_t ibufp, int ilen) 228003831d35Sstevel { 228103831d35Sstevel int timeout = 0; 228203831d35Sstevel int ntries = 0; 228303831d35Sstevel int nosignals = 0; 228403831d35Sstevel dr_proto_hdr_t *hdrp; 228503831d35Sstevel drmach_msglist_t *mlp; 228603831d35Sstevel sbd_error_t *err = NULL; 228703831d35Sstevel 228803831d35Sstevel if (msgtype != DRMSG_MBOX_INIT) { 228903831d35Sstevel mutex_enter(&drmach_ri_mbox_mutex); 229003831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 229103831d35Sstevel if (drmach_mbox_iflag == 0) { 229203831d35Sstevel /* need to initialize the mailbox */ 229303831d35Sstevel dr_proto_hdr_t imsg; 229403831d35Sstevel 229503831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 229603831d35Sstevel 229703831d35Sstevel imsg.command = DRMSG_MBOX_INIT; 229803831d35Sstevel 229903831d35Sstevel imsg.message_id = drmach_get_msgid(); 230003831d35Sstevel imsg.drproto_version = DRMBX_VERSION; 230103831d35Sstevel imsg.expbrd = 0; 230203831d35Sstevel imsg.slot = 0; 230303831d35Sstevel 2304d3d50737SRafael Vanoni cmn_err(CE_WARN, "!reinitializing DR mailbox"); 230503831d35Sstevel mlp = drmach_mbox_req_rply(&imsg, sizeof (imsg), 0, 0, 230603831d35Sstevel 10, 5, 0, NULL); 230703831d35Sstevel err = drmach_mbx2sbderr(mlp); 230803831d35Sstevel /* 230903831d35Sstevel * If framework failure incoming is encountered on 231003831d35Sstevel * the MBOX_INIT [timeout on SMS reply], the error 231103831d35Sstevel * type must be changed before returning to caller. 231203831d35Sstevel * This is to prevent drmach_board_connect() and 231303831d35Sstevel * drmach_board_disconnect() from marking boards 231403831d35Sstevel * UNUSABLE based on MBOX_INIT failures. 231503831d35Sstevel */ 231603831d35Sstevel if ((err != NULL) && (err->e_code == ESTC_MBXRPLY)) { 231703831d35Sstevel cmn_err(CE_WARN, 231803831d35Sstevel "!Changed mbox incoming to outgoing" 231903831d35Sstevel " failure on reinit"); 232003831d35Sstevel sbd_err_clear(&err); 232103831d35Sstevel err = drerr_new(0, ESTC_MBXRQST, NULL); 232203831d35Sstevel } 232303831d35Sstevel drmach_msglist_destroy(mlp); 232403831d35Sstevel if (err) { 232503831d35Sstevel mutex_exit(&drmach_ri_mbox_mutex); 232603831d35Sstevel return (err); 232703831d35Sstevel } 232803831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 232903831d35Sstevel drmach_mbox_iflag = 1; 233003831d35Sstevel } 233103831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 233203831d35Sstevel mutex_exit(&drmach_ri_mbox_mutex); 233303831d35Sstevel } 233403831d35Sstevel 233503831d35Sstevel hdrp = (dr_proto_hdr_t *)obufp; 233603831d35Sstevel 233703831d35Sstevel /* setup outgoing mailbox header */ 233803831d35Sstevel hdrp->command = msgtype; 233903831d35Sstevel hdrp->message_id = drmach_get_msgid(); 234003831d35Sstevel hdrp->drproto_version = DRMBX_VERSION; 234103831d35Sstevel hdrp->expbrd = DRMACH_BNUM2EXP(bnum); 234203831d35Sstevel hdrp->slot = DRMACH_BNUM2SLOT(bnum); 234303831d35Sstevel 234403831d35Sstevel switch (msgtype) { 234503831d35Sstevel 234603831d35Sstevel case DRMSG_MBOX_INIT: 234703831d35Sstevel timeout = drmach_to_mbxinit; 234803831d35Sstevel ntries = 1; 234903831d35Sstevel nosignals = 0; 235003831d35Sstevel break; 235103831d35Sstevel 235203831d35Sstevel case DRMSG_ASSIGN: 235303831d35Sstevel timeout = drmach_to_assign; 235403831d35Sstevel ntries = 1; 235503831d35Sstevel nosignals = 0; 235603831d35Sstevel break; 235703831d35Sstevel 235803831d35Sstevel case DRMSG_UNASSIGN: 235903831d35Sstevel timeout = drmach_to_unassign; 236003831d35Sstevel ntries = 1; 236103831d35Sstevel nosignals = 0; 236203831d35Sstevel break; 236303831d35Sstevel 236403831d35Sstevel case DRMSG_POWERON: 236503831d35Sstevel timeout = drmach_to_poweron; 236603831d35Sstevel ntries = 1; 236703831d35Sstevel nosignals = 0; 236803831d35Sstevel break; 236903831d35Sstevel 237003831d35Sstevel case DRMSG_POWEROFF: 237103831d35Sstevel timeout = drmach_to_poweroff; 237203831d35Sstevel ntries = 1; 237303831d35Sstevel nosignals = 0; 237403831d35Sstevel break; 237503831d35Sstevel 237603831d35Sstevel case DRMSG_SHOWBOARD: 237703831d35Sstevel timeout = drmach_to_showboard; 237803831d35Sstevel ntries = 1; 237903831d35Sstevel nosignals = 0; 238003831d35Sstevel break; 238103831d35Sstevel 238203831d35Sstevel case DRMSG_CLAIM: 238303831d35Sstevel timeout = drmach_to_claim; 238403831d35Sstevel ntries = 1; 238503831d35Sstevel nosignals = 1; 238603831d35Sstevel break; 238703831d35Sstevel 238803831d35Sstevel case DRMSG_UNCLAIM: 238903831d35Sstevel timeout = drmach_to_unclaim; 239003831d35Sstevel ntries = 1; 239103831d35Sstevel nosignals = 1; 239203831d35Sstevel break; 239303831d35Sstevel 239403831d35Sstevel case DRMSG_UNCONFIG: 239503831d35Sstevel timeout = drmach_to_unconfig; 239603831d35Sstevel ntries = 1; 239703831d35Sstevel nosignals = 0; 239803831d35Sstevel break; 239903831d35Sstevel 240003831d35Sstevel case DRMSG_TESTBOARD: 240103831d35Sstevel timeout = drmach_to_testboard; 240203831d35Sstevel ntries = 1; 240303831d35Sstevel nosignals = 0; 240403831d35Sstevel break; 240503831d35Sstevel 240603831d35Sstevel default: 2407d3d50737SRafael Vanoni cmn_err(CE_WARN, "Unknown outgoing message type 0x%x", 2408d3d50737SRafael Vanoni msgtype); 240903831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 241003831d35Sstevel break; 241103831d35Sstevel } 241203831d35Sstevel 241303831d35Sstevel if (err == NULL) { 2414d3d50737SRafael Vanoni mlp = drmach_mbox_req_rply(hdrp, olen, ibufp, ilen, timeout, 2415d3d50737SRafael Vanoni ntries, nosignals, NULL); 241603831d35Sstevel err = drmach_mbx2sbderr(mlp); 241703831d35Sstevel 241803831d35Sstevel /* 241903831d35Sstevel * For DRMSG_TESTBOARD attempts which have timed out, or 242003831d35Sstevel * been aborted due to a signal received after mboxsc_putmsg() 242103831d35Sstevel * has succeeded in sending the message, a DRMSG_ABORT_TEST 242203831d35Sstevel * must be sent. 242303831d35Sstevel */ 242403831d35Sstevel if ((msgtype == DRMSG_TESTBOARD) && (err != NULL) && 242503831d35Sstevel ((mlp->f_error == EINTR) || ((mlp->f_error == ETIMEDOUT) && 242603831d35Sstevel (mlp->p_flag != 0)))) { 242703831d35Sstevel drmach_msglist_t *abmlp; 242803831d35Sstevel dr_abort_test_t abibuf; 242903831d35Sstevel 243003831d35Sstevel hdrp->command = DRMSG_ABORT_TEST; 243103831d35Sstevel hdrp->message_id = drmach_get_msgid(); 243203831d35Sstevel abmlp = drmach_mbox_req_rply(hdrp, 243303831d35Sstevel sizeof (dr_abort_test_t), (caddr_t)&abibuf, 243403831d35Sstevel sizeof (abibuf), drmach_to_aborttest, 5, 1, mlp); 243503831d35Sstevel cmn_err(CE_WARN, "test aborted"); 243603831d35Sstevel drmach_msglist_destroy(abmlp); 243703831d35Sstevel } 243803831d35Sstevel 243903831d35Sstevel drmach_msglist_destroy(mlp); 244003831d35Sstevel } 244103831d35Sstevel 244203831d35Sstevel return (err); 244303831d35Sstevel } 244403831d35Sstevel 244503831d35Sstevel static int 244603831d35Sstevel drmach_mbox_init() 244703831d35Sstevel { 244803831d35Sstevel int err; 244903831d35Sstevel caddr_t obufp; 245003831d35Sstevel sbd_error_t *serr = NULL; 245103831d35Sstevel mboxsc_timeout_range_t mbxtoz; 245203831d35Sstevel 245303831d35Sstevel drmach_mbox_istate = 0; 245403831d35Sstevel /* register the outgoing mailbox */ 245503831d35Sstevel if ((err = mboxsc_init(KEY_DRSC, MBOXSC_MBOX_OUT, 245603831d35Sstevel NULL)) != 0) { 245703831d35Sstevel cmn_err(CE_WARN, "DR - SC mboxsc_init failed: 0x%x", err); 245803831d35Sstevel return (-1); 245903831d35Sstevel } 246003831d35Sstevel drmach_mbox_istate = 1; 246103831d35Sstevel 246203831d35Sstevel /* setup the mboxsc_putmsg timeout value */ 246303831d35Sstevel if (drmach_use_tuned_putmsg_to) { 246403831d35Sstevel cmn_err(CE_NOTE, "!using tuned drmach_to_putmsg = 0x%lx\n", 246503831d35Sstevel drmach_to_putmsg); 246603831d35Sstevel } else { 246703831d35Sstevel if ((err = mboxsc_ctrl(KEY_DRSC, 246803831d35Sstevel MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE, &mbxtoz)) != 0) { 246903831d35Sstevel cmn_err(CE_WARN, "mboxsc_ctrl failed: 0x%x", err); 247003831d35Sstevel drmach_to_putmsg = 60000; 247103831d35Sstevel } else { 247203831d35Sstevel drmach_to_putmsg = mboxsc_putmsg_def_timeout() * 6; 247303831d35Sstevel DRMACH_PR("putmsg range is 0x%lx - 0x%lx value" 247403831d35Sstevel " is 0x%lx\n", mbxtoz.min_timeout, 247503831d35Sstevel mbxtoz.max_timeout, drmach_to_putmsg); 247603831d35Sstevel } 247703831d35Sstevel } 247803831d35Sstevel 247903831d35Sstevel /* register the incoming mailbox */ 248003831d35Sstevel if ((err = mboxsc_init(KEY_SCDR, MBOXSC_MBOX_IN, 248103831d35Sstevel drmach_mbox_event)) != 0) { 248203831d35Sstevel cmn_err(CE_WARN, "SC - DR mboxsc_init failed: 0x%x", err); 248303831d35Sstevel return (-1); 248403831d35Sstevel } 248503831d35Sstevel drmach_mbox_istate = 2; 248603831d35Sstevel 248703831d35Sstevel /* initialize mutex for mailbox globals */ 248803831d35Sstevel mutex_init(&drmach_g_mbox_mutex, NULL, MUTEX_DRIVER, NULL); 248903831d35Sstevel 249003831d35Sstevel /* initialize mutex for mailbox re-init */ 249103831d35Sstevel mutex_init(&drmach_ri_mbox_mutex, NULL, MUTEX_DRIVER, NULL); 249203831d35Sstevel 249303831d35Sstevel /* initialize mailbox message list elements */ 249403831d35Sstevel drmach_msglist_first = drmach_msglist_last = NULL; 249503831d35Sstevel mutex_init(&drmach_msglist_mutex, NULL, MUTEX_DRIVER, NULL); 249603831d35Sstevel 249703831d35Sstevel mutex_init(&drmach_sendmsg_mutex, NULL, MUTEX_DRIVER, NULL); 249803831d35Sstevel cv_init(&drmach_sendmsg_cv, NULL, CV_DRIVER, NULL); 249903831d35Sstevel 250003831d35Sstevel drmach_mbox_istate = 3; 250103831d35Sstevel 250203831d35Sstevel /* start mailbox sendmsg thread */ 250303831d35Sstevel drmach_sendmsg_thread_run = 1; 250403831d35Sstevel if (drmach_sendmsg_thread == NULL) 250503831d35Sstevel drmach_sendmsg_thread = thread_create(NULL, 0, 250603831d35Sstevel (void (*)())drmach_mbox_sendmsg, NULL, 0, &p0, 250703831d35Sstevel TS_RUN, minclsyspri); 250803831d35Sstevel 250903831d35Sstevel /* start mailbox getmsg thread */ 251003831d35Sstevel drmach_getmsg_thread_run = 1; 251103831d35Sstevel if (drmach_getmsg_thread == NULL) 251203831d35Sstevel drmach_getmsg_thread = thread_create(NULL, 0, 251303831d35Sstevel (void (*)())drmach_mbox_getmsg, NULL, 0, &p0, 251403831d35Sstevel TS_RUN, minclsyspri); 251503831d35Sstevel 251603831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 251703831d35Sstevel serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp, 251803831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0); 251903831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 252003831d35Sstevel if (serr) { 252103831d35Sstevel cmn_err(CE_WARN, "mbox_init: MBOX_INIT failed ecode=0x%x", 252203831d35Sstevel serr->e_code); 252303831d35Sstevel sbd_err_clear(&serr); 252403831d35Sstevel return (-1); 252503831d35Sstevel } 252603831d35Sstevel mutex_enter(&drmach_g_mbox_mutex); 252703831d35Sstevel drmach_mbox_iflag = 1; 252803831d35Sstevel drmach_mbox_ipending = 0; 252903831d35Sstevel mutex_exit(&drmach_g_mbox_mutex); 253003831d35Sstevel 253103831d35Sstevel return (0); 253203831d35Sstevel } 253303831d35Sstevel 253403831d35Sstevel static int 253503831d35Sstevel drmach_mbox_fini() 253603831d35Sstevel { 253703831d35Sstevel int err, rv = 0; 253803831d35Sstevel 253903831d35Sstevel if (drmach_mbox_istate > 2) { 254003831d35Sstevel drmach_getmsg_thread_run = 0; 254103831d35Sstevel drmach_sendmsg_thread_run = 0; 254203831d35Sstevel cmn_err(CE_WARN, 254303831d35Sstevel "drmach_mbox_fini: waiting for mbox threads..."); 254403831d35Sstevel while ((drmach_getmsg_thread_run == 0) || 254503831d35Sstevel (drmach_sendmsg_thread_run == 0)) { 254603831d35Sstevel continue; 254703831d35Sstevel } 2548d3d50737SRafael Vanoni cmn_err(CE_WARN, "drmach_mbox_fini: mbox threads done."); 254903831d35Sstevel mutex_destroy(&drmach_msglist_mutex); 255003831d35Sstevel 255103831d35Sstevel } 255203831d35Sstevel if (drmach_mbox_istate) { 255303831d35Sstevel /* de-register the outgoing mailbox */ 255403831d35Sstevel if ((err = mboxsc_fini(KEY_DRSC)) != 0) { 255503831d35Sstevel cmn_err(CE_WARN, "DR - SC mboxsc_fini failed: 0x%x", 255603831d35Sstevel err); 255703831d35Sstevel rv = -1; 255803831d35Sstevel } 255903831d35Sstevel } 256003831d35Sstevel if (drmach_mbox_istate > 1) { 256103831d35Sstevel /* de-register the incoming mailbox */ 256203831d35Sstevel if ((err = mboxsc_fini(KEY_SCDR)) != 0) { 256303831d35Sstevel cmn_err(CE_WARN, "SC - DR mboxsc_fini failed: 0x%x", 256403831d35Sstevel err); 256503831d35Sstevel rv = -1; 256603831d35Sstevel } 256703831d35Sstevel } 256803831d35Sstevel mutex_destroy(&drmach_g_mbox_mutex); 256903831d35Sstevel mutex_destroy(&drmach_ri_mbox_mutex); 257003831d35Sstevel return (rv); 257103831d35Sstevel } 257203831d35Sstevel 257303831d35Sstevel static int 257403831d35Sstevel drmach_portid2bnum(int portid) 257503831d35Sstevel { 257603831d35Sstevel int slot; 257703831d35Sstevel 257803831d35Sstevel switch (portid & 0x1f) { 257903831d35Sstevel case 0: case 1: case 2: case 3: /* cpu/wci devices */ 258003831d35Sstevel case 0x1e: /* slot 0 axq registers */ 258103831d35Sstevel slot = 0; 258203831d35Sstevel break; 258303831d35Sstevel 258403831d35Sstevel case 8: case 9: /* cpu devices */ 258503831d35Sstevel case 0x1c: case 0x1d: /* schizo/wci devices */ 258603831d35Sstevel case 0x1f: /* slot 1 axq registers */ 258703831d35Sstevel slot = 1; 258803831d35Sstevel break; 258903831d35Sstevel 259003831d35Sstevel default: 259103831d35Sstevel ASSERT(0); /* catch in debug kernels */ 259203831d35Sstevel } 259303831d35Sstevel 259403831d35Sstevel return (((portid >> 4) & 0x7e) | slot); 259503831d35Sstevel } 259603831d35Sstevel 259703831d35Sstevel extern int axq_suspend_iopause; 259803831d35Sstevel 259903831d35Sstevel static int 260003831d35Sstevel hold_rele_branch(dev_info_t *rdip, void *arg) 260103831d35Sstevel { 260203831d35Sstevel int i; 260303831d35Sstevel int *holdp = (int *)arg; 260403831d35Sstevel char *name = ddi_node_name(rdip); 260503831d35Sstevel 260603831d35Sstevel /* 260703831d35Sstevel * For Starcat, we must be children of the root devinfo node 260803831d35Sstevel */ 260903831d35Sstevel ASSERT(ddi_get_parent(rdip) == ddi_root_node()); 261003831d35Sstevel 261103831d35Sstevel i = drmach_name2type_idx(name); 261203831d35Sstevel 261303831d35Sstevel /* 261403831d35Sstevel * Only children of the root devinfo node need to be 261503831d35Sstevel * held/released since they are the only valid targets 261603831d35Sstevel * of tree operations. This corresponds to the node types 261703831d35Sstevel * listed in the drmach_name2type array. 261803831d35Sstevel */ 261903831d35Sstevel if (i < 0) { 262003831d35Sstevel /* Not of interest to us */ 262103831d35Sstevel return (DDI_WALK_PRUNECHILD); 262203831d35Sstevel } 262303831d35Sstevel 262403831d35Sstevel if (*holdp) { 262503831d35Sstevel ASSERT(!e_ddi_branch_held(rdip)); 262603831d35Sstevel e_ddi_branch_hold(rdip); 262703831d35Sstevel } else { 262803831d35Sstevel ASSERT(e_ddi_branch_held(rdip)); 262903831d35Sstevel e_ddi_branch_rele(rdip); 263003831d35Sstevel } 263103831d35Sstevel 263203831d35Sstevel return (DDI_WALK_PRUNECHILD); 263303831d35Sstevel } 263403831d35Sstevel 263503831d35Sstevel static int 263603831d35Sstevel drmach_init(void) 263703831d35Sstevel { 263803831d35Sstevel pnode_t nodeid; 263903831d35Sstevel gdcd_t *gdcd; 264003831d35Sstevel int bnum; 264103831d35Sstevel dev_info_t *rdip; 264203831d35Sstevel int hold, circ; 264303831d35Sstevel 264403831d35Sstevel mutex_enter(&drmach_i_lock); 264503831d35Sstevel if (drmach_initialized) { 264603831d35Sstevel mutex_exit(&drmach_i_lock); 264703831d35Sstevel return (0); 264803831d35Sstevel } 264903831d35Sstevel 265003831d35Sstevel gdcd = drmach_gdcd_new(); 265103831d35Sstevel if (gdcd == NULL) { 265203831d35Sstevel mutex_exit(&drmach_i_lock); 265303831d35Sstevel cmn_err(CE_WARN, "drmach_init: failed to access GDCD\n"); 265403831d35Sstevel return (-1); 265503831d35Sstevel } 265603831d35Sstevel 265703831d35Sstevel drmach_boards = drmach_array_new(0, MAX_BOARDS - 1); 265803831d35Sstevel 265903831d35Sstevel nodeid = prom_childnode(prom_rootnode()); 266003831d35Sstevel do { 266103831d35Sstevel int len; 266203831d35Sstevel int portid; 266303831d35Sstevel drmachid_t id; 266403831d35Sstevel 266503831d35Sstevel len = prom_getproplen(nodeid, "portid"); 266603831d35Sstevel if (len != sizeof (portid)) 266703831d35Sstevel continue; 266803831d35Sstevel 266903831d35Sstevel portid = -1; 267003831d35Sstevel (void) prom_getprop(nodeid, "portid", (caddr_t)&portid); 267103831d35Sstevel if (portid == -1) 267203831d35Sstevel continue; 267303831d35Sstevel 267403831d35Sstevel bnum = drmach_portid2bnum(portid); 267503831d35Sstevel 267603831d35Sstevel if (drmach_array_get(drmach_boards, bnum, &id) == -1) { 267703831d35Sstevel /* portid translated to an invalid board number */ 267803831d35Sstevel cmn_err(CE_WARN, "OBP node 0x%x has" 267903831d35Sstevel " invalid property value, %s=%u", 268003831d35Sstevel nodeid, "portid", portid); 268103831d35Sstevel 268203831d35Sstevel /* clean up */ 268303831d35Sstevel drmach_array_dispose(drmach_boards, 268403831d35Sstevel drmach_board_dispose); 268503831d35Sstevel drmach_gdcd_dispose(gdcd); 268603831d35Sstevel mutex_exit(&drmach_i_lock); 268703831d35Sstevel return (-1); 268803831d35Sstevel } else if (id == NULL) { 268903831d35Sstevel drmach_board_t *bp; 269003831d35Sstevel l1_slot_stat_t *dcd; 269103831d35Sstevel int exp, slot; 269203831d35Sstevel 269303831d35Sstevel bp = drmach_board_new(bnum); 269403831d35Sstevel bp->assigned = !drmach_initialized; 269503831d35Sstevel bp->powered = !drmach_initialized; 269603831d35Sstevel 269703831d35Sstevel exp = DRMACH_BNUM2EXP(bnum); 269803831d35Sstevel slot = DRMACH_BNUM2SLOT(bnum); 269903831d35Sstevel dcd = &gdcd->dcd_slot[exp][slot]; 270003831d35Sstevel bp->stardrb_offset = 270103831d35Sstevel dcd->l1ss_cpu_drblock_xwd_offset << 3; 270203831d35Sstevel DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name, 270303831d35Sstevel bp->stardrb_offset); 270403831d35Sstevel 270503831d35Sstevel if (gdcd->dcd_slot[exp][slot].l1ss_flags & 270603831d35Sstevel L1SSFLG_THIS_L1_NULL_PROC_LPA) { 270703831d35Sstevel bp->flags |= DRMACH_NULL_PROC_LPA; 270803831d35Sstevel DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name); 270903831d35Sstevel } 271003831d35Sstevel } 271103831d35Sstevel } while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE); 271203831d35Sstevel 271303831d35Sstevel drmach_cpu_sram_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP); 271403831d35Sstevel 271503831d35Sstevel if (gdcd->dcd_testcage_log2_mbytes_size != DCD_DR_TESTCAGE_DISABLED) { 271603831d35Sstevel ASSERT(gdcd->dcd_testcage_log2_mbytes_size == 271703831d35Sstevel gdcd->dcd_testcage_log2_mbytes_align); 271803831d35Sstevel drmach_iocage_paddr = 271903831d35Sstevel (uint64_t)gdcd->dcd_testcage_mbyte_PA << 20; 272003831d35Sstevel drmach_iocage_size = 272103831d35Sstevel 1 << (gdcd->dcd_testcage_log2_mbytes_size + 20); 272203831d35Sstevel 272303831d35Sstevel drmach_iocage_vaddr = (caddr_t)vmem_alloc(heap_arena, 272403831d35Sstevel drmach_iocage_size, VM_SLEEP); 272503831d35Sstevel hat_devload(kas.a_hat, drmach_iocage_vaddr, drmach_iocage_size, 272603831d35Sstevel mmu_btop(drmach_iocage_paddr), 272703831d35Sstevel PROT_READ | PROT_WRITE, 272803831d35Sstevel HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST); 272903831d35Sstevel 273003831d35Sstevel DRMACH_PR("gdcd size=0x%x align=0x%x PA=0x%x\n", 273103831d35Sstevel gdcd->dcd_testcage_log2_mbytes_size, 273203831d35Sstevel gdcd->dcd_testcage_log2_mbytes_align, 273303831d35Sstevel gdcd->dcd_testcage_mbyte_PA); 273403831d35Sstevel DRMACH_PR("drmach size=0x%x PA=0x%lx VA=0x%p\n", 273503831d35Sstevel drmach_iocage_size, drmach_iocage_paddr, 273607d06da5SSurya Prakki (void *)drmach_iocage_vaddr); 273703831d35Sstevel } 273803831d35Sstevel 273903831d35Sstevel if (drmach_iocage_size == 0) { 274003831d35Sstevel drmach_array_dispose(drmach_boards, drmach_board_dispose); 274103831d35Sstevel drmach_boards = NULL; 274203831d35Sstevel vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE); 274303831d35Sstevel drmach_gdcd_dispose(gdcd); 274403831d35Sstevel mutex_exit(&drmach_i_lock); 274503831d35Sstevel cmn_err(CE_WARN, "drmach_init: iocage not available\n"); 274603831d35Sstevel return (-1); 274703831d35Sstevel } 274803831d35Sstevel 274903831d35Sstevel drmach_gdcd_dispose(gdcd); 275003831d35Sstevel 275103831d35Sstevel mutex_init(&drmach_iocage_lock, NULL, MUTEX_DRIVER, NULL); 275203831d35Sstevel cv_init(&drmach_iocage_cv, NULL, CV_DRIVER, NULL); 275303831d35Sstevel mutex_init(&drmach_xt_mb_lock, NULL, MUTEX_DRIVER, NULL); 275403831d35Sstevel mutex_init(&drmach_bus_sync_lock, NULL, MUTEX_DRIVER, NULL); 275503831d35Sstevel mutex_init(&drmach_slice_table_lock, NULL, MUTEX_DRIVER, NULL); 275603831d35Sstevel 275703831d35Sstevel mutex_enter(&cpu_lock); 275803831d35Sstevel mutex_enter(&drmach_iocage_lock); 275903831d35Sstevel ASSERT(drmach_iocage_is_busy == 0); 276003831d35Sstevel drmach_iocage_is_busy = 1; 276103831d35Sstevel drmach_iocage_mem_scrub(drmach_iocage_size); 276203831d35Sstevel drmach_iocage_is_busy = 0; 276303831d35Sstevel cv_signal(&drmach_iocage_cv); 276403831d35Sstevel mutex_exit(&drmach_iocage_lock); 276503831d35Sstevel mutex_exit(&cpu_lock); 276603831d35Sstevel 276703831d35Sstevel 276803831d35Sstevel if (drmach_mbox_init() == -1) { 276903831d35Sstevel cmn_err(CE_WARN, "DR - SC mailbox initialization Failed"); 277003831d35Sstevel } 277103831d35Sstevel 277203831d35Sstevel /* 277303831d35Sstevel * Walk immediate children of devinfo root node and hold 277403831d35Sstevel * all devinfo branches of interest. 277503831d35Sstevel */ 277603831d35Sstevel hold = 1; 277703831d35Sstevel rdip = ddi_root_node(); 277803831d35Sstevel 277903831d35Sstevel ndi_devi_enter(rdip, &circ); 278003831d35Sstevel ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold); 278103831d35Sstevel ndi_devi_exit(rdip, circ); 278203831d35Sstevel 278303831d35Sstevel drmach_initialized = 1; 278403831d35Sstevel 278503831d35Sstevel /* 278603831d35Sstevel * To avoid a circular patch dependency between DR and AXQ, the AXQ 278703831d35Sstevel * rev introducing the axq_iopause_*_all interfaces should not regress 278803831d35Sstevel * when installed without the DR rev using those interfaces. The default 278903831d35Sstevel * is for iopause to be enabled/disabled during axq suspend/resume. By 279003831d35Sstevel * setting the following axq flag to zero, axq will not enable iopause 279103831d35Sstevel * during suspend/resume, instead DR will call the axq_iopause_*_all 279203831d35Sstevel * interfaces during drmach_copy_rename. 279303831d35Sstevel */ 279403831d35Sstevel axq_suspend_iopause = 0; 279503831d35Sstevel 279603831d35Sstevel mutex_exit(&drmach_i_lock); 279703831d35Sstevel 279803831d35Sstevel return (0); 279903831d35Sstevel } 280003831d35Sstevel 280103831d35Sstevel static void 280203831d35Sstevel drmach_fini(void) 280303831d35Sstevel { 280403831d35Sstevel dev_info_t *rdip; 280503831d35Sstevel int hold, circ; 280603831d35Sstevel 280703831d35Sstevel if (drmach_initialized) { 280803831d35Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER); 280903831d35Sstevel drmach_array_dispose(drmach_boards, drmach_board_dispose); 281003831d35Sstevel drmach_boards = NULL; 281103831d35Sstevel rw_exit(&drmach_boards_rwlock); 281203831d35Sstevel 281303831d35Sstevel mutex_destroy(&drmach_slice_table_lock); 281403831d35Sstevel mutex_destroy(&drmach_xt_mb_lock); 281503831d35Sstevel mutex_destroy(&drmach_bus_sync_lock); 281603831d35Sstevel cv_destroy(&drmach_iocage_cv); 281703831d35Sstevel mutex_destroy(&drmach_iocage_lock); 281803831d35Sstevel 281903831d35Sstevel vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE); 282003831d35Sstevel 282103831d35Sstevel /* 282203831d35Sstevel * Walk immediate children of the root devinfo node 282303831d35Sstevel * releasing holds acquired on branches in drmach_init() 282403831d35Sstevel */ 282503831d35Sstevel hold = 0; 282603831d35Sstevel rdip = ddi_root_node(); 282703831d35Sstevel 282803831d35Sstevel ndi_devi_enter(rdip, &circ); 282903831d35Sstevel ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold); 283003831d35Sstevel ndi_devi_exit(rdip, circ); 283103831d35Sstevel 283203831d35Sstevel drmach_initialized = 0; 283303831d35Sstevel } 283403831d35Sstevel 283507d06da5SSurya Prakki (void) drmach_mbox_fini(); 283603831d35Sstevel if (drmach_xt_mb != NULL) { 283703831d35Sstevel vmem_free(static_alloc_arena, (void *)drmach_xt_mb, 283803831d35Sstevel drmach_xt_mb_size); 283903831d35Sstevel } 284003831d35Sstevel rw_destroy(&drmach_boards_rwlock); 284103831d35Sstevel mutex_destroy(&drmach_i_lock); 284203831d35Sstevel } 284303831d35Sstevel 284403831d35Sstevel static void 284503831d35Sstevel drmach_mem_read_madr(drmach_mem_t *mp, int bank, uint64_t *madr) 284603831d35Sstevel { 284703831d35Sstevel kpreempt_disable(); 284803831d35Sstevel 284903831d35Sstevel /* get register address, read madr value */ 285003831d35Sstevel if (STARCAT_CPUID_TO_PORTID(CPU->cpu_id) == mp->dev.portid) { 285103831d35Sstevel *madr = lddmcdecode(DRMACH_MC_ASI_ADDR(mp, bank)); 285203831d35Sstevel } else { 285303831d35Sstevel *madr = lddphysio(DRMACH_MC_ADDR(mp, bank)); 285403831d35Sstevel } 285503831d35Sstevel 285603831d35Sstevel kpreempt_enable(); 285703831d35Sstevel } 285803831d35Sstevel 285903831d35Sstevel 286003831d35Sstevel static uint64_t * 286103831d35Sstevel drmach_prep_mc_rename(uint64_t *p, int local, 286203831d35Sstevel drmach_mem_t *mp, uint64_t current_basepa, uint64_t new_basepa) 286303831d35Sstevel { 286403831d35Sstevel int bank; 286503831d35Sstevel 286603831d35Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) { 286703831d35Sstevel uint64_t madr, bank_offset; 286803831d35Sstevel 286903831d35Sstevel /* fetch mc's bank madr register value */ 287003831d35Sstevel drmach_mem_read_madr(mp, bank, &madr); 287103831d35Sstevel if (madr & DRMACH_MC_VALID_MASK) { 287203831d35Sstevel uint64_t bankpa; 287303831d35Sstevel 287403831d35Sstevel bank_offset = (DRMACH_MC_UM_TO_PA(madr) | 287503831d35Sstevel DRMACH_MC_LM_TO_PA(madr)) - current_basepa; 287603831d35Sstevel bankpa = new_basepa + bank_offset; 287703831d35Sstevel 287803831d35Sstevel /* encode new base pa into madr */ 287903831d35Sstevel madr &= ~DRMACH_MC_UM_MASK; 288003831d35Sstevel madr |= DRMACH_MC_PA_TO_UM(bankpa); 288103831d35Sstevel madr &= ~DRMACH_MC_LM_MASK; 288203831d35Sstevel madr |= DRMACH_MC_PA_TO_LM(bankpa); 288303831d35Sstevel 288403831d35Sstevel if (local) 288503831d35Sstevel *p++ = DRMACH_MC_ASI_ADDR(mp, bank); 288603831d35Sstevel else 288703831d35Sstevel *p++ = DRMACH_MC_ADDR(mp, bank); 288803831d35Sstevel 288903831d35Sstevel *p++ = madr; 289003831d35Sstevel } 289103831d35Sstevel } 289203831d35Sstevel 289303831d35Sstevel return (p); 289403831d35Sstevel } 289503831d35Sstevel 289603831d35Sstevel static uint64_t * 289703831d35Sstevel drmach_prep_schizo_script(uint64_t *p, drmach_mem_t *mp, uint64_t new_basepa) 289803831d35Sstevel { 289903831d35Sstevel drmach_board_t *bp; 290003831d35Sstevel int rv; 290103831d35Sstevel int idx; 290203831d35Sstevel drmachid_t id; 290303831d35Sstevel uint64_t last_scsr_pa = 0; 290403831d35Sstevel 290503831d35Sstevel /* memory is always in slot 0 */ 290603831d35Sstevel ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0); 290703831d35Sstevel 290803831d35Sstevel /* look up slot 1 board on same expander */ 290903831d35Sstevel idx = DRMACH_EXPSLOT2BNUM(DRMACH_BNUM2EXP(mp->dev.bp->bnum), 1); 291003831d35Sstevel rv = drmach_array_get(drmach_boards, idx, &id); 291103831d35Sstevel bp = id; /* bp will be NULL if board not found */ 291203831d35Sstevel 291303831d35Sstevel /* look up should never be out of bounds */ 291403831d35Sstevel ASSERT(rv == 0); 291503831d35Sstevel 291603831d35Sstevel /* nothing to do when board is not found or has no devices */ 291703831d35Sstevel if (rv == -1 || bp == NULL || bp->devices == NULL) 291803831d35Sstevel return (p); 291903831d35Sstevel 292003831d35Sstevel rv = drmach_array_first(bp->devices, &idx, &id); 292103831d35Sstevel while (rv == 0) { 292203831d35Sstevel if (DRMACH_IS_IO_ID(id)) { 292303831d35Sstevel drmach_io_t *io = id; 292403831d35Sstevel 292503831d35Sstevel /* 292603831d35Sstevel * Skip all non-Schizo IO devices (only IO nodes 292703831d35Sstevel * that are Schizo devices have non-zero scsr_pa). 292803831d35Sstevel * Filter out "other" leaf to avoid writing to the 292903831d35Sstevel * same Schizo Control/Status Register twice. 293003831d35Sstevel */ 293103831d35Sstevel if (io->scsr_pa && io->scsr_pa != last_scsr_pa) { 293203831d35Sstevel uint64_t scsr; 293303831d35Sstevel 293403831d35Sstevel scsr = lddphysio(io->scsr_pa); 293503831d35Sstevel scsr &= ~(DRMACH_LPA_BASE_MASK | 293603831d35Sstevel DRMACH_LPA_BND_MASK); 293703831d35Sstevel scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa); 293803831d35Sstevel scsr |= DRMACH_PA_TO_LPA_BND( 293903831d35Sstevel new_basepa + DRMACH_MEM_SLICE_SIZE); 294003831d35Sstevel 294103831d35Sstevel *p++ = io->scsr_pa; 294203831d35Sstevel *p++ = scsr; 294303831d35Sstevel 294403831d35Sstevel last_scsr_pa = io->scsr_pa; 294503831d35Sstevel } 294603831d35Sstevel } 294703831d35Sstevel rv = drmach_array_next(bp->devices, &idx, &id); 294803831d35Sstevel } 294903831d35Sstevel 295003831d35Sstevel return (p); 295103831d35Sstevel } 295203831d35Sstevel 295303831d35Sstevel /* 295403831d35Sstevel * For Panther MCs, append the MC idle reg address and drmach_mem_t pointer. 295503831d35Sstevel * The latter is returned when drmach_rename fails to idle a Panther MC and 295603831d35Sstevel * is used to identify the MC for error reporting. 295703831d35Sstevel */ 295803831d35Sstevel static uint64_t * 295903831d35Sstevel drmach_prep_pn_mc_idle(uint64_t *p, drmach_mem_t *mp, int local) 296003831d35Sstevel { 296103831d35Sstevel /* only slot 0 has memory */ 296203831d35Sstevel ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0); 296303831d35Sstevel ASSERT(IS_PANTHER(mp->dev.bp->cpu_impl)); 296403831d35Sstevel 296503831d35Sstevel for (mp = mp->dev.bp->mem; mp != NULL; mp = mp->next) { 296603831d35Sstevel ASSERT(DRMACH_IS_MEM_ID(mp)); 296703831d35Sstevel 296803831d35Sstevel if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) { 296903831d35Sstevel if (local) { 297003831d35Sstevel *p++ = ASI_EMU_ACT_STATUS_VA; /* local ASI */ 297103831d35Sstevel *p++ = (uintptr_t)mp; 297203831d35Sstevel } 297303831d35Sstevel } else if (!local) { 297403831d35Sstevel *p++ = DRMACH_EMU_ACT_STATUS_ADDR(mp); /* PIO */ 297503831d35Sstevel *p++ = (uintptr_t)mp; 297603831d35Sstevel } 297703831d35Sstevel } 297803831d35Sstevel 297903831d35Sstevel return (p); 298003831d35Sstevel } 298103831d35Sstevel 298203831d35Sstevel static sbd_error_t * 298303831d35Sstevel drmach_prep_rename_script(drmach_mem_t *s_mp, drmach_mem_t *t_mp, 298403831d35Sstevel uint64_t t_slice_offset, caddr_t buf, int buflen) 298503831d35Sstevel { 298603831d35Sstevel _NOTE(ARGUNUSED(buflen)) 298703831d35Sstevel 298803831d35Sstevel uint64_t *p = (uint64_t *)buf, *q; 298903831d35Sstevel sbd_error_t *err; 299003831d35Sstevel int rv; 299103831d35Sstevel drmach_mem_t *mp, *skip_mp; 299203831d35Sstevel uint64_t s_basepa, t_basepa; 299303831d35Sstevel uint64_t s_new_basepa, t_new_basepa; 299403831d35Sstevel 299503831d35Sstevel /* verify supplied buffer space is adequate */ 299603831d35Sstevel ASSERT(buflen >= 299703831d35Sstevel /* addr for all possible MC banks */ 299803831d35Sstevel (sizeof (uint64_t) * 4 * 4 * 18) + 299903831d35Sstevel /* list section terminator */ 300003831d35Sstevel (sizeof (uint64_t) * 1) + 300103831d35Sstevel /* addr/id tuple for local Panther MC idle reg */ 300203831d35Sstevel (sizeof (uint64_t) * 2) + 300303831d35Sstevel /* list section terminator */ 300403831d35Sstevel (sizeof (uint64_t) * 1) + 300503831d35Sstevel /* addr/id tuple for 2 boards with 4 Panther MC idle regs */ 300603831d35Sstevel (sizeof (uint64_t) * 2 * 2 * 4) + 300703831d35Sstevel /* list section terminator */ 300803831d35Sstevel (sizeof (uint64_t) * 1) + 300903831d35Sstevel /* addr/val tuple for 1 proc with 4 MC banks */ 301003831d35Sstevel (sizeof (uint64_t) * 2 * 4) + 301103831d35Sstevel /* list section terminator */ 301203831d35Sstevel (sizeof (uint64_t) * 1) + 301303831d35Sstevel /* addr/val tuple for 2 boards w/ 2 schizos each */ 301403831d35Sstevel (sizeof (uint64_t) * 2 * 2 * 2) + 301503831d35Sstevel /* addr/val tuple for 2 boards w/ 16 MC banks each */ 301603831d35Sstevel (sizeof (uint64_t) * 2 * 2 * 16) + 301703831d35Sstevel /* list section terminator */ 301803831d35Sstevel (sizeof (uint64_t) * 1) + 301903831d35Sstevel /* addr/val tuple for 18 AXQs w/ two slots each */ 302003831d35Sstevel (sizeof (uint64_t) * 2 * 2 * 18) + 302103831d35Sstevel /* list section terminator */ 302203831d35Sstevel (sizeof (uint64_t) * 1) + 302303831d35Sstevel /* list terminator */ 302403831d35Sstevel (sizeof (uint64_t) * 1)); 302503831d35Sstevel 302603831d35Sstevel /* copy bank list to rename script */ 302703831d35Sstevel mutex_enter(&drmach_bus_sync_lock); 302803831d35Sstevel for (q = drmach_bus_sync_list; *q; q++, p++) 302903831d35Sstevel *p = *q; 303003831d35Sstevel mutex_exit(&drmach_bus_sync_lock); 303103831d35Sstevel 303203831d35Sstevel /* list section terminator */ 303303831d35Sstevel *p++ = 0; 303403831d35Sstevel 303503831d35Sstevel /* 303603831d35Sstevel * Write idle script for MC on this processor. A script will be 303703831d35Sstevel * produced only if this is a Panther processor on the source or 303803831d35Sstevel * target board. 303903831d35Sstevel */ 304003831d35Sstevel if (IS_PANTHER(s_mp->dev.bp->cpu_impl)) 304103831d35Sstevel p = drmach_prep_pn_mc_idle(p, s_mp, 1); 304203831d35Sstevel 304303831d35Sstevel if (IS_PANTHER(t_mp->dev.bp->cpu_impl)) 304403831d35Sstevel p = drmach_prep_pn_mc_idle(p, t_mp, 1); 304503831d35Sstevel 304603831d35Sstevel /* list section terminator */ 304703831d35Sstevel *p++ = 0; 304803831d35Sstevel 304903831d35Sstevel /* 305003831d35Sstevel * Write idle script for all other MCs on source and target 305103831d35Sstevel * Panther boards. 305203831d35Sstevel */ 305303831d35Sstevel if (IS_PANTHER(s_mp->dev.bp->cpu_impl)) 305403831d35Sstevel p = drmach_prep_pn_mc_idle(p, s_mp, 0); 305503831d35Sstevel 305603831d35Sstevel if (IS_PANTHER(t_mp->dev.bp->cpu_impl)) 305703831d35Sstevel p = drmach_prep_pn_mc_idle(p, t_mp, 0); 305803831d35Sstevel 305903831d35Sstevel /* list section terminator */ 306003831d35Sstevel *p++ = 0; 306103831d35Sstevel 306203831d35Sstevel /* 306303831d35Sstevel * Step 1: Write source base address to target MC 306403831d35Sstevel * with present bit off. 306503831d35Sstevel * Step 2: Now rewrite target reg with present bit on. 306603831d35Sstevel */ 306703831d35Sstevel err = drmach_mem_get_base_physaddr(s_mp, &s_basepa); 306803831d35Sstevel ASSERT(err == NULL); 306903831d35Sstevel err = drmach_mem_get_base_physaddr(t_mp, &t_basepa); 307003831d35Sstevel ASSERT(err == NULL); 307103831d35Sstevel 307203831d35Sstevel /* exchange base pa. include slice offset in new target base pa */ 307303831d35Sstevel s_new_basepa = t_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1); 307403831d35Sstevel t_new_basepa = (s_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1)) + 307503831d35Sstevel t_slice_offset; 307603831d35Sstevel 307703831d35Sstevel DRMACH_PR("s_new_basepa 0x%lx\n", s_new_basepa); 307803831d35Sstevel DRMACH_PR("t_new_basepa 0x%lx\n", t_new_basepa); 307903831d35Sstevel 308003831d35Sstevel DRMACH_PR("preparing MC MADR rename script (master is CPU%d):\n", 308103831d35Sstevel CPU->cpu_id); 308203831d35Sstevel 308303831d35Sstevel /* 308403831d35Sstevel * Write rename script for MC on this processor. A script will 308503831d35Sstevel * be produced only if this processor is on the source or target 308603831d35Sstevel * board. 308703831d35Sstevel */ 308803831d35Sstevel 308903831d35Sstevel skip_mp = NULL; 309003831d35Sstevel mp = s_mp->dev.bp->mem; 309103831d35Sstevel while (mp != NULL && skip_mp == NULL) { 309203831d35Sstevel if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) { 309303831d35Sstevel skip_mp = mp; 309403831d35Sstevel p = drmach_prep_mc_rename(p, 1, mp, s_basepa, 309503831d35Sstevel s_new_basepa); 309603831d35Sstevel } 309703831d35Sstevel 309803831d35Sstevel mp = mp->next; 309903831d35Sstevel } 310003831d35Sstevel 310103831d35Sstevel mp = t_mp->dev.bp->mem; 310203831d35Sstevel while (mp != NULL && skip_mp == NULL) { 310303831d35Sstevel if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) { 310403831d35Sstevel skip_mp = mp; 310503831d35Sstevel p = drmach_prep_mc_rename(p, 1, mp, t_basepa, 310603831d35Sstevel t_new_basepa); 310703831d35Sstevel } 310803831d35Sstevel 310903831d35Sstevel mp = mp->next; 311003831d35Sstevel } 311103831d35Sstevel 311203831d35Sstevel /* list section terminator */ 311303831d35Sstevel *p++ = 0; 311403831d35Sstevel 311503831d35Sstevel /* 311603831d35Sstevel * Write rename script for all other MCs on source and target 311703831d35Sstevel * boards. 311803831d35Sstevel */ 311903831d35Sstevel 312003831d35Sstevel for (mp = s_mp->dev.bp->mem; mp; mp = mp->next) { 312103831d35Sstevel if (mp == skip_mp) 312203831d35Sstevel continue; 312303831d35Sstevel p = drmach_prep_mc_rename(p, 0, mp, s_basepa, s_new_basepa); 312403831d35Sstevel } 312503831d35Sstevel 312603831d35Sstevel for (mp = t_mp->dev.bp->mem; mp; mp = mp->next) { 312703831d35Sstevel if (mp == skip_mp) 312803831d35Sstevel continue; 312903831d35Sstevel p = drmach_prep_mc_rename(p, 0, mp, t_basepa, t_new_basepa); 313003831d35Sstevel } 313103831d35Sstevel 313203831d35Sstevel /* Write rename script for Schizo LPA_BASE/LPA_BND */ 313303831d35Sstevel p = drmach_prep_schizo_script(p, s_mp, s_new_basepa); 313403831d35Sstevel p = drmach_prep_schizo_script(p, t_mp, t_new_basepa); 313503831d35Sstevel 313603831d35Sstevel /* list section terminator */ 313703831d35Sstevel *p++ = 0; 313803831d35Sstevel 313903831d35Sstevel DRMACH_PR("preparing AXQ CASM rename script (EXP%d <> EXP%d):\n", 314003831d35Sstevel DRMACH_BNUM2EXP(s_mp->dev.bp->bnum), 314103831d35Sstevel DRMACH_BNUM2EXP(t_mp->dev.bp->bnum)); 314203831d35Sstevel 314303831d35Sstevel rv = axq_do_casm_rename_script(&p, 314403831d35Sstevel DRMACH_PA_TO_SLICE(s_new_basepa), 314503831d35Sstevel DRMACH_PA_TO_SLICE(t_new_basepa)); 314603831d35Sstevel if (rv == DDI_FAILURE) 314703831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 314803831d35Sstevel 314903831d35Sstevel /* list section & final terminator */ 315003831d35Sstevel *p++ = 0; 315103831d35Sstevel *p++ = 0; 315203831d35Sstevel 315303831d35Sstevel #ifdef DEBUG 315403831d35Sstevel { 315503831d35Sstevel uint64_t *q = (uint64_t *)buf; 315603831d35Sstevel 315703831d35Sstevel /* paranoia */ 315803831d35Sstevel ASSERT((caddr_t)p <= buf + buflen); 315903831d35Sstevel 316003831d35Sstevel DRMACH_PR("MC bank base pa list:\n"); 316103831d35Sstevel while (*q) { 316203831d35Sstevel uint64_t a = *q++; 316303831d35Sstevel 316403831d35Sstevel DRMACH_PR("0x%lx\n", a); 316503831d35Sstevel } 316603831d35Sstevel 316703831d35Sstevel /* skip terminator */ 316803831d35Sstevel q += 1; 316903831d35Sstevel 317003831d35Sstevel DRMACH_PR("local Panther MC idle reg (via ASI 0x4a):\n"); 317103831d35Sstevel while (*q) { 317203831d35Sstevel DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1)); 317303831d35Sstevel q += 2; 317403831d35Sstevel } 317503831d35Sstevel 317603831d35Sstevel /* skip terminator */ 317703831d35Sstevel q += 1; 317803831d35Sstevel 317903831d35Sstevel DRMACH_PR("non-local Panther MC idle reg (via ASI 0x15):\n"); 318003831d35Sstevel while (*q) { 318103831d35Sstevel DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1)); 318203831d35Sstevel q += 2; 318303831d35Sstevel } 318403831d35Sstevel 318503831d35Sstevel /* skip terminator */ 318603831d35Sstevel q += 1; 318703831d35Sstevel 318803831d35Sstevel DRMACH_PR("MC reprogramming script (via ASI 0x72):\n"); 318903831d35Sstevel while (*q) { 319003831d35Sstevel uint64_t r = *q++; /* register address */ 319103831d35Sstevel uint64_t v = *q++; /* new register value */ 319203831d35Sstevel 319303831d35Sstevel DRMACH_PR("0x%lx = 0x%lx, basepa 0x%lx\n", 319407d06da5SSurya Prakki r, v, (long)(DRMACH_MC_UM_TO_PA(v)| 319507d06da5SSurya Prakki DRMACH_MC_LM_TO_PA(v))); 319603831d35Sstevel } 319703831d35Sstevel 319803831d35Sstevel /* skip terminator */ 319903831d35Sstevel q += 1; 320003831d35Sstevel 320103831d35Sstevel DRMACH_PR("MC/SCHIZO reprogramming script:\n"); 320203831d35Sstevel while (*q) { 320303831d35Sstevel DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1)); 320403831d35Sstevel q += 2; 320503831d35Sstevel } 320603831d35Sstevel 320703831d35Sstevel /* skip terminator */ 320803831d35Sstevel q += 1; 320903831d35Sstevel 321003831d35Sstevel DRMACH_PR("AXQ reprogramming script:\n"); 321103831d35Sstevel while (*q) { 321203831d35Sstevel DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1)); 321303831d35Sstevel q += 2; 321403831d35Sstevel } 321503831d35Sstevel 321603831d35Sstevel /* verify final terminator is present */ 321703831d35Sstevel ASSERT(*(q + 1) == 0); 321803831d35Sstevel 321907d06da5SSurya Prakki DRMACH_PR("copy-rename script 0x%p, len %d\n", 322007d06da5SSurya Prakki (void *)buf, (int)((intptr_t)p - (intptr_t)buf)); 322103831d35Sstevel 322203831d35Sstevel if (drmach_debug) 322303831d35Sstevel DELAY(10000000); 322403831d35Sstevel } 322503831d35Sstevel #endif 322603831d35Sstevel 322703831d35Sstevel return (NULL); 322803831d35Sstevel } 322903831d35Sstevel 323003831d35Sstevel static void 323103831d35Sstevel drmach_prep_xt_mb_for_slice_update(drmach_board_t *bp, uchar_t slice) 323203831d35Sstevel { 323303831d35Sstevel int rv; 323403831d35Sstevel 323503831d35Sstevel ASSERT(MUTEX_HELD(&drmach_xt_mb_lock)); 323603831d35Sstevel 323703831d35Sstevel if (bp->devices) { 323803831d35Sstevel int d_idx; 323903831d35Sstevel drmachid_t d_id; 324003831d35Sstevel 324103831d35Sstevel rv = drmach_array_first(bp->devices, &d_idx, &d_id); 324203831d35Sstevel while (rv == 0) { 324303831d35Sstevel if (DRMACH_IS_CPU_ID(d_id)) { 324403831d35Sstevel drmach_cpu_t *cp = d_id; 324503831d35Sstevel processorid_t cpuid = cp->cpuid; 324603831d35Sstevel 324703831d35Sstevel mutex_enter(&cpu_lock); 324803831d35Sstevel if (cpu[cpuid] && cpu[cpuid]->cpu_flags) 324903831d35Sstevel drmach_xt_mb[cpuid] = 0x80 | slice; 325003831d35Sstevel mutex_exit(&cpu_lock); 325103831d35Sstevel } 325203831d35Sstevel rv = drmach_array_next(bp->devices, &d_idx, &d_id); 325303831d35Sstevel } 325403831d35Sstevel } 325503831d35Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 0) { 325603831d35Sstevel drmach_board_t *s1bp = NULL; 325703831d35Sstevel 325803831d35Sstevel rv = drmach_array_get(drmach_boards, bp->bnum + 1, 325903831d35Sstevel (void *) &s1bp); 326003831d35Sstevel if (rv == 0 && s1bp != NULL) { 326103831d35Sstevel ASSERT(DRMACH_IS_BOARD_ID(s1bp)); 326203831d35Sstevel ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1); 326303831d35Sstevel drmach_prep_xt_mb_for_slice_update(s1bp, slice); 326403831d35Sstevel } 326503831d35Sstevel } 326603831d35Sstevel } 326703831d35Sstevel 326803831d35Sstevel sbd_error_t * 326903831d35Sstevel drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset, 327003831d35Sstevel drmachid_t s_id, struct memlist *c_ml, drmachid_t *cr_id) 327103831d35Sstevel { 327203831d35Sstevel extern void drmach_rename(uint64_t *, uint_t *, uint64_t *); 327303831d35Sstevel extern void drmach_rename_end(void); 327403831d35Sstevel 327503831d35Sstevel drmach_mem_t *s_mp, *t_mp; 327603831d35Sstevel struct memlist *x_ml; 327703831d35Sstevel uint64_t off_mask, s_copybasepa, t_copybasepa, t_basepa; 327803831d35Sstevel int len; 327903831d35Sstevel caddr_t bp, wp; 328003831d35Sstevel uint_t *p, *q; 328103831d35Sstevel sbd_error_t *err; 328203831d35Sstevel tte_t *tte; 328303831d35Sstevel drmach_copy_rename_t *cr; 328403831d35Sstevel 328503831d35Sstevel if (!DRMACH_IS_MEM_ID(s_id)) 328603831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 328703831d35Sstevel if (!DRMACH_IS_MEM_ID(t_id)) 328803831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 328903831d35Sstevel s_mp = s_id; 329003831d35Sstevel t_mp = t_id; 329103831d35Sstevel 329203831d35Sstevel /* get starting physical address of target memory */ 329303831d35Sstevel err = drmach_mem_get_base_physaddr(t_id, &t_basepa); 329403831d35Sstevel if (err) 329503831d35Sstevel return (err); 329603831d35Sstevel 329703831d35Sstevel /* calculate slice offset mask from slice size */ 329803831d35Sstevel off_mask = DRMACH_MEM_SLICE_SIZE - 1; 329903831d35Sstevel 330003831d35Sstevel /* calculate source and target base pa */ 330156f33205SJonathan Adams s_copybasepa = c_ml->ml_address; 330256f33205SJonathan Adams t_copybasepa = 330356f33205SJonathan Adams t_basepa + ((c_ml->ml_address & off_mask) - t_slice_offset); 330403831d35Sstevel 330503831d35Sstevel /* paranoia */ 330656f33205SJonathan Adams ASSERT((c_ml->ml_address & off_mask) >= t_slice_offset); 330703831d35Sstevel 330803831d35Sstevel /* adjust copy memlist addresses to be relative to copy base pa */ 330903831d35Sstevel x_ml = c_ml; 331003831d35Sstevel while (x_ml != NULL) { 331156f33205SJonathan Adams x_ml->ml_address -= s_copybasepa; 331256f33205SJonathan Adams x_ml = x_ml->ml_next; 331303831d35Sstevel } 331403831d35Sstevel 331503831d35Sstevel #ifdef DEBUG 331603831d35Sstevel { 331703831d35Sstevel uint64_t s_basepa, s_size, t_size; 331803831d35Sstevel 331903831d35Sstevel x_ml = c_ml; 332056f33205SJonathan Adams while (x_ml->ml_next != NULL) 332156f33205SJonathan Adams x_ml = x_ml->ml_next; 332203831d35Sstevel 332303831d35Sstevel DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n", 332403831d35Sstevel s_copybasepa, 332556f33205SJonathan Adams s_copybasepa + x_ml->ml_address + x_ml->ml_size); 332603831d35Sstevel 332703831d35Sstevel DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n", 332803831d35Sstevel t_copybasepa, 332956f33205SJonathan Adams t_copybasepa + x_ml->ml_address + x_ml->ml_size); 333003831d35Sstevel 333103831d35Sstevel DRMACH_PR("copy memlist (relative to copy base pa):\n"); 333203831d35Sstevel DRMACH_MEMLIST_DUMP(c_ml); 333303831d35Sstevel 333403831d35Sstevel err = drmach_mem_get_base_physaddr(s_id, &s_basepa); 333503831d35Sstevel ASSERT(err == NULL); 333603831d35Sstevel 333703831d35Sstevel err = drmach_mem_get_size(s_id, &s_size); 333803831d35Sstevel ASSERT(err == NULL); 333903831d35Sstevel 334003831d35Sstevel err = drmach_mem_get_size(t_id, &t_size); 334103831d35Sstevel ASSERT(err == NULL); 334203831d35Sstevel 334303831d35Sstevel DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n", 334403831d35Sstevel s_basepa, s_size); 334503831d35Sstevel DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n", 334603831d35Sstevel t_basepa, t_size); 334703831d35Sstevel } 334803831d35Sstevel #endif /* DEBUG */ 334903831d35Sstevel 335003831d35Sstevel /* Map in appropriate cpu sram page */ 335103831d35Sstevel tte = &drmach_cpu_sram_tte[CPU->cpu_id]; 335203831d35Sstevel ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) && 335303831d35Sstevel TTE_IS_PRIVILEGED(tte) && TTE_IS_LOCKED(tte)); 33541e2e7a75Shuah sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte); 33551e2e7a75Shuah sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte); 335603831d35Sstevel 335703831d35Sstevel bp = wp = drmach_cpu_sram_va; 335803831d35Sstevel 335903831d35Sstevel /* Make sure the rename routine will fit */ 336003831d35Sstevel len = (ptrdiff_t)drmach_rename_end - (ptrdiff_t)drmach_rename; 336103831d35Sstevel ASSERT(wp + len < bp + PAGESIZE); 336203831d35Sstevel 336303831d35Sstevel /* copy text. standard bcopy not designed to work in nc space */ 336403831d35Sstevel p = (uint_t *)wp; 336503831d35Sstevel q = (uint_t *)drmach_rename; 336603831d35Sstevel while (q < (uint_t *)drmach_rename_end) 336703831d35Sstevel *p++ = *q++; 336803831d35Sstevel 336903831d35Sstevel /* zero remainder. standard bzero not designed to work in nc space */ 337003831d35Sstevel while (p < (uint_t *)(bp + PAGESIZE)) 337103831d35Sstevel *p++ = 0; 337203831d35Sstevel 337307d06da5SSurya Prakki DRMACH_PR("drmach_rename function 0x%p, len %d\n", (void *)wp, len); 337403831d35Sstevel wp += (len + 15) & ~15; 337503831d35Sstevel 3376d3d50737SRafael Vanoni err = drmach_prep_rename_script(s_mp, t_mp, t_slice_offset, wp, 3377d3d50737SRafael Vanoni PAGESIZE - (wp - bp)); 337803831d35Sstevel if (err) { 337903831d35Sstevel cleanup: 338003831d35Sstevel xt_one(CPU->cpu_id, vtag_flushpage_tl1, 33811e2e7a75Shuah (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup); 338203831d35Sstevel return (err); 338303831d35Sstevel } 338403831d35Sstevel 338503831d35Sstevel /* disable and flush CDC */ 338603831d35Sstevel if (axq_cdc_disable_flush_all() != DDI_SUCCESS) { 338703831d35Sstevel axq_cdc_enable_all(); /* paranoia */ 338803831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 338903831d35Sstevel goto cleanup; 339003831d35Sstevel } 339103831d35Sstevel 339203831d35Sstevel /* mark both memory units busy */ 339303831d35Sstevel t_mp->dev.busy++; 339403831d35Sstevel s_mp->dev.busy++; 339503831d35Sstevel 339603831d35Sstevel cr = vmem_alloc(static_alloc_arena, sizeof (drmach_copy_rename_t), 339703831d35Sstevel VM_SLEEP); 339803831d35Sstevel cr->isa = (void *)drmach_copy_rename_init; 339903831d35Sstevel cr->data = wp; 340003831d35Sstevel cr->c_ml = c_ml; 340103831d35Sstevel cr->s_mp = s_mp; 340203831d35Sstevel cr->t_mp = t_mp; 340303831d35Sstevel cr->s_copybasepa = s_copybasepa; 340403831d35Sstevel cr->t_copybasepa = t_copybasepa; 340503831d35Sstevel cr->ecode = DRMACH_CR_OK; 340603831d35Sstevel 340703831d35Sstevel mutex_enter(&drmach_slice_table_lock); 340803831d35Sstevel 340903831d35Sstevel mutex_enter(&drmach_xt_mb_lock); 341003831d35Sstevel bzero((void *)drmach_xt_mb, drmach_xt_mb_size); 341103831d35Sstevel 341203831d35Sstevel if (DRMACH_L1_SET_LPA(s_mp->dev.bp) && drmach_reprogram_lpa) { 341303831d35Sstevel drmach_prep_xt_mb_for_slice_update(s_mp->dev.bp, 341403831d35Sstevel DRMACH_PA_TO_SLICE(t_copybasepa)); 341503831d35Sstevel } 341603831d35Sstevel if (DRMACH_L1_SET_LPA(t_mp->dev.bp) && drmach_reprogram_lpa) { 341703831d35Sstevel drmach_prep_xt_mb_for_slice_update(t_mp->dev.bp, 341803831d35Sstevel DRMACH_PA_TO_SLICE(s_copybasepa)); 341903831d35Sstevel } 342003831d35Sstevel 342103831d35Sstevel *cr_id = cr; 342203831d35Sstevel return (NULL); 342303831d35Sstevel } 342403831d35Sstevel 342503831d35Sstevel int drmach_rename_count; 342603831d35Sstevel int drmach_rename_ntries; 342703831d35Sstevel 342803831d35Sstevel sbd_error_t * 342903831d35Sstevel drmach_copy_rename_fini(drmachid_t id) 343003831d35Sstevel { 343103831d35Sstevel drmach_copy_rename_t *cr = id; 343203831d35Sstevel sbd_error_t *err = NULL; 343303831d35Sstevel dr_mbox_msg_t *obufp; 343403831d35Sstevel 343503831d35Sstevel ASSERT(cr->isa == (void *)drmach_copy_rename_init); 343603831d35Sstevel 343703831d35Sstevel axq_cdc_enable_all(); 343803831d35Sstevel 343903831d35Sstevel xt_one(CPU->cpu_id, vtag_flushpage_tl1, 34401e2e7a75Shuah (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup); 344103831d35Sstevel 344203831d35Sstevel switch (cr->ecode) { 344303831d35Sstevel case DRMACH_CR_OK: 344403831d35Sstevel break; 344503831d35Sstevel case DRMACH_CR_MC_IDLE_ERR: { 344603831d35Sstevel dev_info_t *dip = NULL; 344703831d35Sstevel drmach_mem_t *mp = (drmach_mem_t *)cr->earg; 344803831d35Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 344903831d35Sstevel 345003831d35Sstevel ASSERT(DRMACH_IS_MEM_ID(mp)); 345103831d35Sstevel 345203831d35Sstevel err = drmach_get_dip(mp, &dip); 345303831d35Sstevel 345403831d35Sstevel ASSERT(err == NULL); 345503831d35Sstevel ASSERT(dip != NULL); 345603831d35Sstevel 345703831d35Sstevel err = drerr_new(0, ESBD_MEMFAIL, NULL); 345803831d35Sstevel (void) ddi_pathname(dip, path); 345903831d35Sstevel cmn_err(CE_WARN, "failed to idle memory controller %s on %s: " 346003831d35Sstevel "copy-rename aborted", path, mp->dev.bp->cm.name); 346103831d35Sstevel kmem_free(path, MAXPATHLEN); 346203831d35Sstevel break; 346303831d35Sstevel } 346403831d35Sstevel case DRMACH_CR_IOPAUSE_ERR: 346503831d35Sstevel ASSERT((uintptr_t)cr->earg >= 0 && 346603831d35Sstevel (uintptr_t)cr->earg < AXQ_MAX_EXP); 346703831d35Sstevel 346803831d35Sstevel err = drerr_new(0, ESBD_SUSPEND, "EX%d", (uintptr_t)cr->earg); 346903831d35Sstevel cmn_err(CE_WARN, "failed to idle EX%ld AXQ slot1 activity prior" 347003831d35Sstevel " to copy-rename", (uintptr_t)cr->earg); 347103831d35Sstevel break; 347203831d35Sstevel case DRMACH_CR_ONTRAP_ERR: 347303831d35Sstevel err = drerr_new(0, ESBD_MEMFAIL, NULL); 347403831d35Sstevel cmn_err(CE_WARN, "copy-rename aborted due to uncorrectable " 347503831d35Sstevel "memory error"); 347603831d35Sstevel break; 347703831d35Sstevel default: 347803831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 347903831d35Sstevel cmn_err(CE_WARN, "unknown copy-rename error code (%d)\n", 348003831d35Sstevel cr->ecode); 348103831d35Sstevel break; 348203831d35Sstevel } 348303831d35Sstevel 348403831d35Sstevel #ifdef DEBUG 348503831d35Sstevel if ((DRMACH_L1_SET_LPA(cr->s_mp->dev.bp) || 348603831d35Sstevel DRMACH_L1_SET_LPA(cr->t_mp->dev.bp)) && drmach_reprogram_lpa) { 348703831d35Sstevel int i; 348803831d35Sstevel for (i = 0; i < NCPU; i++) { 348903831d35Sstevel if (drmach_xt_mb[i]) 349003831d35Sstevel DRMACH_PR("cpu%d ignored drmach_xt_mb", i); 349103831d35Sstevel } 349203831d35Sstevel } 349303831d35Sstevel #endif 349403831d35Sstevel mutex_exit(&drmach_xt_mb_lock); 349503831d35Sstevel 349603831d35Sstevel if (cr->c_ml != NULL) 349703831d35Sstevel memlist_delete(cr->c_ml); 349803831d35Sstevel 349903831d35Sstevel cr->t_mp->dev.busy--; 350003831d35Sstevel cr->s_mp->dev.busy--; 350103831d35Sstevel 350203831d35Sstevel if (err) { 350303831d35Sstevel mutex_exit(&drmach_slice_table_lock); 350403831d35Sstevel goto done; 350503831d35Sstevel } 350603831d35Sstevel 350703831d35Sstevel /* update casm shadow for target and source board */ 350803831d35Sstevel drmach_slice_table_update(cr->t_mp->dev.bp, 0); 350903831d35Sstevel drmach_slice_table_update(cr->s_mp->dev.bp, 0); 351003831d35Sstevel mutex_exit(&drmach_slice_table_lock); 351103831d35Sstevel 351203831d35Sstevel mutex_enter(&drmach_bus_sync_lock); 351303831d35Sstevel drmach_bus_sync_list_update(); 351403831d35Sstevel mutex_exit(&drmach_bus_sync_lock); 351503831d35Sstevel 351603831d35Sstevel /* 351703831d35Sstevel * Make a good-faith effort to notify the SC about the copy-rename, but 351803831d35Sstevel * don't worry if it fails, since a subsequent claim/unconfig/unclaim 351903831d35Sstevel * will duplicate the update. 352003831d35Sstevel */ 352103831d35Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP); 352203831d35Sstevel mutex_enter(&drmach_slice_table_lock); 352303831d35Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_uc.mem_slice); 352403831d35Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_uc.mem_regs); 352503831d35Sstevel mutex_exit(&drmach_slice_table_lock); 352603831d35Sstevel (void) drmach_mbox_trans(DRMSG_UNCONFIG, cr->s_mp->dev.bp->bnum, 352703831d35Sstevel (caddr_t)obufp, sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0); 352803831d35Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t)); 352903831d35Sstevel 353003831d35Sstevel done: 353103831d35Sstevel vmem_free(static_alloc_arena, cr, sizeof (drmach_copy_rename_t)); 353203831d35Sstevel 353303831d35Sstevel DRMACH_PR("waited %d out of %d tries for drmach_rename_wait on %d cpus", 353403831d35Sstevel drmach_rename_ntries, drmach_cpu_ntries, drmach_rename_count); 353503831d35Sstevel 353603831d35Sstevel return (err); 353703831d35Sstevel } 353803831d35Sstevel 353903831d35Sstevel int drmach_slow_copy = 0; 354003831d35Sstevel 354103831d35Sstevel void 354203831d35Sstevel drmach_copy_rename(drmachid_t id) 354303831d35Sstevel { 354403831d35Sstevel extern uint_t getpstate(void); 354503831d35Sstevel extern void setpstate(uint_t); 354603831d35Sstevel 354703831d35Sstevel extern xcfunc_t drmach_rename_wait; 354803831d35Sstevel extern xcfunc_t drmach_rename_done; 354903831d35Sstevel extern xcfunc_t drmach_rename_abort; 355003831d35Sstevel 355103831d35Sstevel drmach_copy_rename_t *cr = id; 355203831d35Sstevel uint64_t neer; 355303831d35Sstevel struct memlist *ml; 355403831d35Sstevel int i, count; 355503831d35Sstevel int csize, lnsize; 355603831d35Sstevel uint64_t caddr; 355703831d35Sstevel cpuset_t cpuset; 355803831d35Sstevel uint_t pstate; 355903831d35Sstevel uint32_t exp = 0; 356003831d35Sstevel on_trap_data_t otd; 356103831d35Sstevel xcfunc_t *drmach_end_wait_xcall = drmach_rename_done; 356203831d35Sstevel 356303831d35Sstevel ASSERT(cr->isa == (void *)drmach_copy_rename_init); 356403831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 356503831d35Sstevel ASSERT(cr->ecode == DRMACH_CR_OK); 356603831d35Sstevel 356703831d35Sstevel /* 356803831d35Sstevel * Prevent slot1 IO from accessing Safari memory bus. 356903831d35Sstevel */ 357003831d35Sstevel if (axq_iopause_enable_all(&exp) != DDI_SUCCESS) { 357103831d35Sstevel ASSERT(exp >= 0 && exp < AXQ_MAX_EXP); 357203831d35Sstevel cr->ecode = DRMACH_CR_IOPAUSE_ERR; 357303831d35Sstevel cr->earg = (void *)(uintptr_t)exp; 357403831d35Sstevel return; 357503831d35Sstevel } 357603831d35Sstevel 357703831d35Sstevel cpuset = cpu_ready_set; 357803831d35Sstevel CPUSET_DEL(cpuset, CPU->cpu_id); 357903831d35Sstevel count = ncpus - 1; 358003831d35Sstevel drmach_rename_count = count; /* for debug */ 358103831d35Sstevel 358203831d35Sstevel drmach_xt_ready = 0; 358303831d35Sstevel xt_some(cpuset, drmach_rename_wait, NULL, NULL); 358403831d35Sstevel 358503831d35Sstevel for (i = 0; i < drmach_cpu_ntries; i++) { 358603831d35Sstevel if (drmach_xt_ready == count) 358703831d35Sstevel break; 358803831d35Sstevel DELAY(drmach_cpu_delay); 358903831d35Sstevel } 359003831d35Sstevel 359103831d35Sstevel drmach_rename_ntries = i; /* for debug */ 359203831d35Sstevel 359303831d35Sstevel drmach_xt_ready = 0; /* steal the line back */ 359403831d35Sstevel for (i = 0; i < NCPU; i++) /* steal the line back, preserve data */ 359503831d35Sstevel drmach_xt_mb[i] = drmach_xt_mb[i]; 359603831d35Sstevel 359703831d35Sstevel caddr = drmach_iocage_paddr; 359803831d35Sstevel csize = cpunodes[CPU->cpu_id].ecache_size; 359903831d35Sstevel lnsize = cpunodes[CPU->cpu_id].ecache_linesize; 360003831d35Sstevel 360103831d35Sstevel /* disable CE reporting */ 360203831d35Sstevel neer = get_error_enable(); 360303831d35Sstevel set_error_enable(neer & ~EN_REG_CEEN); 360403831d35Sstevel 360503831d35Sstevel /* disable interrupts (paranoia) */ 360603831d35Sstevel pstate = getpstate(); 360703831d35Sstevel setpstate(pstate & ~PSTATE_IE); 360803831d35Sstevel 360903831d35Sstevel /* 361003831d35Sstevel * Execute copy-rename under on_trap to protect against a panic due 361103831d35Sstevel * to an uncorrectable error. Instead, DR will abort the copy-rename 361203831d35Sstevel * operation and rely on the OS to do the error reporting. 361303831d35Sstevel * 361403831d35Sstevel * In general, trap handling on any cpu once the copy begins 361503831d35Sstevel * can result in an inconsistent memory image on the target. 361603831d35Sstevel */ 361703831d35Sstevel if (on_trap(&otd, OT_DATA_EC)) { 361803831d35Sstevel cr->ecode = DRMACH_CR_ONTRAP_ERR; 361903831d35Sstevel goto copy_rename_end; 362003831d35Sstevel } 362103831d35Sstevel 362203831d35Sstevel /* 362303831d35Sstevel * DO COPY. 362403831d35Sstevel */ 362556f33205SJonathan Adams for (ml = cr->c_ml; ml; ml = ml->ml_next) { 362603831d35Sstevel uint64_t s_pa, t_pa; 362703831d35Sstevel uint64_t nbytes; 362803831d35Sstevel 362956f33205SJonathan Adams s_pa = cr->s_copybasepa + ml->ml_address; 363056f33205SJonathan Adams t_pa = cr->t_copybasepa + ml->ml_address; 363156f33205SJonathan Adams nbytes = ml->ml_size; 363203831d35Sstevel 363303831d35Sstevel while (nbytes != 0ull) { 363403831d35Sstevel /* copy 32 bytes at src_pa to dst_pa */ 363503831d35Sstevel bcopy32_il(s_pa, t_pa); 363603831d35Sstevel 363703831d35Sstevel /* increment by 32 bytes */ 363803831d35Sstevel s_pa += (4 * sizeof (uint64_t)); 363903831d35Sstevel t_pa += (4 * sizeof (uint64_t)); 364003831d35Sstevel 364103831d35Sstevel /* decrement by 32 bytes */ 364203831d35Sstevel nbytes -= (4 * sizeof (uint64_t)); 364303831d35Sstevel 364403831d35Sstevel if (drmach_slow_copy) { /* for debug */ 364503831d35Sstevel uint64_t i = 13 * 50; 3646f500b196SRichard Bean while (i--) 3647f500b196SRichard Bean ; 364803831d35Sstevel } 364903831d35Sstevel } 365003831d35Sstevel } 365103831d35Sstevel 365203831d35Sstevel /* 365303831d35Sstevel * XXX CHEETAH SUPPORT 365403831d35Sstevel * For cheetah, we need to grab the iocage lock since iocage 365503831d35Sstevel * memory is used for e$ flush. 365603831d35Sstevel * 365703831d35Sstevel * NOTE: This code block is dangerous at this point in the 365803831d35Sstevel * copy-rename operation. It modifies memory after the copy 365903831d35Sstevel * has taken place which means that any persistent state will 366003831d35Sstevel * be abandoned after the rename operation. The code is also 366103831d35Sstevel * performing thread synchronization at a time when all but 366203831d35Sstevel * one processors are paused. This is a potential deadlock 366303831d35Sstevel * situation. 366403831d35Sstevel * 366503831d35Sstevel * This code block must be moved to drmach_copy_rename_init. 366603831d35Sstevel */ 366703831d35Sstevel if (drmach_is_cheetah) { 366803831d35Sstevel mutex_enter(&drmach_iocage_lock); 366903831d35Sstevel while (drmach_iocage_is_busy) 367003831d35Sstevel cv_wait(&drmach_iocage_cv, &drmach_iocage_lock); 367103831d35Sstevel drmach_iocage_is_busy = 1; 367203831d35Sstevel drmach_iocage_mem_scrub(ecache_size * 2); 367303831d35Sstevel mutex_exit(&drmach_iocage_lock); 367403831d35Sstevel } 367503831d35Sstevel 367603831d35Sstevel /* 367703831d35Sstevel * bcopy32_il is implemented as a series of ldxa/stxa via 367803831d35Sstevel * ASI_MEM instructions. Following the copy loop, the E$ 367903831d35Sstevel * of the master (this) processor will have lines in state 368003831d35Sstevel * O that correspond to lines of home memory in state gI. 368103831d35Sstevel * An E$ flush is necessary to commit these lines before 368203831d35Sstevel * proceeding with the rename operation. 368303831d35Sstevel * 368403831d35Sstevel * Flushing the E$ will automatically flush the W$, but 368503831d35Sstevel * the D$ and I$ must be flushed separately and explicitly. 368603831d35Sstevel */ 368703831d35Sstevel flush_ecache_il(caddr, csize, lnsize); /* inline version */ 368803831d35Sstevel 368903831d35Sstevel /* 369003831d35Sstevel * Each line of home memory is now in state gM, except in 369103831d35Sstevel * the case of a cheetah processor when the E$ flush area 369203831d35Sstevel * is included within the copied region. In such a case, 369303831d35Sstevel * the lines of home memory for the upper half of the 369403831d35Sstevel * flush area are in state gS. 369503831d35Sstevel * 369603831d35Sstevel * Each line of target memory is in state gM. 369703831d35Sstevel * 369803831d35Sstevel * Each line of this processor's E$ is in state I, except 369903831d35Sstevel * those of a cheetah processor. All lines of a cheetah 370003831d35Sstevel * processor's E$ are in state S and correspond to the lines 370103831d35Sstevel * in upper half of the E$ flush area. 370203831d35Sstevel * 370303831d35Sstevel * It is vital at this point that none of the lines in the 370403831d35Sstevel * home or target memories are in state gI and that none 370503831d35Sstevel * of the lines in this processor's E$ are in state O or Os. 370603831d35Sstevel * A single instance of such a condition will cause loss of 370703831d35Sstevel * coherency following the rename operation. 370803831d35Sstevel */ 370903831d35Sstevel 371003831d35Sstevel /* 371103831d35Sstevel * Rename 371203831d35Sstevel */ 371303831d35Sstevel (*(void(*)())drmach_cpu_sram_va)(cr->data, &cr->ecode, &cr->earg); 371403831d35Sstevel 371503831d35Sstevel /* 371603831d35Sstevel * Rename operation complete. The physical address space 371703831d35Sstevel * of the home and target memories have been swapped, the 371803831d35Sstevel * routing data in the respective CASM entries have been 371903831d35Sstevel * swapped, and LPA settings in the processor and schizo 372003831d35Sstevel * devices have been reprogrammed accordingly. 372103831d35Sstevel * 372203831d35Sstevel * In the case of a cheetah processor, the E$ remains 372303831d35Sstevel * populated with lines in state S that correspond to the 372403831d35Sstevel * lines in the former home memory. Now that the physical 372503831d35Sstevel * addresses have been swapped, these E$ lines correspond 372603831d35Sstevel * to lines in the new home memory which are in state gM. 372703831d35Sstevel * This combination is invalid. An additional E$ flush is 372803831d35Sstevel * necessary to restore coherency. The E$ flush will cause 372903831d35Sstevel * the lines of the new home memory for the flush region 373003831d35Sstevel * to transition from state gM to gS. The former home memory 373103831d35Sstevel * remains unmodified. This additional E$ flush has no effect 373203831d35Sstevel * on a cheetah+ processor. 373303831d35Sstevel */ 373403831d35Sstevel flush_ecache_il(caddr, csize, lnsize); /* inline version */ 373503831d35Sstevel 373603831d35Sstevel /* 373703831d35Sstevel * The D$ and I$ must be flushed to ensure that coherency is 373803831d35Sstevel * maintained. Any line in a cache that is in the valid 373903831d35Sstevel * state has its corresponding line of the new home memory 374003831d35Sstevel * in the gM state. This is an invalid condition. When the 374103831d35Sstevel * flushes are complete the cache line states will be 374203831d35Sstevel * resynchronized with those in the new home memory. 374303831d35Sstevel */ 374403831d35Sstevel flush_icache_il(); /* inline version */ 374503831d35Sstevel flush_dcache_il(); /* inline version */ 374603831d35Sstevel flush_pcache_il(); /* inline version */ 374703831d35Sstevel 374803831d35Sstevel copy_rename_end: 374903831d35Sstevel 375003831d35Sstevel no_trap(); 375103831d35Sstevel 375203831d35Sstevel /* enable interrupts */ 375303831d35Sstevel setpstate(pstate); 375403831d35Sstevel 375503831d35Sstevel /* enable CE reporting */ 375603831d35Sstevel set_error_enable(neer); 375703831d35Sstevel 375803831d35Sstevel if (cr->ecode != DRMACH_CR_OK) 375903831d35Sstevel drmach_end_wait_xcall = drmach_rename_abort; 376003831d35Sstevel 376103831d35Sstevel /* 376203831d35Sstevel * XXX CHEETAH SUPPORT 376303831d35Sstevel */ 376403831d35Sstevel if (drmach_is_cheetah) { 376503831d35Sstevel mutex_enter(&drmach_iocage_lock); 376603831d35Sstevel drmach_iocage_mem_scrub(ecache_size * 2); 376703831d35Sstevel drmach_iocage_is_busy = 0; 376803831d35Sstevel cv_signal(&drmach_iocage_cv); 376903831d35Sstevel mutex_exit(&drmach_iocage_lock); 377003831d35Sstevel } 377103831d35Sstevel 377203831d35Sstevel axq_iopause_disable_all(); 377303831d35Sstevel 377403831d35Sstevel xt_some(cpuset, drmach_end_wait_xcall, NULL, NULL); 377503831d35Sstevel } 377603831d35Sstevel 377703831d35Sstevel static void drmach_io_dispose(drmachid_t); 377803831d35Sstevel static sbd_error_t *drmach_io_release(drmachid_t); 377903831d35Sstevel static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *); 378003831d35Sstevel 378103831d35Sstevel static sbd_error_t * 378203831d35Sstevel drmach_pci_new(drmach_device_t *proto, drmachid_t *idp) 378303831d35Sstevel { 378403831d35Sstevel drmach_node_t *node = proto->node; 378503831d35Sstevel sbd_error_t *err; 378603831d35Sstevel drmach_reg_t regs[3]; 378703831d35Sstevel int rv; 378803831d35Sstevel int len = 0; 378903831d35Sstevel 379003831d35Sstevel rv = node->n_getproplen(node, "reg", &len); 379103831d35Sstevel if (rv != 0 || len != sizeof (regs)) { 379203831d35Sstevel sbd_error_t *err; 379303831d35Sstevel 379403831d35Sstevel /* pci nodes are expected to have regs */ 379503831d35Sstevel err = drerr_new(1, ESTC_GETPROP, 379603831d35Sstevel "Device Node 0x%x: property %s", 379703831d35Sstevel (uint_t)node->get_dnode(node), "reg"); 379803831d35Sstevel return (err); 379903831d35Sstevel } 380003831d35Sstevel 380103831d35Sstevel rv = node->n_getprop(node, "reg", (void *)regs, sizeof (regs)); 380203831d35Sstevel if (rv) { 380303831d35Sstevel sbd_error_t *err; 380403831d35Sstevel 380503831d35Sstevel err = drerr_new(1, ESTC_GETPROP, 380603831d35Sstevel "Device Node 0x%x: property %s", 380703831d35Sstevel (uint_t)node->get_dnode(node), "reg"); 380803831d35Sstevel 380903831d35Sstevel return (err); 381003831d35Sstevel } 381103831d35Sstevel 381203831d35Sstevel /* 381303831d35Sstevel * Fix up unit number so that Leaf A has a lower unit number 381403831d35Sstevel * than Leaf B. 381503831d35Sstevel */ 381603831d35Sstevel if ((proto->portid % 2) != 0) { 381703831d35Sstevel if ((regs[0].reg_addr_lo & 0x700000) == 0x700000) 381803831d35Sstevel proto->unum = 0; 381903831d35Sstevel else 382003831d35Sstevel proto->unum = 1; 382103831d35Sstevel } else { 382203831d35Sstevel if ((regs[0].reg_addr_lo & 0x700000) == 0x700000) 382303831d35Sstevel proto->unum = 2; 382403831d35Sstevel else 382503831d35Sstevel proto->unum = 3; 382603831d35Sstevel } 382703831d35Sstevel 382803831d35Sstevel err = drmach_io_new(proto, idp); 382903831d35Sstevel if (err == NULL) { 383003831d35Sstevel drmach_io_t *self = *idp; 383103831d35Sstevel 383203831d35Sstevel /* reassemble 64-bit base address */ 383303831d35Sstevel self->scsr_pa = (uint64_t)regs[1].reg_addr_hi << 32; 383403831d35Sstevel self->scsr_pa |= (uint64_t)regs[1].reg_addr_lo; 383503831d35Sstevel } 383603831d35Sstevel 383703831d35Sstevel return (err); 383803831d35Sstevel } 383903831d35Sstevel 384003831d35Sstevel static sbd_error_t * 384103831d35Sstevel drmach_io_new(drmach_device_t *proto, drmachid_t *idp) 384203831d35Sstevel { 384303831d35Sstevel drmach_io_t *ip; 384403831d35Sstevel 384503831d35Sstevel ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP); 384603831d35Sstevel bcopy(proto, &ip->dev, sizeof (ip->dev)); 384703831d35Sstevel ip->dev.node = drmach_node_dup(proto->node); 384803831d35Sstevel ip->dev.cm.isa = (void *)drmach_io_new; 384903831d35Sstevel ip->dev.cm.dispose = drmach_io_dispose; 385003831d35Sstevel ip->dev.cm.release = drmach_io_release; 385103831d35Sstevel ip->dev.cm.status = drmach_io_status; 385203831d35Sstevel 385307d06da5SSurya Prakki (void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d", 385403831d35Sstevel ip->dev.type, ip->dev.unum); 385503831d35Sstevel 385603831d35Sstevel *idp = (drmachid_t)ip; 385703831d35Sstevel return (NULL); 385803831d35Sstevel } 385903831d35Sstevel 386003831d35Sstevel static void 386103831d35Sstevel drmach_io_dispose(drmachid_t id) 386203831d35Sstevel { 386303831d35Sstevel drmach_io_t *self; 386403831d35Sstevel 386503831d35Sstevel ASSERT(DRMACH_IS_IO_ID(id)); 386603831d35Sstevel 386703831d35Sstevel self = id; 386803831d35Sstevel if (self->dev.node) 386903831d35Sstevel drmach_node_dispose(self->dev.node); 387003831d35Sstevel 387103831d35Sstevel kmem_free(self, sizeof (*self)); 387203831d35Sstevel } 387303831d35Sstevel 387403831d35Sstevel /*ARGSUSED*/ 387503831d35Sstevel sbd_error_t * 387603831d35Sstevel drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts) 387703831d35Sstevel { 387803831d35Sstevel drmach_board_t *bp = (drmach_board_t *)id; 387903831d35Sstevel sbd_error_t *err = NULL; 388003831d35Sstevel 388103831d35Sstevel if (id && DRMACH_IS_BOARD_ID(id)) { 388203831d35Sstevel switch (cmd) { 388303831d35Sstevel case SBD_CMD_TEST: 388403831d35Sstevel case SBD_CMD_STATUS: 388503831d35Sstevel case SBD_CMD_GETNCM: 388603831d35Sstevel break; 388703831d35Sstevel case SBD_CMD_CONNECT: 388803831d35Sstevel if (bp->connected) 388903831d35Sstevel err = drerr_new(0, ESBD_STATE, NULL); 389003831d35Sstevel 389103831d35Sstevel if (bp->cond == SBD_COND_UNUSABLE) 389203831d35Sstevel err = drerr_new(0, 389303831d35Sstevel ESBD_FATAL_STATE, NULL); 389403831d35Sstevel break; 389503831d35Sstevel case SBD_CMD_DISCONNECT: 389603831d35Sstevel if (!bp->connected) 389703831d35Sstevel err = drerr_new(0, ESBD_STATE, NULL); 389803831d35Sstevel 389903831d35Sstevel if (bp->cond == SBD_COND_UNUSABLE) 390003831d35Sstevel err = drerr_new(0, 390103831d35Sstevel ESBD_FATAL_STATE, NULL); 390203831d35Sstevel break; 390303831d35Sstevel default: 390403831d35Sstevel if (bp->cond == SBD_COND_UNUSABLE) 390503831d35Sstevel err = drerr_new(0, 390603831d35Sstevel ESBD_FATAL_STATE, NULL); 390703831d35Sstevel break; 390803831d35Sstevel 390903831d35Sstevel } 391003831d35Sstevel } 391103831d35Sstevel 391203831d35Sstevel return (err); 391303831d35Sstevel } 391403831d35Sstevel 391503831d35Sstevel /*ARGSUSED*/ 391603831d35Sstevel sbd_error_t * 391703831d35Sstevel drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts) 391803831d35Sstevel { 391903831d35Sstevel return (NULL); 392003831d35Sstevel } 392103831d35Sstevel 392203831d35Sstevel sbd_error_t * 392303831d35Sstevel drmach_board_assign(int bnum, drmachid_t *id) 392403831d35Sstevel { 392503831d35Sstevel sbd_error_t *err = NULL; 392603831d35Sstevel caddr_t obufp; 392703831d35Sstevel 392803831d35Sstevel if (!drmach_initialized && drmach_init() == -1) { 392903831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 393003831d35Sstevel } 393103831d35Sstevel 393203831d35Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER); 393303831d35Sstevel 393403831d35Sstevel if (!err) { 393503831d35Sstevel if (drmach_array_get(drmach_boards, bnum, id) == -1) { 393603831d35Sstevel err = drerr_new(0, ESTC_BNUM, "%d", bnum); 393703831d35Sstevel } else { 393803831d35Sstevel drmach_board_t *bp; 393903831d35Sstevel 394003831d35Sstevel if (*id) 394103831d35Sstevel rw_downgrade(&drmach_boards_rwlock); 394203831d35Sstevel 394303831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 394403831d35Sstevel err = drmach_mbox_trans(DRMSG_ASSIGN, bnum, obufp, 394503831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0); 394603831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 394703831d35Sstevel 394803831d35Sstevel if (!err) { 394903831d35Sstevel bp = *id; 395003831d35Sstevel if (!*id) 395103831d35Sstevel bp = *id = 395203831d35Sstevel (drmachid_t)drmach_board_new(bnum); 395303831d35Sstevel bp->assigned = 1; 395403831d35Sstevel } 395503831d35Sstevel } 395603831d35Sstevel } 395703831d35Sstevel rw_exit(&drmach_boards_rwlock); 395803831d35Sstevel return (err); 395903831d35Sstevel } 396003831d35Sstevel 396103831d35Sstevel static uint_t 396203831d35Sstevel drmach_board_non_panther_cpus(gdcd_t *gdcd, uint_t exp, uint_t slot) 396303831d35Sstevel { 396403831d35Sstevel uint_t port, port_start, port_end; 396503831d35Sstevel uint_t non_panther_cpus = 0; 396603831d35Sstevel uint_t impl; 396703831d35Sstevel 396803831d35Sstevel ASSERT(gdcd != NULL); 396903831d35Sstevel 397003831d35Sstevel /* 397103831d35Sstevel * Determine PRD port indices based on slot location. 397203831d35Sstevel */ 397303831d35Sstevel switch (slot) { 397403831d35Sstevel case 0: 397503831d35Sstevel port_start = 0; 397603831d35Sstevel port_end = 3; 397703831d35Sstevel break; 397803831d35Sstevel case 1: 397903831d35Sstevel port_start = 4; 398003831d35Sstevel port_end = 5; 398103831d35Sstevel break; 398203831d35Sstevel default: 398303831d35Sstevel ASSERT(0); 398403831d35Sstevel /* check all */ 398503831d35Sstevel port_start = 0; 398603831d35Sstevel port_end = 5; 398703831d35Sstevel break; 398803831d35Sstevel } 398903831d35Sstevel 399003831d35Sstevel for (port = port_start; port <= port_end; port++) { 399103831d35Sstevel if (gdcd->dcd_prd[exp][port].prd_ptype == SAFPTYPE_CPU && 399203831d35Sstevel RSV_GOOD(gdcd->dcd_prd[exp][port].prd_prsv)) { 399303831d35Sstevel /* 399403831d35Sstevel * This Safari port passed POST and represents a 399503831d35Sstevel * cpu, so check the implementation. 399603831d35Sstevel */ 399703831d35Sstevel impl = (gdcd->dcd_prd[exp][port].prd_ver_reg >> 32) 399803831d35Sstevel & 0xffff; 399903831d35Sstevel 400003831d35Sstevel switch (impl) { 400103831d35Sstevel case CHEETAH_IMPL: 400203831d35Sstevel case CHEETAH_PLUS_IMPL: 400303831d35Sstevel case JAGUAR_IMPL: 400403831d35Sstevel non_panther_cpus++; 400503831d35Sstevel break; 400603831d35Sstevel case PANTHER_IMPL: 400703831d35Sstevel break; 400803831d35Sstevel default: 400903831d35Sstevel ASSERT(0); 401003831d35Sstevel non_panther_cpus++; 401103831d35Sstevel break; 401203831d35Sstevel } 401303831d35Sstevel } 401403831d35Sstevel } 401503831d35Sstevel 401603831d35Sstevel DRMACH_PR("drmach_board_non_panther_cpus: exp=%d, slot=%d, " 401703831d35Sstevel "non_panther_cpus=%d", exp, slot, non_panther_cpus); 401803831d35Sstevel 401903831d35Sstevel return (non_panther_cpus); 402003831d35Sstevel } 402103831d35Sstevel 402203831d35Sstevel sbd_error_t * 402303831d35Sstevel drmach_board_connect(drmachid_t id, drmach_opts_t *opts) 402403831d35Sstevel { 402503831d35Sstevel _NOTE(ARGUNUSED(opts)) 402603831d35Sstevel 402703831d35Sstevel drmach_board_t *bp = (drmach_board_t *)id; 402803831d35Sstevel sbd_error_t *err; 402903831d35Sstevel dr_mbox_msg_t *obufp; 403003831d35Sstevel gdcd_t *gdcd = NULL; 403103831d35Sstevel uint_t exp, slot; 403203831d35Sstevel sc_gptwocfg_cookie_t scc; 403303831d35Sstevel int panther_pages_enabled; 403403831d35Sstevel 403503831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 403603831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 403703831d35Sstevel 403803831d35Sstevel /* 403903831d35Sstevel * Build the casm info portion of the CLAIM message. 404003831d35Sstevel */ 404103831d35Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP); 404203831d35Sstevel mutex_enter(&drmach_slice_table_lock); 404303831d35Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_cr.mem_slice); 404403831d35Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_cr.mem_regs); 404503831d35Sstevel mutex_exit(&drmach_slice_table_lock); 404603831d35Sstevel err = drmach_mbox_trans(DRMSG_CLAIM, bp->bnum, (caddr_t)obufp, 404703831d35Sstevel sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0); 404803831d35Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t)); 404903831d35Sstevel 405003831d35Sstevel if (err) { 405103831d35Sstevel /* 405203831d35Sstevel * if mailbox timeout or unrecoverable error from SC, 405303831d35Sstevel * board cannot be touched. Mark the status as 405403831d35Sstevel * unusable. 405503831d35Sstevel */ 405603831d35Sstevel if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) || 405703831d35Sstevel (err->e_code == ESTC_MBXRPLY)) 405803831d35Sstevel bp->cond = SBD_COND_UNUSABLE; 405903831d35Sstevel return (err); 406003831d35Sstevel } 406103831d35Sstevel 406203831d35Sstevel gdcd = drmach_gdcd_new(); 406303831d35Sstevel if (gdcd == NULL) { 406403831d35Sstevel cmn_err(CE_WARN, "failed to read GDCD info for %s\n", 406503831d35Sstevel bp->cm.name); 406603831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 406703831d35Sstevel } 406803831d35Sstevel 406903831d35Sstevel /* 407003831d35Sstevel * Read CPU SRAM DR buffer offset from GDCD. 407103831d35Sstevel */ 407203831d35Sstevel exp = DRMACH_BNUM2EXP(bp->bnum); 407303831d35Sstevel slot = DRMACH_BNUM2SLOT(bp->bnum); 407403831d35Sstevel bp->stardrb_offset = 407503831d35Sstevel gdcd->dcd_slot[exp][slot].l1ss_cpu_drblock_xwd_offset << 3; 407603831d35Sstevel DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name, 407703831d35Sstevel bp->stardrb_offset); 407803831d35Sstevel 407903831d35Sstevel /* 408003831d35Sstevel * Read board LPA setting from GDCD. 408103831d35Sstevel */ 408203831d35Sstevel bp->flags &= ~DRMACH_NULL_PROC_LPA; 408303831d35Sstevel if (gdcd->dcd_slot[exp][slot].l1ss_flags & 408403831d35Sstevel L1SSFLG_THIS_L1_NULL_PROC_LPA) { 408503831d35Sstevel bp->flags |= DRMACH_NULL_PROC_LPA; 408603831d35Sstevel DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name); 408703831d35Sstevel } 408803831d35Sstevel 408903831d35Sstevel /* 409003831d35Sstevel * XXX Until the Solaris large pages support heterogeneous cpu 409103831d35Sstevel * domains, DR needs to prevent the addition of non-Panther cpus 409203831d35Sstevel * to an all-Panther domain with large pages enabled. 409303831d35Sstevel */ 409403831d35Sstevel panther_pages_enabled = (page_num_pagesizes() > DEFAULT_MMU_PAGE_SIZES); 409503831d35Sstevel if (drmach_board_non_panther_cpus(gdcd, exp, slot) > 0 && 409603831d35Sstevel panther_pages_enabled && drmach_large_page_restriction) { 409703831d35Sstevel cmn_err(CE_WARN, "Domain shutdown is required to add a non-" 409803831d35Sstevel "UltraSPARC-IV+ board into an all UltraSPARC-IV+ domain"); 409903831d35Sstevel err = drerr_new(0, ESTC_SUPPORT, NULL); 410003831d35Sstevel } 410103831d35Sstevel 410203831d35Sstevel if (err == NULL) { 410303831d35Sstevel /* do saf configurator stuff */ 410403831d35Sstevel DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum); 410503831d35Sstevel scc = sc_probe_board(bp->bnum); 410603831d35Sstevel if (scc == NULL) 410703831d35Sstevel err = drerr_new(0, ESTC_PROBE, bp->cm.name); 410803831d35Sstevel } 410903831d35Sstevel 411003831d35Sstevel if (err) { 411103831d35Sstevel /* flush CDC srams */ 411203831d35Sstevel if (axq_cdc_flush_all() != DDI_SUCCESS) { 411303831d35Sstevel goto out; 411403831d35Sstevel } 411503831d35Sstevel 411603831d35Sstevel /* 411703831d35Sstevel * Build the casm info portion of the UNCLAIM message. 411803831d35Sstevel */ 411903831d35Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP); 412003831d35Sstevel mutex_enter(&drmach_slice_table_lock); 412103831d35Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice); 412203831d35Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs); 412303831d35Sstevel mutex_exit(&drmach_slice_table_lock); 412403831d35Sstevel (void) drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum, 412503831d35Sstevel (caddr_t)obufp, sizeof (dr_mbox_msg_t), 412603831d35Sstevel (caddr_t)NULL, 0); 412703831d35Sstevel 412803831d35Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t)); 412903831d35Sstevel 413003831d35Sstevel /* 413103831d35Sstevel * we clear the connected flag just in case it would have 413203831d35Sstevel * been set by a concurrent drmach_board_status() thread 413303831d35Sstevel * before the UNCLAIM completed. 413403831d35Sstevel */ 413503831d35Sstevel bp->connected = 0; 413603831d35Sstevel goto out; 413703831d35Sstevel } 413803831d35Sstevel 413903831d35Sstevel /* 414003831d35Sstevel * Now that the board has been successfully attached, obtain 414103831d35Sstevel * platform-specific DIMM serial id information for the board. 414203831d35Sstevel */ 414303831d35Sstevel if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) && 414403831d35Sstevel plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE)) { 414503831d35Sstevel (void) plat_request_mem_sids(DRMACH_BNUM2EXP(bp->bnum)); 414603831d35Sstevel } 414703831d35Sstevel 414803831d35Sstevel out: 414903831d35Sstevel if (gdcd != NULL) 415003831d35Sstevel drmach_gdcd_dispose(gdcd); 415103831d35Sstevel 415203831d35Sstevel return (err); 415303831d35Sstevel } 415403831d35Sstevel 415503831d35Sstevel static void 415603831d35Sstevel drmach_slice_table_update(drmach_board_t *bp, int invalidate) 415703831d35Sstevel { 415803831d35Sstevel static char *axq_name = "address-extender-queue"; 415903831d35Sstevel static dev_info_t *axq_dip = NULL; 416003831d35Sstevel static int axq_exp = -1; 416103831d35Sstevel static int axq_slot; 416203831d35Sstevel int e, s, slice; 416303831d35Sstevel 416403831d35Sstevel ASSERT(MUTEX_HELD(&drmach_slice_table_lock)); 416503831d35Sstevel 416603831d35Sstevel e = DRMACH_BNUM2EXP(bp->bnum); 416703831d35Sstevel if (invalidate) { 416803831d35Sstevel ASSERT(DRMACH_BNUM2SLOT(bp->bnum) == 0); 416903831d35Sstevel 417003831d35Sstevel /* invalidate cached casm value */ 417103831d35Sstevel drmach_slice_table[e] = 0; 417203831d35Sstevel 417303831d35Sstevel /* invalidate cached axq info if for same exp */ 417403831d35Sstevel if (e == axq_exp && axq_dip) { 417503831d35Sstevel ndi_rele_devi(axq_dip); 417603831d35Sstevel axq_dip = NULL; 417703831d35Sstevel } 417803831d35Sstevel } 417903831d35Sstevel 418003831d35Sstevel if (axq_dip == NULL || !i_ddi_devi_attached(axq_dip)) { 418103831d35Sstevel int i, portid; 418203831d35Sstevel 418303831d35Sstevel /* search for an attached slot0 axq instance */ 418403831d35Sstevel for (i = 0; i < AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP; i++) { 418503831d35Sstevel if (axq_dip) 418603831d35Sstevel ndi_rele_devi(axq_dip); 418703831d35Sstevel axq_dip = ddi_find_devinfo(axq_name, i, 0); 418803831d35Sstevel if (axq_dip && DDI_CF2(axq_dip)) { 418903831d35Sstevel portid = ddi_getprop(DDI_DEV_T_ANY, axq_dip, 419003831d35Sstevel DDI_PROP_DONTPASS, "portid", -1); 419103831d35Sstevel if (portid == -1) { 419203831d35Sstevel DRMACH_PR("cant get portid of axq " 419303831d35Sstevel "instance %d\n", i); 419403831d35Sstevel continue; 419503831d35Sstevel } 419603831d35Sstevel 419703831d35Sstevel axq_exp = (portid >> 5) & 0x1f; 419803831d35Sstevel axq_slot = portid & 1; 419903831d35Sstevel 420003831d35Sstevel if (invalidate && axq_exp == e) 420103831d35Sstevel continue; 420203831d35Sstevel 420303831d35Sstevel if (axq_slot == 0) 420403831d35Sstevel break; /* found */ 420503831d35Sstevel } 420603831d35Sstevel } 420703831d35Sstevel 420803831d35Sstevel if (i == AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP) { 420903831d35Sstevel if (axq_dip) { 421003831d35Sstevel ndi_rele_devi(axq_dip); 421103831d35Sstevel axq_dip = NULL; 421203831d35Sstevel } 421303831d35Sstevel DRMACH_PR("drmach_slice_table_update: failed to " 421403831d35Sstevel "update axq dip\n"); 421503831d35Sstevel return; 421603831d35Sstevel } 421703831d35Sstevel 421803831d35Sstevel } 421903831d35Sstevel 422003831d35Sstevel ASSERT(axq_dip); 422103831d35Sstevel ASSERT(axq_slot == 0); 422203831d35Sstevel 422303831d35Sstevel if (invalidate) 422403831d35Sstevel return; 422503831d35Sstevel 422603831d35Sstevel s = DRMACH_BNUM2SLOT(bp->bnum); 4227d3d50737SRafael Vanoni DRMACH_PR("using AXQ casm %d.%d for slot%d.%d\n", axq_exp, axq_slot, 4228d3d50737SRafael Vanoni e, s); 422903831d35Sstevel 423003831d35Sstevel /* invalidate entry */ 423103831d35Sstevel drmach_slice_table[e] &= ~0x20; 423203831d35Sstevel 423303831d35Sstevel /* 423403831d35Sstevel * find a slice that routes to expander e. If no match 423503831d35Sstevel * is found, drmach_slice_table[e] will remain invalid. 423603831d35Sstevel * 423703831d35Sstevel * The CASM is a routing table indexed by slice number. 423803831d35Sstevel * Each element in the table contains permission bits, 423903831d35Sstevel * a destination expander number and a valid bit. The 424003831d35Sstevel * valid bit must true for the element to be meaningful. 424103831d35Sstevel * 424203831d35Sstevel * CASM entry structure 424303831d35Sstevel * Bits 15..6 ignored 424403831d35Sstevel * Bit 5 valid 424503831d35Sstevel * Bits 0..4 expander number 424603831d35Sstevel * 424703831d35Sstevel * NOTE: the for loop is really enumerating the range of slices, 424803831d35Sstevel * which is ALWAYS equal to the range of expanders. Hence, 424903831d35Sstevel * AXQ_MAX_EXP is okay to use in this loop. 425003831d35Sstevel */ 425103831d35Sstevel for (slice = 0; slice < AXQ_MAX_EXP; slice++) { 425203831d35Sstevel uint32_t casm = axq_casm_read(axq_exp, axq_slot, slice); 425303831d35Sstevel 425403831d35Sstevel if ((casm & 0x20) && (casm & 0x1f) == e) 425503831d35Sstevel drmach_slice_table[e] = 0x20 | slice; 425603831d35Sstevel } 425703831d35Sstevel } 425803831d35Sstevel 425903831d35Sstevel /* 426003831d35Sstevel * Get base and bound PAs for slot 1 board lpa programming 426103831d35Sstevel * If a cpu/mem board is present in the same expander, use slice 426203831d35Sstevel * information corresponding to the CASM. Otherwise, set base and 426303831d35Sstevel * bound PAs to 0. 426403831d35Sstevel */ 426503831d35Sstevel static void 426603831d35Sstevel drmach_lpa_bb_get(drmach_board_t *s1bp, uint64_t *basep, uint64_t *boundp) 426703831d35Sstevel { 426803831d35Sstevel drmachid_t s0id; 426903831d35Sstevel 427003831d35Sstevel ASSERT(mutex_owned(&drmach_slice_table_lock)); 427103831d35Sstevel ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1); 427203831d35Sstevel 427303831d35Sstevel *basep = *boundp = 0; 427403831d35Sstevel if (drmach_array_get(drmach_boards, s1bp->bnum - 1, &s0id) == 0 && 427503831d35Sstevel s0id != 0) { 427603831d35Sstevel 427703831d35Sstevel uint32_t slice; 4278d3d50737SRafael Vanoni if ((slice = drmach_slice_table[DRMACH_BNUM2EXP(s1bp->bnum)]) 427903831d35Sstevel & 0x20) { 428003831d35Sstevel *basep = DRMACH_SLICE_TO_PA(slice & DRMACH_SLICE_MASK); 428103831d35Sstevel *boundp = *basep + DRMACH_MEM_SLICE_SIZE; 428203831d35Sstevel } 428303831d35Sstevel } 428403831d35Sstevel } 428503831d35Sstevel 428603831d35Sstevel 428703831d35Sstevel /* 428803831d35Sstevel * Reprogram slot 1 lpa's as required. 428903831d35Sstevel * The purpose of this routine is maintain the LPA settings of the devices 429003831d35Sstevel * in slot 1. To date we know Schizo and Cheetah are the only devices that 429103831d35Sstevel * require this attention. The LPA setting must match the slice field in the 429203831d35Sstevel * CASM element for the local expander. This field is guaranteed to be 429303831d35Sstevel * programmed in accordance with the cacheable address space on the slot 0 429403831d35Sstevel * board of the local expander. If no memory is present on the slot 0 board, 429503831d35Sstevel * there is no cacheable address space and, hence, the CASM slice field will 429603831d35Sstevel * be zero or its valid bit will be false (or both). 429703831d35Sstevel */ 429803831d35Sstevel 429903831d35Sstevel static void 430003831d35Sstevel drmach_slot1_lpa_set(drmach_board_t *bp) 430103831d35Sstevel { 430203831d35Sstevel drmachid_t id; 430303831d35Sstevel drmach_board_t *s1bp = NULL; 430403831d35Sstevel int rv, idx, is_maxcat = 1; 430503831d35Sstevel uint64_t last_scsr_pa = 0; 430603831d35Sstevel uint64_t new_basepa, new_boundpa; 430703831d35Sstevel 430803831d35Sstevel if (DRMACH_BNUM2SLOT(bp->bnum)) { 430903831d35Sstevel s1bp = bp; 431003831d35Sstevel if (s1bp->devices == NULL) { 431103831d35Sstevel DRMACH_PR("drmach...lpa_set: slot1=%d not present", 431203831d35Sstevel bp->bnum); 431303831d35Sstevel return; 431403831d35Sstevel } 431503831d35Sstevel } else { 431603831d35Sstevel rv = drmach_array_get(drmach_boards, bp->bnum + 1, &id); 431703831d35Sstevel /* nothing to do when board is not found or has no devices */ 431803831d35Sstevel s1bp = id; 431903831d35Sstevel if (rv == -1 || s1bp == NULL || s1bp->devices == NULL) { 432003831d35Sstevel DRMACH_PR("drmach...lpa_set: slot1=%d not present", 432103831d35Sstevel bp->bnum + 1); 432203831d35Sstevel return; 432303831d35Sstevel } 432403831d35Sstevel ASSERT(DRMACH_IS_BOARD_ID(id)); 432503831d35Sstevel } 432603831d35Sstevel mutex_enter(&drmach_slice_table_lock); 432703831d35Sstevel drmach_lpa_bb_get(s1bp, &new_basepa, &new_boundpa); 432803831d35Sstevel DRMACH_PR("drmach_...lpa_set: bnum=%d base=0x%lx bound=0x%lx\n", 432903831d35Sstevel s1bp->bnum, new_basepa, new_boundpa); 433003831d35Sstevel 433103831d35Sstevel rv = drmach_array_first(s1bp->devices, &idx, &id); 433203831d35Sstevel while (rv == 0) { 433303831d35Sstevel if (DRMACH_IS_IO_ID(id)) { 433403831d35Sstevel drmach_io_t *io = id; 433503831d35Sstevel 433603831d35Sstevel is_maxcat = 0; 433703831d35Sstevel 433803831d35Sstevel /* 433903831d35Sstevel * Skip all non-Schizo IO devices (only IO nodes 434003831d35Sstevel * that are Schizo devices have non-zero scsr_pa). 434103831d35Sstevel * Filter out "other" leaf to avoid writing to the 434203831d35Sstevel * same Schizo Control/Status Register twice. 434303831d35Sstevel */ 434403831d35Sstevel if (io->scsr_pa && io->scsr_pa != last_scsr_pa) { 434503831d35Sstevel uint64_t scsr; 434603831d35Sstevel 434703831d35Sstevel scsr = lddphysio(io->scsr_pa); 434803831d35Sstevel DRMACH_PR("drmach...lpa_set: old scsr=0x%lx\n", 434903831d35Sstevel scsr); 435003831d35Sstevel scsr &= ~(DRMACH_LPA_BASE_MASK | 435103831d35Sstevel DRMACH_LPA_BND_MASK); 435203831d35Sstevel scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa); 435303831d35Sstevel scsr |= DRMACH_PA_TO_LPA_BND(new_boundpa); 435403831d35Sstevel 435503831d35Sstevel stdphysio(io->scsr_pa, scsr); 435603831d35Sstevel DRMACH_PR("drmach...lpa_set: new scsr=0x%lx\n", 435703831d35Sstevel scsr); 435803831d35Sstevel 435903831d35Sstevel last_scsr_pa = io->scsr_pa; 436003831d35Sstevel } 436103831d35Sstevel } 436203831d35Sstevel rv = drmach_array_next(s1bp->devices, &idx, &id); 436303831d35Sstevel } 436403831d35Sstevel 436503831d35Sstevel if (is_maxcat && DRMACH_L1_SET_LPA(s1bp) && drmach_reprogram_lpa) { 436603831d35Sstevel extern xcfunc_t drmach_set_lpa; 436703831d35Sstevel 436803831d35Sstevel DRMACH_PR("reprogramming maxcat lpa's"); 436903831d35Sstevel 437003831d35Sstevel mutex_enter(&cpu_lock); 437103831d35Sstevel rv = drmach_array_first(s1bp->devices, &idx, &id); 437203831d35Sstevel while (rv == 0 && id != NULL) { 437303831d35Sstevel if (DRMACH_IS_CPU_ID(id)) { 437403831d35Sstevel int ntries; 437503831d35Sstevel processorid_t cpuid; 437603831d35Sstevel 437703831d35Sstevel cpuid = ((drmach_cpu_t *)id)->cpuid; 437803831d35Sstevel 437903831d35Sstevel /* 438003831d35Sstevel * Check for unconfigured or powered-off 438103831d35Sstevel * MCPUs. If CPU_READY flag is clear, the 438203831d35Sstevel * MCPU cannot be xcalled. 438303831d35Sstevel */ 438403831d35Sstevel if ((cpu[cpuid] == NULL) || 438503831d35Sstevel (cpu[cpuid]->cpu_flags & 438603831d35Sstevel CPU_READY) == 0) { 438703831d35Sstevel 438803831d35Sstevel rv = drmach_array_next(s1bp->devices, 438903831d35Sstevel &idx, &id); 439003831d35Sstevel continue; 439103831d35Sstevel } 439203831d35Sstevel 439303831d35Sstevel /* 439403831d35Sstevel * XXX CHEETAH SUPPORT 439503831d35Sstevel * for cheetah, we need to clear iocage 439603831d35Sstevel * memory since it will be used for e$ flush 439703831d35Sstevel * in drmach_set_lpa. 439803831d35Sstevel */ 439903831d35Sstevel if (drmach_is_cheetah) { 440003831d35Sstevel mutex_enter(&drmach_iocage_lock); 440103831d35Sstevel while (drmach_iocage_is_busy) 440203831d35Sstevel cv_wait(&drmach_iocage_cv, 440303831d35Sstevel &drmach_iocage_lock); 440403831d35Sstevel drmach_iocage_is_busy = 1; 4405d3d50737SRafael Vanoni drmach_iocage_mem_scrub(ecache_size * 4406d3d50737SRafael Vanoni 2); 440703831d35Sstevel mutex_exit(&drmach_iocage_lock); 440803831d35Sstevel } 440903831d35Sstevel 441003831d35Sstevel /* 441103831d35Sstevel * drmach_slice_table[*] 441203831d35Sstevel * bit 5 valid 441303831d35Sstevel * bit 0:4 slice number 441403831d35Sstevel * 441503831d35Sstevel * drmach_xt_mb[*] format for drmach_set_lpa 441603831d35Sstevel * bit 7 valid 441703831d35Sstevel * bit 6 set null LPA 441803831d35Sstevel * (overrides bits 0:4) 441903831d35Sstevel * bit 0:4 slice number 442003831d35Sstevel * 442103831d35Sstevel * drmach_set_lpa derives processor CBASE and 442203831d35Sstevel * CBND from bits 6 and 0:4 of drmach_xt_mb. 442303831d35Sstevel * If bit 6 is set, then CBASE = CBND = 0. 442403831d35Sstevel * Otherwise, CBASE = slice number; 442503831d35Sstevel * CBND = slice number + 1. 442603831d35Sstevel * No action is taken if bit 7 is zero. 442703831d35Sstevel */ 442803831d35Sstevel 442903831d35Sstevel mutex_enter(&drmach_xt_mb_lock); 443003831d35Sstevel bzero((void *)drmach_xt_mb, 443103831d35Sstevel drmach_xt_mb_size); 443203831d35Sstevel 443303831d35Sstevel if (new_basepa == 0 && new_boundpa == 0) 443403831d35Sstevel drmach_xt_mb[cpuid] = 0x80 | 0x40; 443503831d35Sstevel else 443603831d35Sstevel drmach_xt_mb[cpuid] = 0x80 | 443703831d35Sstevel DRMACH_PA_TO_SLICE(new_basepa); 443803831d35Sstevel 443903831d35Sstevel drmach_xt_ready = 0; 444003831d35Sstevel 444103831d35Sstevel xt_one(cpuid, drmach_set_lpa, NULL, NULL); 444203831d35Sstevel 444303831d35Sstevel ntries = drmach_cpu_ntries; 444403831d35Sstevel while (!drmach_xt_ready && ntries) { 444503831d35Sstevel DELAY(drmach_cpu_delay); 444603831d35Sstevel ntries--; 444703831d35Sstevel } 444803831d35Sstevel mutex_exit(&drmach_xt_mb_lock); 444903831d35Sstevel drmach_xt_ready = 0; 445003831d35Sstevel 445103831d35Sstevel /* 445203831d35Sstevel * XXX CHEETAH SUPPORT 445303831d35Sstevel * for cheetah, we need to clear iocage 445403831d35Sstevel * memory since it was used for e$ flush 445503831d35Sstevel * in performed drmach_set_lpa. 445603831d35Sstevel */ 445703831d35Sstevel if (drmach_is_cheetah) { 445803831d35Sstevel mutex_enter(&drmach_iocage_lock); 4459d3d50737SRafael Vanoni drmach_iocage_mem_scrub(ecache_size * 4460d3d50737SRafael Vanoni 2); 446103831d35Sstevel drmach_iocage_is_busy = 0; 446203831d35Sstevel cv_signal(&drmach_iocage_cv); 446303831d35Sstevel mutex_exit(&drmach_iocage_lock); 446403831d35Sstevel } 446503831d35Sstevel } 446603831d35Sstevel rv = drmach_array_next(s1bp->devices, &idx, &id); 446703831d35Sstevel } 446803831d35Sstevel mutex_exit(&cpu_lock); 446903831d35Sstevel } 447003831d35Sstevel mutex_exit(&drmach_slice_table_lock); 447103831d35Sstevel } 447203831d35Sstevel 447303831d35Sstevel /* 447403831d35Sstevel * Return the number of connected Panther boards in the domain. 447503831d35Sstevel */ 447603831d35Sstevel static int 447703831d35Sstevel drmach_panther_boards(void) 447803831d35Sstevel { 447903831d35Sstevel int rv; 448003831d35Sstevel int b_idx; 448103831d35Sstevel drmachid_t b_id; 448203831d35Sstevel drmach_board_t *bp; 448303831d35Sstevel int npanther = 0; 448403831d35Sstevel 448503831d35Sstevel rv = drmach_array_first(drmach_boards, &b_idx, &b_id); 448603831d35Sstevel while (rv == 0) { 448703831d35Sstevel ASSERT(DRMACH_IS_BOARD_ID(b_id)); 448803831d35Sstevel bp = b_id; 448903831d35Sstevel 449003831d35Sstevel if (IS_PANTHER(bp->cpu_impl)) 449103831d35Sstevel npanther++; 449203831d35Sstevel 449303831d35Sstevel rv = drmach_array_next(drmach_boards, &b_idx, &b_id); 449403831d35Sstevel } 449503831d35Sstevel 449603831d35Sstevel return (npanther); 449703831d35Sstevel } 449803831d35Sstevel 449903831d35Sstevel /*ARGSUSED*/ 450003831d35Sstevel sbd_error_t * 450103831d35Sstevel drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts) 450203831d35Sstevel { 450303831d35Sstevel drmach_board_t *bp; 450403831d35Sstevel dr_mbox_msg_t *obufp; 450503831d35Sstevel sbd_error_t *err = NULL; 450603831d35Sstevel 450703831d35Sstevel sc_gptwocfg_cookie_t scc; 450803831d35Sstevel 450903831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 451003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 451103831d35Sstevel bp = id; 451203831d35Sstevel 451303831d35Sstevel /* 451403831d35Sstevel * Build the casm info portion of the UNCLAIM message. 451503831d35Sstevel * This must be done prior to calling for saf configurator 451603831d35Sstevel * deprobe, to ensure that the associated axq instance 451703831d35Sstevel * is not detached. 451803831d35Sstevel */ 451903831d35Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP); 452003831d35Sstevel mutex_enter(&drmach_slice_table_lock); 452103831d35Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice); 452203831d35Sstevel 452303831d35Sstevel /* 452403831d35Sstevel * If disconnecting slot 0 board, update the casm slice table 452503831d35Sstevel * info now, for use by drmach_slot1_lpa_set() 452603831d35Sstevel */ 452703831d35Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 0) 452803831d35Sstevel drmach_slice_table_update(bp, 1); 452903831d35Sstevel 453003831d35Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs); 453103831d35Sstevel mutex_exit(&drmach_slice_table_lock); 453203831d35Sstevel 453303831d35Sstevel /* 453403831d35Sstevel * Update LPA information for slot1 board 453503831d35Sstevel */ 453603831d35Sstevel drmach_slot1_lpa_set(bp); 453703831d35Sstevel 453803831d35Sstevel /* disable and flush CDC */ 453903831d35Sstevel if (axq_cdc_disable_flush_all() != DDI_SUCCESS) { 454003831d35Sstevel axq_cdc_enable_all(); /* paranoia */ 454103831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 454203831d35Sstevel } 454303831d35Sstevel 454403831d35Sstevel /* 454503831d35Sstevel * call saf configurator for deprobe 454603831d35Sstevel * It's done now before sending an UNCLAIM message because 454703831d35Sstevel * IKP will probe boards it doesn't know about <present at boot> 454803831d35Sstevel * prior to unprobing them. If this happens after sending the 454903831d35Sstevel * UNCLAIM, it will cause a dstop for domain transgression error. 455003831d35Sstevel */ 455103831d35Sstevel 455203831d35Sstevel if (!err) { 455303831d35Sstevel scc = sc_unprobe_board(bp->bnum); 455403831d35Sstevel axq_cdc_enable_all(); 455503831d35Sstevel if (scc != NULL) { 455603831d35Sstevel err = drerr_new(0, ESTC_DEPROBE, bp->cm.name); 455703831d35Sstevel } 455803831d35Sstevel } 455903831d35Sstevel 456003831d35Sstevel /* 456103831d35Sstevel * If disconnecting a board from a Panther domain, wait a fixed- 456203831d35Sstevel * time delay for pending Safari transactions to complete on the 456303831d35Sstevel * disconnecting board's processors. The bus sync list read used 456403831d35Sstevel * in drmach_shutdown_asm to synchronize with outstanding Safari 456503831d35Sstevel * transactions assumes no read-bypass-write mode for all memory 456603831d35Sstevel * controllers. Since Panther supports read-bypass-write, a 456703831d35Sstevel * delay is used that is slightly larger than the maximum Safari 456803831d35Sstevel * timeout value in the Safari/Fireplane Config Reg. 456903831d35Sstevel */ 457003831d35Sstevel if (drmach_panther_boards() > 0 || drmach_unclaim_delay_all) { 4571d3d50737SRafael Vanoni clock_t stime = ddi_get_lbolt(); 457203831d35Sstevel 457303831d35Sstevel delay(drv_usectohz(drmach_unclaim_usec_delay)); 457403831d35Sstevel 4575d3d50737SRafael Vanoni stime = ddi_get_lbolt() - stime; 457603831d35Sstevel DRMACH_PR("delayed %ld ticks (%ld secs) before disconnecting " 457703831d35Sstevel "board %s from domain\n", stime, stime / hz, bp->cm.name); 457803831d35Sstevel } 457903831d35Sstevel 458003831d35Sstevel if (!err) { 458103831d35Sstevel obufp->msgdata.dm_ur.mem_clear = 0; 458203831d35Sstevel 458303831d35Sstevel err = drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum, (caddr_t)obufp, 458403831d35Sstevel sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0); 458503831d35Sstevel 458603831d35Sstevel if (err) { 458703831d35Sstevel /* 458803831d35Sstevel * if mailbox timeout or unrecoverable error from SC, 458903831d35Sstevel * board cannot be touched. Mark the status as 459003831d35Sstevel * unusable. 459103831d35Sstevel */ 459203831d35Sstevel if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) || 459303831d35Sstevel (err->e_code == ESTC_MBXRPLY)) 459403831d35Sstevel bp->cond = SBD_COND_UNUSABLE; 459503831d35Sstevel else { 459603831d35Sstevel DRMACH_PR("UNCLAIM failed for bnum=%d\n", 459703831d35Sstevel bp->bnum); 459803831d35Sstevel DRMACH_PR("calling sc_probe_board: bnum=%d\n", 459903831d35Sstevel bp->bnum); 460003831d35Sstevel scc = sc_probe_board(bp->bnum); 460103831d35Sstevel if (scc == NULL) { 460203831d35Sstevel cmn_err(CE_WARN, 460303831d35Sstevel "sc_probe_board failed for bnum=%d", 460403831d35Sstevel bp->bnum); 460503831d35Sstevel } else { 460603831d35Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 0) { 460703831d35Sstevel mutex_enter( 460803831d35Sstevel &drmach_slice_table_lock); 460903831d35Sstevel drmach_slice_table_update(bp, 461003831d35Sstevel 0); 461103831d35Sstevel mutex_exit( 461203831d35Sstevel &drmach_slice_table_lock); 461303831d35Sstevel } 461403831d35Sstevel drmach_slot1_lpa_set(bp); 461503831d35Sstevel } 461603831d35Sstevel } 461703831d35Sstevel } else { 461803831d35Sstevel bp->connected = 0; 461903831d35Sstevel /* 462003831d35Sstevel * Now that the board has been successfully detached, 462103831d35Sstevel * discard platform-specific DIMM serial id information 462203831d35Sstevel * for the board. 462303831d35Sstevel */ 462403831d35Sstevel if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) && 462503831d35Sstevel plat_ecc_capability_sc_get( 462603831d35Sstevel PLAT_ECC_DIMM_SID_MESSAGE)) { 462703831d35Sstevel (void) plat_discard_mem_sids( 462803831d35Sstevel DRMACH_BNUM2EXP(bp->bnum)); 462903831d35Sstevel } 463003831d35Sstevel } 463103831d35Sstevel } 463203831d35Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t)); 463303831d35Sstevel 463403831d35Sstevel return (err); 463503831d35Sstevel } 463603831d35Sstevel 463703831d35Sstevel static int 463803831d35Sstevel drmach_get_portid(drmach_node_t *np) 463903831d35Sstevel { 464003831d35Sstevel drmach_node_t pp; 464103831d35Sstevel int portid; 464203831d35Sstevel char type[OBP_MAXPROPNAME]; 464303831d35Sstevel 464403831d35Sstevel if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0) 464503831d35Sstevel return (portid); 464603831d35Sstevel 464703831d35Sstevel /* 464803831d35Sstevel * Get the device_type property to see if we should 464903831d35Sstevel * continue processing this node. 465003831d35Sstevel */ 465103831d35Sstevel if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0) 465203831d35Sstevel return (-1); 465303831d35Sstevel 465403831d35Sstevel /* 465503831d35Sstevel * If the device is a CPU without a 'portid' property, 465603831d35Sstevel * it is a CMP core. For such cases, the parent node 465703831d35Sstevel * has the portid. 465803831d35Sstevel */ 465903831d35Sstevel if (strcmp(type, DRMACH_CPU_NAMEPROP) == 0) { 466003831d35Sstevel if (np->get_parent(np, &pp) != 0) 466103831d35Sstevel return (-1); 466203831d35Sstevel 466303831d35Sstevel if (pp.n_getprop(&pp, "portid", &portid, sizeof (portid)) == 0) 466403831d35Sstevel return (portid); 466503831d35Sstevel } 466603831d35Sstevel 466703831d35Sstevel return (-1); 466803831d35Sstevel } 466903831d35Sstevel 467003831d35Sstevel /* 467103831d35Sstevel * This is a helper function to determine if a given 467203831d35Sstevel * node should be considered for a dr operation according 467303831d35Sstevel * to predefined dr type nodes and the node's name. 467403831d35Sstevel * Formal Parameter : The name of a device node. 467503831d35Sstevel * Return Value: -1, name does not map to a valid dr type. 467603831d35Sstevel * A value greater or equal to 0, name is a valid dr type. 467703831d35Sstevel */ 467803831d35Sstevel static int 467903831d35Sstevel drmach_name2type_idx(char *name) 468003831d35Sstevel { 468103831d35Sstevel int index, ntypes; 468203831d35Sstevel 468303831d35Sstevel if (name == NULL) 468403831d35Sstevel return (-1); 468503831d35Sstevel 468603831d35Sstevel /* 468703831d35Sstevel * Determine how many possible types are currently supported 468803831d35Sstevel * for dr. 468903831d35Sstevel */ 469003831d35Sstevel ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]); 469103831d35Sstevel 469203831d35Sstevel /* Determine if the node's name correspond to a predefined type. */ 469303831d35Sstevel for (index = 0; index < ntypes; index++) { 469403831d35Sstevel if (strcmp(drmach_name2type[index].name, name) == 0) 469503831d35Sstevel /* The node is an allowed type for dr. */ 469603831d35Sstevel return (index); 469703831d35Sstevel } 469803831d35Sstevel 469903831d35Sstevel /* 470003831d35Sstevel * If the name of the node does not map to any of the 470103831d35Sstevel * types in the array drmach_name2type then the node is not of 470203831d35Sstevel * interest to dr. 470303831d35Sstevel */ 470403831d35Sstevel return (-1); 470503831d35Sstevel } 470603831d35Sstevel 470703831d35Sstevel static int 470803831d35Sstevel drmach_board_find_devices_cb(drmach_node_walk_args_t *args) 470903831d35Sstevel { 471003831d35Sstevel drmach_node_t *node = args->node; 471103831d35Sstevel drmach_board_cb_data_t *data = args->data; 471203831d35Sstevel drmach_board_t *obj = data->obj; 471303831d35Sstevel 471403831d35Sstevel int rv, portid; 471503831d35Sstevel drmachid_t id; 471603831d35Sstevel drmach_device_t *device; 471703831d35Sstevel char name[OBP_MAXDRVNAME]; 471803831d35Sstevel 471903831d35Sstevel portid = drmach_get_portid(node); 472003831d35Sstevel if (portid == -1) { 472103831d35Sstevel /* 472203831d35Sstevel * if the node does not have a portid property, then 472303831d35Sstevel * by that information alone it is known that drmach 472403831d35Sstevel * is not interested in it. 472503831d35Sstevel */ 472603831d35Sstevel return (0); 472703831d35Sstevel } 472803831d35Sstevel rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME); 472903831d35Sstevel 473003831d35Sstevel /* The node must have a name */ 473103831d35Sstevel if (rv) 473203831d35Sstevel return (0); 473303831d35Sstevel 473403831d35Sstevel /* 473503831d35Sstevel * Ignore devices whose portid do not map to this board, 473603831d35Sstevel * or that their name property is not mapped to a valid 473703831d35Sstevel * dr device name. 473803831d35Sstevel */ 473903831d35Sstevel if ((drmach_portid2bnum(portid) != obj->bnum) || 474003831d35Sstevel (drmach_name2type_idx(name) < 0)) 474103831d35Sstevel return (0); 474203831d35Sstevel 474303831d35Sstevel /* 474403831d35Sstevel * Create a device data structure from this node data. 474503831d35Sstevel * The call may yield nothing if the node is not of interest 474603831d35Sstevel * to drmach. 474703831d35Sstevel */ 474803831d35Sstevel data->err = drmach_device_new(node, obj, portid, &id); 474903831d35Sstevel if (data->err) 475003831d35Sstevel return (-1); 475103831d35Sstevel else if (!id) { 475203831d35Sstevel /* 475303831d35Sstevel * drmach_device_new examined the node we passed in 475403831d35Sstevel * and determined that it was either one not of 475503831d35Sstevel * interest to drmach or the PIM dr layer. 475603831d35Sstevel * So, it is skipped. 475703831d35Sstevel */ 475803831d35Sstevel return (0); 475903831d35Sstevel } 476003831d35Sstevel 476103831d35Sstevel rv = drmach_array_set(obj->devices, data->ndevs++, id); 476203831d35Sstevel if (rv) { 476303831d35Sstevel data->err = DRMACH_INTERNAL_ERROR(); 476403831d35Sstevel return (-1); 476503831d35Sstevel } 476603831d35Sstevel 476703831d35Sstevel device = id; 476803831d35Sstevel 476903831d35Sstevel #ifdef DEBUG 477003831d35Sstevel DRMACH_PR("%d %s %d %p\n", portid, device->type, device->unum, id); 477103831d35Sstevel if (DRMACH_IS_IO_ID(id)) 477203831d35Sstevel DRMACH_PR("ndevs = %d dip/node = %p", data->ndevs, node->here); 477303831d35Sstevel #endif 477403831d35Sstevel 477503831d35Sstevel data->err = (*data->found)(data->a, device->type, device->unum, id); 477603831d35Sstevel return (data->err == NULL ? 0 : -1); 477703831d35Sstevel } 477803831d35Sstevel 477903831d35Sstevel sbd_error_t * 478003831d35Sstevel drmach_board_find_devices(drmachid_t id, void *a, 478103831d35Sstevel sbd_error_t *(*found)(void *a, const char *, int, drmachid_t)) 478203831d35Sstevel { 478303831d35Sstevel drmach_board_t *bp = (drmach_board_t *)id; 478403831d35Sstevel sbd_error_t *err; 478503831d35Sstevel int max_devices; 478603831d35Sstevel int rv; 478703831d35Sstevel drmach_board_cb_data_t data; 478803831d35Sstevel 478903831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 479003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 479103831d35Sstevel 479203831d35Sstevel max_devices = plat_max_cpu_units_per_board(); 479303831d35Sstevel max_devices += plat_max_mem_units_per_board(); 479403831d35Sstevel max_devices += plat_max_io_units_per_board(); 479503831d35Sstevel 479603831d35Sstevel bp->devices = drmach_array_new(0, max_devices); 479703831d35Sstevel 479803831d35Sstevel if (bp->tree == NULL) 479903831d35Sstevel bp->tree = drmach_node_new(); 480003831d35Sstevel 480103831d35Sstevel data.obj = bp; 480203831d35Sstevel data.ndevs = 0; 480303831d35Sstevel data.found = found; 480403831d35Sstevel data.a = a; 480503831d35Sstevel data.err = NULL; 480603831d35Sstevel 480703831d35Sstevel mutex_enter(&drmach_slice_table_lock); 480803831d35Sstevel mutex_enter(&drmach_bus_sync_lock); 480903831d35Sstevel 481003831d35Sstevel rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb); 481103831d35Sstevel 481203831d35Sstevel drmach_slice_table_update(bp, 0); 481303831d35Sstevel drmach_bus_sync_list_update(); 481403831d35Sstevel 481503831d35Sstevel mutex_exit(&drmach_bus_sync_lock); 481603831d35Sstevel mutex_exit(&drmach_slice_table_lock); 481703831d35Sstevel 481803831d35Sstevel if (rv == 0) { 481903831d35Sstevel err = NULL; 482003831d35Sstevel drmach_slot1_lpa_set(bp); 482103831d35Sstevel } else { 482203831d35Sstevel drmach_array_dispose(bp->devices, drmach_device_dispose); 482303831d35Sstevel bp->devices = NULL; 482403831d35Sstevel 482503831d35Sstevel if (data.err) 482603831d35Sstevel err = data.err; 482703831d35Sstevel else 482803831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 482903831d35Sstevel } 483003831d35Sstevel 483103831d35Sstevel return (err); 483203831d35Sstevel } 483303831d35Sstevel 483403831d35Sstevel int 483503831d35Sstevel drmach_board_lookup(int bnum, drmachid_t *id) 483603831d35Sstevel { 483703831d35Sstevel int rv = 0; 483803831d35Sstevel 483903831d35Sstevel if (!drmach_initialized && drmach_init() == -1) { 484003831d35Sstevel *id = 0; 484103831d35Sstevel return (-1); 484203831d35Sstevel } 484303831d35Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER); 484403831d35Sstevel if (drmach_array_get(drmach_boards, bnum, id)) { 484503831d35Sstevel *id = 0; 484603831d35Sstevel rv = -1; 484703831d35Sstevel } else { 484803831d35Sstevel caddr_t obufp; 484903831d35Sstevel dr_showboard_t shb; 485003831d35Sstevel sbd_error_t *err = NULL; 485103831d35Sstevel drmach_board_t *bp; 485203831d35Sstevel 485303831d35Sstevel bp = *id; 485403831d35Sstevel 485503831d35Sstevel if (bp) 485603831d35Sstevel rw_downgrade(&drmach_boards_rwlock); 485703831d35Sstevel 485803831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 485903831d35Sstevel err = drmach_mbox_trans(DRMSG_SHOWBOARD, bnum, obufp, 486003831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)&shb, 486103831d35Sstevel sizeof (dr_showboard_t)); 486203831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 486303831d35Sstevel 486403831d35Sstevel if (err) { 486503831d35Sstevel if (err->e_code == ESTC_UNAVAILABLE) { 486603831d35Sstevel *id = 0; 486703831d35Sstevel rv = -1; 486803831d35Sstevel } 486903831d35Sstevel sbd_err_clear(&err); 487003831d35Sstevel } else { 487103831d35Sstevel if (!bp) 487203831d35Sstevel bp = *id = (drmachid_t)drmach_board_new(bnum); 487303831d35Sstevel bp->connected = (shb.bd_assigned && shb.bd_active); 487403831d35Sstevel bp->empty = shb.slot_empty; 487503831d35Sstevel 487603831d35Sstevel switch (shb.test_status) { 487703831d35Sstevel case DR_TEST_STATUS_UNKNOWN: 487803831d35Sstevel case DR_TEST_STATUS_IPOST: 487903831d35Sstevel case DR_TEST_STATUS_ABORTED: 488003831d35Sstevel bp->cond = SBD_COND_UNKNOWN; 488103831d35Sstevel break; 488203831d35Sstevel case DR_TEST_STATUS_PASSED: 488303831d35Sstevel bp->cond = SBD_COND_OK; 488403831d35Sstevel break; 488503831d35Sstevel case DR_TEST_STATUS_FAILED: 488603831d35Sstevel bp->cond = SBD_COND_FAILED; 488703831d35Sstevel break; 488803831d35Sstevel default: 488903831d35Sstevel bp->cond = SBD_COND_UNKNOWN; 489003831d35Sstevel DRMACH_PR("Unknown test status=0x%x from SC\n", 489103831d35Sstevel shb.test_status); 489203831d35Sstevel break; 489303831d35Sstevel } 489407d06da5SSurya Prakki (void) strncpy(bp->type, shb.board_type, 489507d06da5SSurya Prakki sizeof (bp->type)); 489603831d35Sstevel bp->assigned = shb.bd_assigned; 489703831d35Sstevel bp->powered = shb.power_on; 489803831d35Sstevel } 489903831d35Sstevel } 490003831d35Sstevel rw_exit(&drmach_boards_rwlock); 490103831d35Sstevel return (rv); 490203831d35Sstevel } 490303831d35Sstevel 490403831d35Sstevel sbd_error_t * 490503831d35Sstevel drmach_board_name(int bnum, char *buf, int buflen) 490603831d35Sstevel { 490707d06da5SSurya Prakki (void) snprintf(buf, buflen, "%s%d", DRMACH_BNUM2SLOT(bnum) ? 490803831d35Sstevel "IO" : "SB", DRMACH_BNUM2EXP(bnum)); 490903831d35Sstevel 491003831d35Sstevel return (NULL); 491103831d35Sstevel } 491203831d35Sstevel 491303831d35Sstevel sbd_error_t * 491403831d35Sstevel drmach_board_poweroff(drmachid_t id) 491503831d35Sstevel { 491603831d35Sstevel drmach_board_t *bp; 491703831d35Sstevel sbd_error_t *err; 491803831d35Sstevel drmach_status_t stat; 491903831d35Sstevel 492003831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 492103831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 492203831d35Sstevel bp = id; 492303831d35Sstevel 492403831d35Sstevel err = drmach_board_status(id, &stat); 492503831d35Sstevel if (!err) { 492603831d35Sstevel if (stat.configured || stat.busy) 492703831d35Sstevel err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name); 492803831d35Sstevel else { 492903831d35Sstevel caddr_t obufp; 493003831d35Sstevel 493103831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 493203831d35Sstevel err = drmach_mbox_trans(DRMSG_POWEROFF, bp->bnum, obufp, 493303831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0); 493403831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 493503831d35Sstevel if (!err) 493603831d35Sstevel bp->powered = 0; 493703831d35Sstevel } 493803831d35Sstevel } 493903831d35Sstevel return (err); 494003831d35Sstevel } 494103831d35Sstevel 494203831d35Sstevel sbd_error_t * 494303831d35Sstevel drmach_board_poweron(drmachid_t id) 494403831d35Sstevel { 494503831d35Sstevel drmach_board_t *bp; 494603831d35Sstevel caddr_t obufp; 494703831d35Sstevel sbd_error_t *err; 494803831d35Sstevel 494903831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 495003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 495103831d35Sstevel bp = id; 495203831d35Sstevel 495303831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 495403831d35Sstevel err = drmach_mbox_trans(DRMSG_POWERON, bp->bnum, obufp, 495503831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0); 495603831d35Sstevel if (!err) 495703831d35Sstevel bp->powered = 1; 495803831d35Sstevel 495903831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 496003831d35Sstevel 496103831d35Sstevel return (err); 496203831d35Sstevel } 496303831d35Sstevel 496403831d35Sstevel static sbd_error_t * 496503831d35Sstevel drmach_board_release(drmachid_t id) 496603831d35Sstevel { 496703831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 496803831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 496903831d35Sstevel return (NULL); 497003831d35Sstevel } 497103831d35Sstevel 497203831d35Sstevel sbd_error_t * 497303831d35Sstevel drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force) 497403831d35Sstevel { 497503831d35Sstevel drmach_board_t *bp; 497625cf1a30Sjl139090 drmach_device_t *dp[MAX_CORES_PER_CMP]; 497703831d35Sstevel dr_mbox_msg_t *obufp; 497803831d35Sstevel sbd_error_t *err; 497903831d35Sstevel dr_testboard_reply_t tbr; 498003831d35Sstevel int cpylen; 498103831d35Sstevel char *copts; 498203831d35Sstevel int is_io; 498325cf1a30Sjl139090 cpu_flag_t oflags[MAX_CORES_PER_CMP]; 498403831d35Sstevel 498503831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 498603831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 498703831d35Sstevel bp = id; 498803831d35Sstevel 498903831d35Sstevel /* 499003831d35Sstevel * If the board is an I/O or MAXCAT board, setup I/O cage for 499103831d35Sstevel * testing. Slot 1 indicates I/O or MAXCAT board. 499203831d35Sstevel */ 499303831d35Sstevel 499403831d35Sstevel is_io = DRMACH_BNUM2SLOT(bp->bnum); 499503831d35Sstevel 499603831d35Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP); 499703831d35Sstevel 499803831d35Sstevel if (force) 499903831d35Sstevel obufp->msgdata.dm_tb.force = 1; 500003831d35Sstevel 500103831d35Sstevel obufp->msgdata.dm_tb.immediate = 1; 500203831d35Sstevel 500303831d35Sstevel if ((opts->size > 0) && ((copts = opts->copts) != NULL)) { 500403831d35Sstevel cpylen = (opts->size > DR_HPOPTLEN ? DR_HPOPTLEN : opts->size); 500503831d35Sstevel bcopy(copts, obufp->msgdata.dm_tb.hpost_opts, cpylen); 500603831d35Sstevel } 500703831d35Sstevel 500803831d35Sstevel if (is_io) { 500903831d35Sstevel err = drmach_iocage_setup(&obufp->msgdata.dm_tb, dp, oflags); 501003831d35Sstevel 501103831d35Sstevel if (err) { 501203831d35Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t)); 501303831d35Sstevel return (err); 501403831d35Sstevel } 501503831d35Sstevel } 501603831d35Sstevel 501703831d35Sstevel err = drmach_mbox_trans(DRMSG_TESTBOARD, bp->bnum, (caddr_t)obufp, 501803831d35Sstevel sizeof (dr_mbox_msg_t), (caddr_t)&tbr, sizeof (tbr)); 501903831d35Sstevel 502003831d35Sstevel if (!err) 502103831d35Sstevel bp->cond = SBD_COND_OK; 502203831d35Sstevel else 502303831d35Sstevel bp->cond = SBD_COND_UNKNOWN; 502403831d35Sstevel 502503831d35Sstevel if ((!err) && (tbr.test_status != DR_TEST_STATUS_PASSED)) { 502603831d35Sstevel /* examine test status */ 502703831d35Sstevel switch (tbr.test_status) { 502803831d35Sstevel case DR_TEST_STATUS_IPOST: 502903831d35Sstevel bp->cond = SBD_COND_UNKNOWN; 5030d3d50737SRafael Vanoni err = drerr_new(0, ESTC_TEST_IN_PROGRESS, NULL); 503103831d35Sstevel break; 503203831d35Sstevel case DR_TEST_STATUS_UNKNOWN: 503303831d35Sstevel bp->cond = SBD_COND_UNKNOWN; 503403831d35Sstevel err = drerr_new(1, 503503831d35Sstevel ESTC_TEST_STATUS_UNKNOWN, NULL); 503603831d35Sstevel break; 503703831d35Sstevel case DR_TEST_STATUS_FAILED: 503803831d35Sstevel bp->cond = SBD_COND_FAILED; 5039d3d50737SRafael Vanoni err = drerr_new(1, ESTC_TEST_FAILED, NULL); 504003831d35Sstevel break; 504103831d35Sstevel case DR_TEST_STATUS_ABORTED: 504203831d35Sstevel bp->cond = SBD_COND_UNKNOWN; 5043d3d50737SRafael Vanoni err = drerr_new(1, ESTC_TEST_ABORTED, NULL); 504403831d35Sstevel break; 504503831d35Sstevel default: 504603831d35Sstevel bp->cond = SBD_COND_UNKNOWN; 5047d3d50737SRafael Vanoni err = drerr_new(1, ESTC_TEST_RESULT_UNKNOWN, 504803831d35Sstevel NULL); 504903831d35Sstevel break; 505003831d35Sstevel } 505103831d35Sstevel } 505203831d35Sstevel 505303831d35Sstevel /* 505403831d35Sstevel * If I/O cage test was performed, check for availability of the 505503831d35Sstevel * cpu used. If cpu has been returned, it's OK to proceed with 505603831d35Sstevel * reconfiguring it for use. 505703831d35Sstevel */ 505803831d35Sstevel if (is_io) { 505903831d35Sstevel DRMACH_PR("drmach_board_test: tbr.cpu_recovered: %d", 506003831d35Sstevel tbr.cpu_recovered); 506103831d35Sstevel DRMACH_PR("drmach_board_test: port id: %d", 506203831d35Sstevel tbr.cpu_portid); 506303831d35Sstevel 506403831d35Sstevel /* 506503831d35Sstevel * Check the cpu_recovered flag in the testboard reply, or 506603831d35Sstevel * if the testboard request message was not sent to SMS due 506703831d35Sstevel * to an mboxsc_putmsg() failure, it's OK to recover the 506803831d35Sstevel * cpu since hpost hasn't touched it. 506903831d35Sstevel */ 507003831d35Sstevel if ((tbr.cpu_recovered && tbr.cpu_portid == 507103831d35Sstevel obufp->msgdata.dm_tb.cpu_portid) || 507203831d35Sstevel ((err) && (err->e_code == ESTC_MBXRQST))) { 507303831d35Sstevel 507403831d35Sstevel int i; 507503831d35Sstevel 507603831d35Sstevel mutex_enter(&cpu_lock); 507725cf1a30Sjl139090 for (i = 0; i < MAX_CORES_PER_CMP; i++) { 507803831d35Sstevel if (dp[i] != NULL) { 507903831d35Sstevel (void) drmach_iocage_cpu_return(dp[i], 508003831d35Sstevel oflags[i]); 508103831d35Sstevel } 508203831d35Sstevel } 508303831d35Sstevel mutex_exit(&cpu_lock); 508403831d35Sstevel } else { 508503831d35Sstevel cmn_err(CE_WARN, "Unable to recover port id %d " 508603831d35Sstevel "after I/O cage test: cpu_recovered=%d, " 508703831d35Sstevel "returned portid=%d", 508803831d35Sstevel obufp->msgdata.dm_tb.cpu_portid, 508903831d35Sstevel tbr.cpu_recovered, tbr.cpu_portid); 509003831d35Sstevel } 509107d06da5SSurya Prakki (void) drmach_iocage_mem_return(&tbr); 509203831d35Sstevel } 509303831d35Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t)); 509403831d35Sstevel 509503831d35Sstevel return (err); 509603831d35Sstevel } 509703831d35Sstevel 509803831d35Sstevel sbd_error_t * 509903831d35Sstevel drmach_board_unassign(drmachid_t id) 510003831d35Sstevel { 510103831d35Sstevel drmach_board_t *bp; 510203831d35Sstevel sbd_error_t *err; 510303831d35Sstevel drmach_status_t stat; 510403831d35Sstevel caddr_t obufp; 510503831d35Sstevel 510603831d35Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER); 510703831d35Sstevel 510803831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) { 510903831d35Sstevel rw_exit(&drmach_boards_rwlock); 511003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 511103831d35Sstevel } 511203831d35Sstevel bp = id; 511303831d35Sstevel 511403831d35Sstevel err = drmach_board_status(id, &stat); 511503831d35Sstevel if (err) { 511603831d35Sstevel rw_exit(&drmach_boards_rwlock); 511703831d35Sstevel return (err); 511803831d35Sstevel } 511903831d35Sstevel 512003831d35Sstevel if (stat.configured || stat.busy) { 512103831d35Sstevel err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name); 512203831d35Sstevel } else { 512303831d35Sstevel 512403831d35Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP); 512503831d35Sstevel err = drmach_mbox_trans(DRMSG_UNASSIGN, bp->bnum, obufp, 512603831d35Sstevel sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0); 512703831d35Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t)); 512803831d35Sstevel if (!err) { 512903831d35Sstevel if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0) 513003831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 513103831d35Sstevel else 513203831d35Sstevel drmach_board_dispose(bp); 513303831d35Sstevel } 513403831d35Sstevel } 513503831d35Sstevel rw_exit(&drmach_boards_rwlock); 513603831d35Sstevel return (err); 513703831d35Sstevel } 513803831d35Sstevel 513903831d35Sstevel static sbd_error_t * 514003831d35Sstevel drmach_read_reg_addr(drmach_device_t *dp, uint64_t *p) 514103831d35Sstevel { 514203831d35Sstevel int len; 514303831d35Sstevel drmach_reg_t reg; 514403831d35Sstevel drmach_node_t pp; 514503831d35Sstevel drmach_node_t *np = dp->node; 514603831d35Sstevel 514703831d35Sstevel /* 514803831d35Sstevel * If the node does not have a portid property, 514903831d35Sstevel * it represents a CMP device. For a CMP, the reg 515003831d35Sstevel * property of the parent holds the information of 515103831d35Sstevel * interest. 515203831d35Sstevel */ 515303831d35Sstevel if (dp->node->n_getproplen(dp->node, "portid", &len) != 0) { 515403831d35Sstevel 515503831d35Sstevel if (dp->node->get_parent(dp->node, &pp) != 0) { 515603831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 515703831d35Sstevel } 515803831d35Sstevel np = &pp; 515903831d35Sstevel } 516003831d35Sstevel 516103831d35Sstevel if (np->n_getproplen(np, "reg", &len) != 0) 516203831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 516303831d35Sstevel 516403831d35Sstevel if (len != sizeof (reg)) 516503831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 516603831d35Sstevel 516703831d35Sstevel if (np->n_getprop(np, "reg", ®, sizeof (reg)) != 0) 516803831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 516903831d35Sstevel 517003831d35Sstevel /* reassemble 64-bit base address */ 517103831d35Sstevel *p = ((uint64_t)reg.reg_addr_hi << 32) | reg.reg_addr_lo; 517203831d35Sstevel 517303831d35Sstevel return (NULL); 517403831d35Sstevel } 517503831d35Sstevel 517603831d35Sstevel static void 517703831d35Sstevel drmach_cpu_read(uint64_t arg1, uint64_t arg2) 517803831d35Sstevel { 517903831d35Sstevel uint64_t *saf_config_reg = (uint64_t *)arg1; 518003831d35Sstevel uint_t *reg_read = (uint_t *)arg2; 518103831d35Sstevel 518203831d35Sstevel *saf_config_reg = lddsafconfig(); 518303831d35Sstevel *reg_read = 0x1; 518403831d35Sstevel } 518503831d35Sstevel 518603831d35Sstevel /* 518703831d35Sstevel * A return value of 1 indicates success and 0 indicates a failure 518803831d35Sstevel */ 518903831d35Sstevel static int 519003831d35Sstevel drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr) 519103831d35Sstevel { 519203831d35Sstevel 519303831d35Sstevel int rv = 0x0; 519403831d35Sstevel 519503831d35Sstevel *scr = 0x0; 519603831d35Sstevel 519703831d35Sstevel /* 519803831d35Sstevel * Confirm cpu was in ready set when xc was issued. 519903831d35Sstevel * This is done by verifying rv which is 520003831d35Sstevel * set to 0x1 when xc_one is successful. 520103831d35Sstevel */ 520203831d35Sstevel xc_one(cp->dev.portid, (xcfunc_t *)drmach_cpu_read, 520303831d35Sstevel (uint64_t)scr, (uint64_t)&rv); 520403831d35Sstevel 520503831d35Sstevel return (rv); 520603831d35Sstevel 520703831d35Sstevel } 520803831d35Sstevel 520903831d35Sstevel static sbd_error_t * 521003831d35Sstevel drmach_cpu_read_cpuid(drmach_cpu_t *cp, processorid_t *cpuid) 521103831d35Sstevel { 521203831d35Sstevel drmach_node_t *np; 521303831d35Sstevel 521403831d35Sstevel np = cp->dev.node; 521503831d35Sstevel 521603831d35Sstevel /* 521703831d35Sstevel * If a CPU does not have a portid property, it must 521803831d35Sstevel * be a CMP device with a cpuid property. 521903831d35Sstevel */ 522003831d35Sstevel if (np->n_getprop(np, "portid", cpuid, sizeof (*cpuid)) != 0) { 522103831d35Sstevel 522203831d35Sstevel if (np->n_getprop(np, "cpuid", cpuid, sizeof (*cpuid)) != 0) { 522303831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 522403831d35Sstevel } 522503831d35Sstevel } 522603831d35Sstevel 522703831d35Sstevel return (NULL); 522803831d35Sstevel } 522903831d35Sstevel 523003831d35Sstevel /* Starcat CMP core id is bit 2 of the cpuid */ 523103831d35Sstevel #define DRMACH_COREID_MASK (1u << 2) 523203831d35Sstevel #define DRMACH_CPUID2SRAM_IDX(id) \ 523303831d35Sstevel ((id & DRMACH_COREID_MASK) >> 1 | (id & 0x1)) 523403831d35Sstevel 523503831d35Sstevel static sbd_error_t * 523603831d35Sstevel drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp) 523703831d35Sstevel { 523803831d35Sstevel sbd_error_t *err; 523903831d35Sstevel uint64_t scr_pa; 524003831d35Sstevel drmach_cpu_t *cp = NULL; 524103831d35Sstevel pfn_t pfn; 524203831d35Sstevel uint64_t cpu_stardrb_offset, cpu_sram_pa; 524303831d35Sstevel int idx; 524403831d35Sstevel int impl; 524503831d35Sstevel processorid_t cpuid; 524603831d35Sstevel 524703831d35Sstevel err = drmach_read_reg_addr(proto, &scr_pa); 524803831d35Sstevel if (err) { 524903831d35Sstevel goto fail; 525003831d35Sstevel } 525103831d35Sstevel 525203831d35Sstevel cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP); 525303831d35Sstevel bcopy(proto, &cp->dev, sizeof (cp->dev)); 525403831d35Sstevel cp->dev.node = drmach_node_dup(proto->node); 525503831d35Sstevel cp->dev.cm.isa = (void *)drmach_cpu_new; 525603831d35Sstevel cp->dev.cm.dispose = drmach_cpu_dispose; 525703831d35Sstevel cp->dev.cm.release = drmach_cpu_release; 525803831d35Sstevel cp->dev.cm.status = drmach_cpu_status; 525903831d35Sstevel cp->scr_pa = scr_pa; 526003831d35Sstevel 526103831d35Sstevel err = drmach_cpu_read_cpuid(cp, &cpuid); 526203831d35Sstevel if (err) { 526303831d35Sstevel goto fail; 526403831d35Sstevel } 526503831d35Sstevel 526603831d35Sstevel err = drmach_cpu_get_impl(cp, &impl); 526703831d35Sstevel if (err) { 526803831d35Sstevel goto fail; 526903831d35Sstevel } 527003831d35Sstevel 527103831d35Sstevel cp->cpuid = cpuid; 527203831d35Sstevel cp->coreid = STARCAT_CPUID_TO_COREID(cp->cpuid); 527303831d35Sstevel cp->dev.unum = STARCAT_CPUID_TO_AGENT(cp->cpuid); 527403831d35Sstevel 527503831d35Sstevel /* 527603831d35Sstevel * Init the board cpu type. Assumes all board cpus are the same type. 527703831d35Sstevel */ 527803831d35Sstevel if (cp->dev.bp->cpu_impl == 0) { 527903831d35Sstevel cp->dev.bp->cpu_impl = impl; 528003831d35Sstevel } 528103831d35Sstevel ASSERT(cp->dev.bp->cpu_impl == impl); 528203831d35Sstevel 528303831d35Sstevel /* 528403831d35Sstevel * XXX CHEETAH SUPPORT 528503831d35Sstevel * determine if the domain uses Cheetah procs 528603831d35Sstevel */ 528703831d35Sstevel if (drmach_is_cheetah < 0) { 528803831d35Sstevel drmach_is_cheetah = IS_CHEETAH(impl); 528903831d35Sstevel } 529003831d35Sstevel 529103831d35Sstevel /* 529203831d35Sstevel * Initialize TTE for mapping CPU SRAM STARDRB buffer. 529303831d35Sstevel * The STARDRB buffer (16KB on Cheetah+ boards, 32KB on 529403831d35Sstevel * Jaguar/Panther boards) is shared by all cpus in a Safari port 529503831d35Sstevel * pair. Each cpu uses 8KB according to the following layout: 529603831d35Sstevel * 529703831d35Sstevel * Page 0: even numbered Cheetah+'s and Panther/Jaguar core 0's 529803831d35Sstevel * Page 1: odd numbered Cheetah+'s and Panther/Jaguar core 0's 529903831d35Sstevel * Page 2: even numbered Panther/Jaguar core 1's 530003831d35Sstevel * Page 3: odd numbered Panther/Jaguar core 1's 530103831d35Sstevel */ 530203831d35Sstevel idx = DRMACH_CPUID2SRAM_IDX(cp->cpuid); 530303831d35Sstevel cpu_stardrb_offset = cp->dev.bp->stardrb_offset + (PAGESIZE * idx); 530403831d35Sstevel cpu_sram_pa = DRMACH_CPU_SRAM_ADDR + cpu_stardrb_offset; 530503831d35Sstevel pfn = cpu_sram_pa >> PAGESHIFT; 530603831d35Sstevel 530703831d35Sstevel ASSERT(drmach_cpu_sram_tte[cp->cpuid].tte_inthi == 0 && 530803831d35Sstevel drmach_cpu_sram_tte[cp->cpuid].tte_intlo == 0); 530903831d35Sstevel drmach_cpu_sram_tte[cp->cpuid].tte_inthi = TTE_PFN_INTHI(pfn) | 531003831d35Sstevel TTE_VALID_INT | TTE_SZ_INT(TTE8K); 531103831d35Sstevel drmach_cpu_sram_tte[cp->cpuid].tte_intlo = TTE_PFN_INTLO(pfn) | 531203831d35Sstevel TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT; 531303831d35Sstevel 531403831d35Sstevel DRMACH_PR("drmach_cpu_new: cpuid=%d, coreid=%d, stardrb_offset=0x%lx, " 531503831d35Sstevel "cpu_sram_offset=0x%lx, idx=%d\n", cp->cpuid, cp->coreid, 531603831d35Sstevel cp->dev.bp->stardrb_offset, cpu_stardrb_offset, idx); 531703831d35Sstevel 531807d06da5SSurya Prakki (void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d", 531903831d35Sstevel cp->dev.type, cp->dev.unum); 532003831d35Sstevel 532103831d35Sstevel *idp = (drmachid_t)cp; 532203831d35Sstevel return (NULL); 532303831d35Sstevel 532403831d35Sstevel fail: 532503831d35Sstevel if (cp) { 532603831d35Sstevel drmach_node_dispose(cp->dev.node); 532703831d35Sstevel kmem_free(cp, sizeof (*cp)); 532803831d35Sstevel } 532903831d35Sstevel 533003831d35Sstevel *idp = (drmachid_t)0; 533103831d35Sstevel return (err); 533203831d35Sstevel } 533303831d35Sstevel 533403831d35Sstevel static void 533503831d35Sstevel drmach_cpu_dispose(drmachid_t id) 533603831d35Sstevel { 533703831d35Sstevel drmach_cpu_t *self; 533803831d35Sstevel processorid_t cpuid; 533903831d35Sstevel 534003831d35Sstevel ASSERT(DRMACH_IS_CPU_ID(id)); 534103831d35Sstevel 534203831d35Sstevel self = id; 534303831d35Sstevel if (self->dev.node) 534403831d35Sstevel drmach_node_dispose(self->dev.node); 534503831d35Sstevel 534603831d35Sstevel cpuid = self->cpuid; 534703831d35Sstevel ASSERT(TTE_IS_VALID(&drmach_cpu_sram_tte[cpuid]) && 534803831d35Sstevel TTE_IS_8K(&drmach_cpu_sram_tte[cpuid]) && 534903831d35Sstevel TTE_IS_PRIVILEGED(&drmach_cpu_sram_tte[cpuid]) && 535003831d35Sstevel TTE_IS_LOCKED(&drmach_cpu_sram_tte[cpuid])); 535103831d35Sstevel drmach_cpu_sram_tte[cpuid].tte_inthi = 0; 535203831d35Sstevel drmach_cpu_sram_tte[cpuid].tte_intlo = 0; 535303831d35Sstevel 535403831d35Sstevel kmem_free(self, sizeof (*self)); 535503831d35Sstevel } 535603831d35Sstevel 535703831d35Sstevel static int 535803831d35Sstevel drmach_cpu_start(struct cpu *cp) 535903831d35Sstevel { 536003831d35Sstevel extern xcfunc_t drmach_set_lpa; 536103831d35Sstevel extern void restart_other_cpu(int); 536203831d35Sstevel int cpuid = cp->cpu_id; 536303831d35Sstevel int rv, bnum; 536403831d35Sstevel drmach_board_t *bp; 536503831d35Sstevel 536603831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 536703831d35Sstevel ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0); 536803831d35Sstevel 536903831d35Sstevel cp->cpu_flags &= ~CPU_POWEROFF; 537003831d35Sstevel 537103831d35Sstevel /* 537203831d35Sstevel * NOTE: restart_other_cpu pauses cpus during the 537303831d35Sstevel * slave cpu start. This helps to quiesce the 537403831d35Sstevel * bus traffic a bit which makes the tick sync 537503831d35Sstevel * routine in the prom more robust. 537603831d35Sstevel */ 537703831d35Sstevel DRMACH_PR("COLD START for cpu (%d)\n", cpuid); 537803831d35Sstevel 537903831d35Sstevel if (prom_hotaddcpu(cpuid) != 0) { 538003831d35Sstevel cmn_err(CE_PANIC, "prom_hotaddcpu() for cpuid=%d failed.", 538103831d35Sstevel cpuid); 538203831d35Sstevel } 538303831d35Sstevel 538403831d35Sstevel restart_other_cpu(cpuid); 538503831d35Sstevel 538603831d35Sstevel bnum = drmach_portid2bnum(cpunodes[cpuid].portid); 538703831d35Sstevel rv = drmach_array_get(drmach_boards, bnum, (drmachid_t)&bp); 538803831d35Sstevel if (rv == -1 || bp == NULL) { 538903831d35Sstevel DRMACH_PR("drmach_cpu_start: cannot read board info for " 539007d06da5SSurya Prakki "cpuid=%d: rv=%d, bp=%p\n", cpuid, rv, (void *)bp); 539103831d35Sstevel } else if (DRMACH_L1_SET_LPA(bp) && drmach_reprogram_lpa) { 539203831d35Sstevel int exp; 539303831d35Sstevel int ntries; 539403831d35Sstevel 539503831d35Sstevel mutex_enter(&drmach_xt_mb_lock); 539603831d35Sstevel mutex_enter(&drmach_slice_table_lock); 539703831d35Sstevel bzero((void *)drmach_xt_mb, drmach_xt_mb_size); 539803831d35Sstevel 539903831d35Sstevel /* 540003831d35Sstevel * drmach_slice_table[*] 540103831d35Sstevel * bit 5 valid 540203831d35Sstevel * bit 0:4 slice number 540303831d35Sstevel * 540403831d35Sstevel * drmach_xt_mb[*] format for drmach_set_lpa 540503831d35Sstevel * bit 7 valid 540603831d35Sstevel * bit 6 set null LPA (overrides bits 0:4) 540703831d35Sstevel * bit 0:4 slice number 540803831d35Sstevel * 540903831d35Sstevel * drmach_set_lpa derives processor CBASE and CBND 541003831d35Sstevel * from bits 6 and 0:4 of drmach_xt_mb. If bit 6 is 541103831d35Sstevel * set, then CBASE = CBND = 0. Otherwise, CBASE = slice 541203831d35Sstevel * number; CBND = slice number + 1. 541303831d35Sstevel * No action is taken if bit 7 is zero. 541403831d35Sstevel */ 541503831d35Sstevel exp = (cpuid >> 5) & 0x1f; 541603831d35Sstevel if (drmach_slice_table[exp] & 0x20) { 541703831d35Sstevel drmach_xt_mb[cpuid] = 0x80 | 541803831d35Sstevel (drmach_slice_table[exp] & 0x1f); 541903831d35Sstevel } else { 542003831d35Sstevel drmach_xt_mb[cpuid] = 0x80 | 0x40; 542103831d35Sstevel } 542203831d35Sstevel 542303831d35Sstevel drmach_xt_ready = 0; 542403831d35Sstevel 542503831d35Sstevel xt_one(cpuid, drmach_set_lpa, NULL, NULL); 542603831d35Sstevel 542703831d35Sstevel ntries = drmach_cpu_ntries; 542803831d35Sstevel while (!drmach_xt_ready && ntries) { 542903831d35Sstevel DELAY(drmach_cpu_delay); 543003831d35Sstevel ntries--; 543103831d35Sstevel } 543203831d35Sstevel 543303831d35Sstevel mutex_exit(&drmach_slice_table_lock); 543403831d35Sstevel mutex_exit(&drmach_xt_mb_lock); 543503831d35Sstevel 543603831d35Sstevel DRMACH_PR( 543703831d35Sstevel "waited %d out of %d tries for drmach_set_lpa on cpu%d", 543803831d35Sstevel drmach_cpu_ntries - ntries, drmach_cpu_ntries, 543903831d35Sstevel cp->cpu_id); 544003831d35Sstevel } 544103831d35Sstevel 5442d3d50737SRafael Vanoni xt_one(cpuid, vtag_flushpage_tl1, (uint64_t)drmach_cpu_sram_va, 5443d3d50737SRafael Vanoni (uint64_t)ksfmmup); 544403831d35Sstevel 544503831d35Sstevel return (0); 544603831d35Sstevel } 544703831d35Sstevel 544803831d35Sstevel /* 544903831d35Sstevel * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after 545003831d35Sstevel * it has been offlined. The function of this routine is to get the cpu 545103831d35Sstevel * spinning in a safe place. The requirement is that the system will not 545203831d35Sstevel * reference anything on the detaching board (memory and i/o is detached 545303831d35Sstevel * elsewhere) and that the CPU not reference anything on any other board 545403831d35Sstevel * in the system. This isolation is required during and after the writes 545503831d35Sstevel * to the domain masks to remove the board from the domain. 545603831d35Sstevel * 545703831d35Sstevel * To accomplish this isolation the following is done: 545803831d35Sstevel * 1) Create a locked mapping to the STARDRB data buffer located 545903831d35Sstevel * in this cpu's sram. There is one TTE per cpu, initialized in 546003831d35Sstevel * drmach_cpu_new(). The cpuid is used to select which TTE to use. 546103831d35Sstevel * Each Safari port pair shares the CPU SRAM on a Serengeti CPU/MEM 546203831d35Sstevel * board. The STARDRB buffer is 16KB on Cheetah+ boards, 32KB on Jaguar 546303831d35Sstevel * boards. Each STARDRB buffer is logically divided by DR into one 546403831d35Sstevel * 8KB page per cpu (or Jaguar core). 546503831d35Sstevel * 2) Copy the target function (drmach_shutdown_asm) into buffer. 546603831d35Sstevel * 3) Jump to function now in the cpu sram. 546703831d35Sstevel * Function will: 546803831d35Sstevel * 3.1) Flush its Ecache (displacement). 546903831d35Sstevel * 3.2) Flush its Dcache with HW mechanism. 547003831d35Sstevel * 3.3) Flush its Icache with HW mechanism. 547103831d35Sstevel * 3.4) Flush all valid and _unlocked_ D-TLB and I-TLB entries. 547203831d35Sstevel * 3.5) Set LPA to NULL 547303831d35Sstevel * 3.6) Clear xt_mb to signal completion. Note: cache line is 547403831d35Sstevel * recovered by drmach_cpu_poweroff(). 547503831d35Sstevel * 4) Jump into an infinite loop. 547603831d35Sstevel */ 547703831d35Sstevel 547803831d35Sstevel static void 547903831d35Sstevel drmach_cpu_stop_self(void) 548003831d35Sstevel { 5481d3d50737SRafael Vanoni extern void drmach_shutdown_asm(uint64_t, uint64_t, int, int, uint64_t); 548203831d35Sstevel extern void drmach_shutdown_asm_end(void); 548303831d35Sstevel 548403831d35Sstevel tte_t *tte; 548503831d35Sstevel uint_t *p, *q; 548603831d35Sstevel uint64_t stack_pointer; 548703831d35Sstevel 548803831d35Sstevel ASSERT(((ptrdiff_t)drmach_shutdown_asm_end - 548903831d35Sstevel (ptrdiff_t)drmach_shutdown_asm) < PAGESIZE); 549003831d35Sstevel 549103831d35Sstevel tte = &drmach_cpu_sram_tte[CPU->cpu_id]; 5492d3d50737SRafael Vanoni ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) && TTE_IS_PRIVILEGED(tte) && 5493d3d50737SRafael Vanoni TTE_IS_LOCKED(tte)); 54941e2e7a75Shuah sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte); 54951e2e7a75Shuah sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte); 549603831d35Sstevel 549703831d35Sstevel /* copy text. standard bcopy not designed to work in nc space */ 549803831d35Sstevel p = (uint_t *)drmach_cpu_sram_va; 549903831d35Sstevel q = (uint_t *)drmach_shutdown_asm; 550003831d35Sstevel while (q < (uint_t *)drmach_shutdown_asm_end) 550103831d35Sstevel *p++ = *q++; 550203831d35Sstevel 550303831d35Sstevel /* zero to assist debug */ 550403831d35Sstevel q = (uint_t *)(drmach_cpu_sram_va + PAGESIZE); 550503831d35Sstevel while (p < q) 550603831d35Sstevel *p++ = 0; 550703831d35Sstevel 550803831d35Sstevel /* a parking spot for the stack pointer */ 550903831d35Sstevel stack_pointer = (uint64_t)q; 551003831d35Sstevel 551103831d35Sstevel /* call copy of drmach_shutdown_asm */ 551203831d35Sstevel (*(void (*)())drmach_cpu_sram_va)( 551303831d35Sstevel stack_pointer, 551403831d35Sstevel drmach_iocage_paddr, 551503831d35Sstevel cpunodes[CPU->cpu_id].ecache_size, 551603831d35Sstevel cpunodes[CPU->cpu_id].ecache_linesize, 551703831d35Sstevel va_to_pa((void *)&drmach_xt_mb[CPU->cpu_id])); 551803831d35Sstevel } 551903831d35Sstevel 552003831d35Sstevel static void 552103831d35Sstevel drmach_cpu_shutdown_self(void) 552203831d35Sstevel { 552303831d35Sstevel cpu_t *cp = CPU; 552403831d35Sstevel int cpuid = cp->cpu_id; 552503831d35Sstevel extern void flush_windows(void); 552603831d35Sstevel 552703831d35Sstevel flush_windows(); 552803831d35Sstevel 552903831d35Sstevel (void) spl8(); 553003831d35Sstevel 553103831d35Sstevel ASSERT(cp->cpu_intr_actv == 0); 553203831d35Sstevel ASSERT(cp->cpu_thread == cp->cpu_idle_thread || 553303831d35Sstevel cp->cpu_thread == cp->cpu_startup_thread); 553403831d35Sstevel 553503831d35Sstevel cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF; 553603831d35Sstevel 553703831d35Sstevel drmach_cpu_stop_self(); 553803831d35Sstevel 553903831d35Sstevel cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid); 554003831d35Sstevel } 554103831d35Sstevel 554203831d35Sstevel static sbd_error_t * 554303831d35Sstevel drmach_cpu_release(drmachid_t id) 554403831d35Sstevel { 554503831d35Sstevel drmach_cpu_t *cp; 554603831d35Sstevel struct cpu *cpu; 554703831d35Sstevel sbd_error_t *err; 554803831d35Sstevel 554903831d35Sstevel if (!DRMACH_IS_CPU_ID(id)) 555003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 555103831d35Sstevel cp = id; 555203831d35Sstevel 555303831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 555403831d35Sstevel 555503831d35Sstevel cpu = cpu_get(cp->cpuid); 555603831d35Sstevel if (cpu == NULL) 555703831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 555803831d35Sstevel else 555903831d35Sstevel err = NULL; 556003831d35Sstevel 556103831d35Sstevel return (err); 556203831d35Sstevel } 556303831d35Sstevel 556403831d35Sstevel static sbd_error_t * 556503831d35Sstevel drmach_cpu_status(drmachid_t id, drmach_status_t *stat) 556603831d35Sstevel { 556703831d35Sstevel drmach_cpu_t *cp; 556803831d35Sstevel drmach_device_t *dp; 556903831d35Sstevel 557003831d35Sstevel ASSERT(DRMACH_IS_CPU_ID(id)); 557103831d35Sstevel cp = id; 557203831d35Sstevel dp = &cp->dev; 557303831d35Sstevel 557403831d35Sstevel stat->assigned = dp->bp->assigned; 557503831d35Sstevel stat->powered = dp->bp->powered; 557603831d35Sstevel mutex_enter(&cpu_lock); 557703831d35Sstevel stat->configured = (cpu_get(cp->cpuid) != NULL); 557803831d35Sstevel mutex_exit(&cpu_lock); 557903831d35Sstevel stat->busy = dp->busy; 558007d06da5SSurya Prakki (void) strncpy(stat->type, dp->type, sizeof (stat->type)); 558103831d35Sstevel stat->info[0] = '\0'; 558203831d35Sstevel 558303831d35Sstevel return (NULL); 558403831d35Sstevel } 558503831d35Sstevel 558603831d35Sstevel sbd_error_t * 558703831d35Sstevel drmach_cpu_disconnect(drmachid_t id) 558803831d35Sstevel { 558903831d35Sstevel if (!DRMACH_IS_CPU_ID(id)) 559003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 559103831d35Sstevel 559203831d35Sstevel return (NULL); 559303831d35Sstevel } 559403831d35Sstevel 559503831d35Sstevel sbd_error_t * 559603831d35Sstevel drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid) 559703831d35Sstevel { 559803831d35Sstevel drmach_cpu_t *cpu; 559903831d35Sstevel 560003831d35Sstevel if (!DRMACH_IS_CPU_ID(id)) 560103831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 560203831d35Sstevel cpu = id; 560303831d35Sstevel 560403831d35Sstevel *cpuid = cpu->cpuid; 560503831d35Sstevel return (NULL); 560603831d35Sstevel } 560703831d35Sstevel 560803831d35Sstevel sbd_error_t * 560903831d35Sstevel drmach_cpu_get_impl(drmachid_t id, int *ip) 561003831d35Sstevel { 561103831d35Sstevel drmach_node_t *np; 561203831d35Sstevel int impl; 561303831d35Sstevel 561403831d35Sstevel if (!DRMACH_IS_CPU_ID(id)) 561503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 561603831d35Sstevel 561703831d35Sstevel np = ((drmach_device_t *)id)->node; 561803831d35Sstevel 561903831d35Sstevel if (np->n_getprop(np, "implementation#", &impl, sizeof (impl)) == -1) { 562003831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 562103831d35Sstevel } 562203831d35Sstevel 562303831d35Sstevel *ip = impl; 562403831d35Sstevel 562503831d35Sstevel return (NULL); 562603831d35Sstevel } 562703831d35Sstevel 562803831d35Sstevel /* 562903831d35Sstevel * Flush this cpu's ecache, then ensure all outstanding safari 563003831d35Sstevel * transactions have retired. 563103831d35Sstevel */ 563203831d35Sstevel void 563303831d35Sstevel drmach_cpu_flush_ecache_sync(void) 563403831d35Sstevel { 563503831d35Sstevel uint64_t *p; 563603831d35Sstevel 563703831d35Sstevel ASSERT(curthread->t_bound_cpu == CPU); 563803831d35Sstevel 563903831d35Sstevel cpu_flush_ecache(); 564003831d35Sstevel 564103831d35Sstevel mutex_enter(&drmach_bus_sync_lock); 564203831d35Sstevel for (p = drmach_bus_sync_list; *p; p++) 564303831d35Sstevel (void) ldphys(*p); 564403831d35Sstevel mutex_exit(&drmach_bus_sync_lock); 564503831d35Sstevel 564603831d35Sstevel cpu_flush_ecache(); 564703831d35Sstevel } 564803831d35Sstevel 564903831d35Sstevel sbd_error_t * 565003831d35Sstevel drmach_get_dip(drmachid_t id, dev_info_t **dip) 565103831d35Sstevel { 565203831d35Sstevel drmach_device_t *dp; 565303831d35Sstevel 565403831d35Sstevel if (!DRMACH_IS_DEVICE_ID(id)) 565503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 565603831d35Sstevel dp = id; 565703831d35Sstevel 565803831d35Sstevel *dip = dp->node->n_getdip(dp->node); 565903831d35Sstevel return (NULL); 566003831d35Sstevel } 566103831d35Sstevel 566203831d35Sstevel sbd_error_t * 566303831d35Sstevel drmach_io_is_attached(drmachid_t id, int *yes) 566403831d35Sstevel { 566503831d35Sstevel drmach_device_t *dp; 566603831d35Sstevel dev_info_t *dip; 566703831d35Sstevel int state; 566803831d35Sstevel 566903831d35Sstevel if (!DRMACH_IS_IO_ID(id)) 567003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 567103831d35Sstevel dp = id; 567203831d35Sstevel 567303831d35Sstevel dip = dp->node->n_getdip(dp->node); 567403831d35Sstevel if (dip == NULL) { 567503831d35Sstevel *yes = 0; 567603831d35Sstevel return (NULL); 567703831d35Sstevel } 567803831d35Sstevel 567903831d35Sstevel state = ddi_get_devstate(dip); 568003831d35Sstevel *yes = i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP); 568103831d35Sstevel 568203831d35Sstevel return (NULL); 568303831d35Sstevel } 568403831d35Sstevel 568503831d35Sstevel static int 568603831d35Sstevel drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t *dip) 568703831d35Sstevel { 568803831d35Sstevel char dtype[OBP_MAXPROPNAME]; 568903831d35Sstevel int portid; 569003831d35Sstevel uint_t pci_csr_base; 569103831d35Sstevel struct pci_phys_spec *regbuf = NULL; 569203831d35Sstevel int rv, len; 569303831d35Sstevel 569403831d35Sstevel ASSERT(dip != NULL); 569503831d35Sstevel rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "device_type", &len); 569603831d35Sstevel if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (dtype))) 569703831d35Sstevel return (0); 569803831d35Sstevel 569903831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "device_type", 570003831d35Sstevel (caddr_t)dtype, &len) == DDI_PROP_SUCCESS) { 570103831d35Sstevel 570203831d35Sstevel if (strncmp(dtype, "pci", 3) == 0) { 570303831d35Sstevel 570403831d35Sstevel /* 570503831d35Sstevel * Get safari portid. All schizo/xmits 0 570603831d35Sstevel * safari IDs end in 0x1C. 570703831d35Sstevel */ 5708d3d50737SRafael Vanoni rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid", 5709d3d50737SRafael Vanoni &len); 571003831d35Sstevel 571103831d35Sstevel if ((rv != DDI_PROP_SUCCESS) || 571203831d35Sstevel (len > sizeof (portid))) 571303831d35Sstevel return (0); 571403831d35Sstevel 571503831d35Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, 571603831d35Sstevel "portid", (caddr_t)&portid, &len); 571703831d35Sstevel 571803831d35Sstevel if (rv != DDI_PROP_SUCCESS) 571903831d35Sstevel return (0); 572003831d35Sstevel 572103831d35Sstevel if ((portid & 0x1F) != 0x1C) 572203831d35Sstevel return (0); 572303831d35Sstevel 572403831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip, 572503831d35Sstevel DDI_PROP_DONTPASS, "reg", (caddr_t)®buf, 572603831d35Sstevel &len) == DDI_PROP_SUCCESS) { 572703831d35Sstevel 572803831d35Sstevel pci_csr_base = regbuf[0].pci_phys_mid & 572903831d35Sstevel PCI_CONF_ADDR_MASK; 573003831d35Sstevel kmem_free(regbuf, len); 573103831d35Sstevel /* 573203831d35Sstevel * All PCI B-Leafs are at configspace 0x70.0000. 573303831d35Sstevel */ 573403831d35Sstevel if (pci_csr_base == 0x700000) 573503831d35Sstevel return (1); 573603831d35Sstevel } 573703831d35Sstevel } 573803831d35Sstevel } 573903831d35Sstevel return (0); 574003831d35Sstevel } 574103831d35Sstevel 574203831d35Sstevel #define SCHIZO_BINDING_NAME "pci108e,8001" 574303831d35Sstevel #define XMITS_BINDING_NAME "pci108e,8002" 574403831d35Sstevel 574503831d35Sstevel /* 574603831d35Sstevel * Verify if the dip is an instance of MAN 'eri'. 574703831d35Sstevel */ 574803831d35Sstevel static int 574903831d35Sstevel drmach_dip_is_man_eri(dev_info_t *dip) 575003831d35Sstevel { 575103831d35Sstevel struct pci_phys_spec *regbuf = NULL; 575203831d35Sstevel dev_info_t *parent_dip; 575303831d35Sstevel char *name; 575403831d35Sstevel uint_t pci_device; 575503831d35Sstevel uint_t pci_function; 575603831d35Sstevel int len; 575703831d35Sstevel 575803831d35Sstevel if (dip == NULL) 575903831d35Sstevel return (0); 576003831d35Sstevel /* 576103831d35Sstevel * Verify if the parent is schizo(xmits)0 and pci B leaf. 576203831d35Sstevel */ 576303831d35Sstevel if (((parent_dip = ddi_get_parent(dip)) == NULL) || 576403831d35Sstevel ((name = ddi_binding_name(parent_dip)) == NULL)) 576503831d35Sstevel return (0); 576603831d35Sstevel if (strcmp(name, SCHIZO_BINDING_NAME) != 0) { 576703831d35Sstevel /* 576803831d35Sstevel * This RIO could be on XMITS, so get the dip to 576903831d35Sstevel * XMITS PCI Leaf. 577003831d35Sstevel */ 577103831d35Sstevel if ((parent_dip = ddi_get_parent(parent_dip)) == NULL) 577203831d35Sstevel return (0); 577303831d35Sstevel if (((name = ddi_binding_name(parent_dip)) == NULL) || 577403831d35Sstevel (strcmp(name, XMITS_BINDING_NAME) != 0)) { 577503831d35Sstevel return (0); 577603831d35Sstevel } 577703831d35Sstevel } 577803831d35Sstevel if (!drmach_dip_is_schizo_xmits_0_pci_b(parent_dip)) 577903831d35Sstevel return (0); 578003831d35Sstevel /* 578103831d35Sstevel * Finally make sure it is the MAN eri. 578203831d35Sstevel */ 578303831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 578403831d35Sstevel "reg", (caddr_t)®buf, &len) == DDI_PROP_SUCCESS) { 578503831d35Sstevel 578603831d35Sstevel pci_device = PCI_REG_DEV_G(regbuf->pci_phys_hi); 578703831d35Sstevel pci_function = PCI_REG_FUNC_G(regbuf->pci_phys_hi); 578803831d35Sstevel kmem_free(regbuf, len); 578903831d35Sstevel 579003831d35Sstevel /* 579103831d35Sstevel * The network function of the RIO ASIC will always be 579203831d35Sstevel * device 3 and function 1 ("network@3,1"). 579303831d35Sstevel */ 579403831d35Sstevel if ((pci_device == 3) && (pci_function == 1)) 579503831d35Sstevel return (1); 579603831d35Sstevel } 579703831d35Sstevel return (0); 579803831d35Sstevel } 579903831d35Sstevel 580003831d35Sstevel typedef struct { 580103831d35Sstevel int iosram_inst; 580203831d35Sstevel dev_info_t *eri_dip; 580303831d35Sstevel int bnum; 580403831d35Sstevel } drmach_io_inst_t; 580503831d35Sstevel 580603831d35Sstevel int 580703831d35Sstevel drmach_board_find_io_insts(dev_info_t *dip, void *args) 580803831d35Sstevel { 580903831d35Sstevel drmach_io_inst_t *ios = (drmach_io_inst_t *)args; 581003831d35Sstevel 581103831d35Sstevel int rv; 581203831d35Sstevel int len; 581303831d35Sstevel int portid; 581403831d35Sstevel char name[OBP_MAXDRVNAME]; 581503831d35Sstevel 581603831d35Sstevel rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid", &len); 581703831d35Sstevel 581803831d35Sstevel if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (portid))) { 581903831d35Sstevel return (DDI_WALK_CONTINUE); 582003831d35Sstevel } 582103831d35Sstevel 582203831d35Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, 582303831d35Sstevel "portid", (caddr_t)&portid, &len); 582403831d35Sstevel if (rv != DDI_PROP_SUCCESS) 582503831d35Sstevel return (DDI_WALK_CONTINUE); 582603831d35Sstevel 582703831d35Sstevel /* ignore devices that are not on this board */ 582803831d35Sstevel if (drmach_portid2bnum(portid) != ios->bnum) 582903831d35Sstevel return (DDI_WALK_CONTINUE); 583003831d35Sstevel 583103831d35Sstevel if ((ios->iosram_inst < 0) || (ios->eri_dip == NULL)) { 5832d3d50737SRafael Vanoni rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "name", &len); 583303831d35Sstevel if (rv == DDI_PROP_SUCCESS) { 583403831d35Sstevel 583503831d35Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 583603831d35Sstevel 0, "name", 583703831d35Sstevel (caddr_t)name, &len); 583803831d35Sstevel if (rv != DDI_PROP_SUCCESS) 583903831d35Sstevel return (DDI_WALK_CONTINUE); 584003831d35Sstevel 584103831d35Sstevel if (strncmp("iosram", name, 6) == 0) { 584203831d35Sstevel ios->iosram_inst = ddi_get_instance(dip); 584303831d35Sstevel if (ios->eri_dip == NULL) 584403831d35Sstevel return (DDI_WALK_CONTINUE); 584503831d35Sstevel else 584603831d35Sstevel return (DDI_WALK_TERMINATE); 584703831d35Sstevel } else { 584803831d35Sstevel if (drmach_dip_is_man_eri(dip)) { 584903831d35Sstevel ASSERT(ios->eri_dip == NULL); 585003831d35Sstevel ndi_hold_devi(dip); 585103831d35Sstevel ios->eri_dip = dip; 585203831d35Sstevel if (ios->iosram_inst < 0) 585303831d35Sstevel return (DDI_WALK_CONTINUE); 585403831d35Sstevel else 585503831d35Sstevel return (DDI_WALK_TERMINATE); 585603831d35Sstevel } 585703831d35Sstevel } 585803831d35Sstevel } 585903831d35Sstevel } 586003831d35Sstevel return (DDI_WALK_CONTINUE); 586103831d35Sstevel } 586203831d35Sstevel 586303831d35Sstevel sbd_error_t * 586403831d35Sstevel drmach_io_pre_release(drmachid_t id) 586503831d35Sstevel { 586603831d35Sstevel drmach_io_inst_t ios; 586703831d35Sstevel drmach_board_t *bp; 586803831d35Sstevel int rv = 0; 586903831d35Sstevel sbd_error_t *err = NULL; 587003831d35Sstevel drmach_device_t *dp; 587103831d35Sstevel dev_info_t *rdip; 587203831d35Sstevel int circ; 587303831d35Sstevel 587403831d35Sstevel if (!DRMACH_IS_IO_ID(id)) 587503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 587603831d35Sstevel dp = id; 587703831d35Sstevel bp = dp->bp; 587803831d35Sstevel 587903831d35Sstevel rdip = dp->node->n_getdip(dp->node); 588003831d35Sstevel 588103831d35Sstevel /* walk device tree to find iosram instance for the board */ 588203831d35Sstevel ios.iosram_inst = -1; 588303831d35Sstevel ios.eri_dip = NULL; 588403831d35Sstevel ios.bnum = bp->bnum; 588503831d35Sstevel 588603831d35Sstevel ndi_devi_enter(rdip, &circ); 588703831d35Sstevel ddi_walk_devs(ddi_get_child(rdip), drmach_board_find_io_insts, 588803831d35Sstevel (void *)&ios); 588903831d35Sstevel 589003831d35Sstevel DRMACH_PR("drmach_io_pre_release: bnum=%d iosram=%d eri=0x%p\n", 589107d06da5SSurya Prakki ios.bnum, ios.iosram_inst, (void *)ios.eri_dip); 589203831d35Sstevel ndi_devi_exit(rdip, circ); 589303831d35Sstevel 589403831d35Sstevel if (ios.eri_dip) { 589503831d35Sstevel /* 589603831d35Sstevel * Release hold acquired in drmach_board_find_io_insts() 589703831d35Sstevel */ 589803831d35Sstevel ndi_rele_devi(ios.eri_dip); 589903831d35Sstevel } 590003831d35Sstevel if (ios.iosram_inst >= 0) { 590103831d35Sstevel /* call for tunnel switch */ 590203831d35Sstevel do { 590303831d35Sstevel DRMACH_PR("calling iosram_switchfrom(%d)\n", 590403831d35Sstevel ios.iosram_inst); 590503831d35Sstevel rv = iosram_switchfrom(ios.iosram_inst); 590603831d35Sstevel if (rv) 590703831d35Sstevel DRMACH_PR("iosram_switchfrom returned %d\n", 590803831d35Sstevel rv); 590903831d35Sstevel } while (rv == EAGAIN); 591003831d35Sstevel 591103831d35Sstevel if (rv) 591203831d35Sstevel err = drerr_new(0, ESTC_IOSWITCH, NULL); 591303831d35Sstevel } 591403831d35Sstevel return (err); 591503831d35Sstevel } 591603831d35Sstevel 591703831d35Sstevel sbd_error_t * 591803831d35Sstevel drmach_io_unrelease(drmachid_t id) 591903831d35Sstevel { 592003831d35Sstevel dev_info_t *dip; 592103831d35Sstevel sbd_error_t *err = NULL; 592203831d35Sstevel drmach_device_t *dp; 592303831d35Sstevel 592403831d35Sstevel if (!DRMACH_IS_IO_ID(id)) 592503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 592603831d35Sstevel dp = id; 592703831d35Sstevel 592803831d35Sstevel dip = dp->node->n_getdip(dp->node); 592903831d35Sstevel 593003831d35Sstevel if (dip == NULL) 593103831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 593203831d35Sstevel else { 593303831d35Sstevel int (*func)(dev_info_t *dip); 593403831d35Sstevel 593503831d35Sstevel func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach", 593603831d35Sstevel 0); 593703831d35Sstevel 593803831d35Sstevel if (func) { 593903831d35Sstevel drmach_io_inst_t ios; 594003831d35Sstevel dev_info_t *pdip; 594103831d35Sstevel int circ; 594203831d35Sstevel 594303831d35Sstevel /* 594403831d35Sstevel * Walk device tree to find rio dip for the board 594503831d35Sstevel * Since we are not interested in iosram instance here, 594603831d35Sstevel * initialize it to 0, so that the walk terminates as 594703831d35Sstevel * soon as eri dip is found. 594803831d35Sstevel */ 594903831d35Sstevel ios.iosram_inst = 0; 595003831d35Sstevel ios.eri_dip = NULL; 595103831d35Sstevel ios.bnum = dp->bp->bnum; 595203831d35Sstevel 595303831d35Sstevel if (pdip = ddi_get_parent(dip)) { 595403831d35Sstevel ndi_hold_devi(pdip); 595503831d35Sstevel ndi_devi_enter(pdip, &circ); 595603831d35Sstevel } 595703831d35Sstevel /* 595803831d35Sstevel * Root node doesn't have to be held in any way. 595903831d35Sstevel */ 5960d3d50737SRafael Vanoni ddi_walk_devs(dip, drmach_board_find_io_insts, 5961d3d50737SRafael Vanoni (void *)&ios); 596203831d35Sstevel 596303831d35Sstevel if (pdip) { 596403831d35Sstevel ndi_devi_exit(pdip, circ); 596503831d35Sstevel ndi_rele_devi(pdip); 596603831d35Sstevel } 596703831d35Sstevel 596803831d35Sstevel DRMACH_PR("drmach_io_unrelease: bnum=%d eri=0x%p\n", 596907d06da5SSurya Prakki ios.bnum, (void *)ios.eri_dip); 597003831d35Sstevel 597103831d35Sstevel if (ios.eri_dip) { 597203831d35Sstevel DRMACH_PR("calling man_dr_attach\n"); 597303831d35Sstevel if ((*func)(ios.eri_dip)) 5974d3d50737SRafael Vanoni err = drerr_new(0, ESTC_NWSWITCH, NULL); 597503831d35Sstevel /* 597603831d35Sstevel * Release hold acquired in 597703831d35Sstevel * drmach_board_find_io_insts() 597803831d35Sstevel */ 597903831d35Sstevel ndi_rele_devi(ios.eri_dip); 598003831d35Sstevel } 598103831d35Sstevel } else 598203831d35Sstevel DRMACH_PR("man_dr_attach NOT present\n"); 598303831d35Sstevel } 598403831d35Sstevel return (err); 598503831d35Sstevel } 598603831d35Sstevel 598703831d35Sstevel static sbd_error_t * 598803831d35Sstevel drmach_io_release(drmachid_t id) 598903831d35Sstevel { 599003831d35Sstevel dev_info_t *dip; 599103831d35Sstevel sbd_error_t *err = NULL; 599203831d35Sstevel drmach_device_t *dp; 599303831d35Sstevel 599403831d35Sstevel if (!DRMACH_IS_IO_ID(id)) 599503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 599603831d35Sstevel dp = id; 599703831d35Sstevel 599803831d35Sstevel dip = dp->node->n_getdip(dp->node); 599903831d35Sstevel 600003831d35Sstevel if (dip == NULL) 600103831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 600203831d35Sstevel else { 600303831d35Sstevel int (*func)(dev_info_t *dip); 600403831d35Sstevel 600503831d35Sstevel func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_detach", 600603831d35Sstevel 0); 600703831d35Sstevel 600803831d35Sstevel if (func) { 600903831d35Sstevel drmach_io_inst_t ios; 601003831d35Sstevel dev_info_t *pdip; 601103831d35Sstevel int circ; 601203831d35Sstevel 601303831d35Sstevel /* 601403831d35Sstevel * Walk device tree to find rio dip for the board 601503831d35Sstevel * Since we are not interested in iosram instance here, 601603831d35Sstevel * initialize it to 0, so that the walk terminates as 601703831d35Sstevel * soon as eri dip is found. 601803831d35Sstevel */ 601903831d35Sstevel ios.iosram_inst = 0; 602003831d35Sstevel ios.eri_dip = NULL; 602103831d35Sstevel ios.bnum = dp->bp->bnum; 602203831d35Sstevel 602303831d35Sstevel if (pdip = ddi_get_parent(dip)) { 602403831d35Sstevel ndi_hold_devi(pdip); 602503831d35Sstevel ndi_devi_enter(pdip, &circ); 602603831d35Sstevel } 602703831d35Sstevel /* 602803831d35Sstevel * Root node doesn't have to be held in any way. 602903831d35Sstevel */ 6030d3d50737SRafael Vanoni ddi_walk_devs(dip, drmach_board_find_io_insts, 6031d3d50737SRafael Vanoni (void *)&ios); 603203831d35Sstevel 603303831d35Sstevel if (pdip) { 603403831d35Sstevel ndi_devi_exit(pdip, circ); 603503831d35Sstevel ndi_rele_devi(pdip); 603603831d35Sstevel } 603703831d35Sstevel 603803831d35Sstevel DRMACH_PR("drmach_io_release: bnum=%d eri=0x%p\n", 603907d06da5SSurya Prakki ios.bnum, (void *)ios.eri_dip); 604003831d35Sstevel 604103831d35Sstevel if (ios.eri_dip) { 604203831d35Sstevel DRMACH_PR("calling man_dr_detach\n"); 604303831d35Sstevel if ((*func)(ios.eri_dip)) 6044d3d50737SRafael Vanoni err = drerr_new(0, ESTC_NWSWITCH, NULL); 604503831d35Sstevel /* 604603831d35Sstevel * Release hold acquired in 604703831d35Sstevel * drmach_board_find_io_insts() 604803831d35Sstevel */ 604903831d35Sstevel ndi_rele_devi(ios.eri_dip); 605003831d35Sstevel } 605103831d35Sstevel } else 605203831d35Sstevel DRMACH_PR("man_dr_detach NOT present\n"); 605303831d35Sstevel } 605403831d35Sstevel return (err); 605503831d35Sstevel } 605603831d35Sstevel 605703831d35Sstevel sbd_error_t * 605803831d35Sstevel drmach_io_post_release(drmachid_t id) 605903831d35Sstevel { 606003831d35Sstevel char *path; 606103831d35Sstevel dev_info_t *rdip; 606203831d35Sstevel drmach_device_t *dp; 606303831d35Sstevel 606403831d35Sstevel if (!DRMACH_IS_DEVICE_ID(id)) 606503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 606603831d35Sstevel dp = id; 606703831d35Sstevel 606803831d35Sstevel rdip = dp->node->n_getdip(dp->node); 606903831d35Sstevel 607003831d35Sstevel /* 607103831d35Sstevel * Always called after drmach_unconfigure() which on Starcat 607203831d35Sstevel * unconfigures the branch but doesn't remove it so the 607303831d35Sstevel * dip must always exist. 607403831d35Sstevel */ 607503831d35Sstevel ASSERT(rdip); 607603831d35Sstevel 607703831d35Sstevel ASSERT(e_ddi_branch_held(rdip)); 607803831d35Sstevel #ifdef DEBUG 607903831d35Sstevel path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 608003831d35Sstevel (void) ddi_pathname(rdip, path); 608103831d35Sstevel DRMACH_PR("post_release dip path is: %s\n", path); 608203831d35Sstevel kmem_free(path, MAXPATHLEN); 608303831d35Sstevel #endif 608403831d35Sstevel 608503831d35Sstevel if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) { 608603831d35Sstevel if (schpc_remove_pci(rdip)) { 608703831d35Sstevel DRMACH_PR("schpc_remove_pci failed\n"); 608803831d35Sstevel return (drerr_new(0, ESBD_OFFLINE, NULL)); 608903831d35Sstevel } else { 609003831d35Sstevel DRMACH_PR("schpc_remove_pci succeeded\n"); 609103831d35Sstevel } 609203831d35Sstevel } 609303831d35Sstevel 609403831d35Sstevel return (NULL); 609503831d35Sstevel } 609603831d35Sstevel 609703831d35Sstevel sbd_error_t * 609803831d35Sstevel drmach_io_post_attach(drmachid_t id) 609903831d35Sstevel { 610003831d35Sstevel int circ; 610103831d35Sstevel dev_info_t *dip; 610203831d35Sstevel dev_info_t *pdip; 610303831d35Sstevel drmach_device_t *dp; 610403831d35Sstevel drmach_io_inst_t ios; 610503831d35Sstevel 610603831d35Sstevel if (!DRMACH_IS_DEVICE_ID(id)) 610703831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 610803831d35Sstevel dp = id; 610903831d35Sstevel 611003831d35Sstevel dip = dp->node->n_getdip(dp->node); 611103831d35Sstevel 611203831d35Sstevel /* 611303831d35Sstevel * We held the branch rooted at dip earlier, so at a minimum the 611403831d35Sstevel * root i.e. dip must be present in the device tree. 611503831d35Sstevel */ 611603831d35Sstevel ASSERT(dip); 611703831d35Sstevel 611803831d35Sstevel if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) { 611903831d35Sstevel if (schpc_add_pci(dip)) { 612003831d35Sstevel DRMACH_PR("schpc_add_pci failed\n"); 612103831d35Sstevel } else { 612203831d35Sstevel DRMACH_PR("schpc_add_pci succeeded\n"); 612303831d35Sstevel } 612403831d35Sstevel } 612503831d35Sstevel 612603831d35Sstevel /* 612703831d35Sstevel * Walk device tree to find rio dip for the board 612803831d35Sstevel * Since we are not interested in iosram instance here, 612903831d35Sstevel * initialize it to 0, so that the walk terminates as 613003831d35Sstevel * soon as eri dip is found. 613103831d35Sstevel */ 613203831d35Sstevel ios.iosram_inst = 0; 613303831d35Sstevel ios.eri_dip = NULL; 613403831d35Sstevel ios.bnum = dp->bp->bnum; 613503831d35Sstevel 613603831d35Sstevel if (pdip = ddi_get_parent(dip)) { 613703831d35Sstevel ndi_hold_devi(pdip); 613803831d35Sstevel ndi_devi_enter(pdip, &circ); 613903831d35Sstevel } 614003831d35Sstevel /* 614103831d35Sstevel * Root node doesn't have to be held in any way. 614203831d35Sstevel */ 6143d3d50737SRafael Vanoni ddi_walk_devs(dip, drmach_board_find_io_insts, (void *)&ios); 614403831d35Sstevel if (pdip) { 614503831d35Sstevel ndi_devi_exit(pdip, circ); 614603831d35Sstevel ndi_rele_devi(pdip); 614703831d35Sstevel } 614803831d35Sstevel 614907d06da5SSurya Prakki DRMACH_PR("drmach_io_post_attach: bnum=%d eri=0x%p\n", 615007d06da5SSurya Prakki ios.bnum, (void *)ios.eri_dip); 615103831d35Sstevel 615203831d35Sstevel if (ios.eri_dip) { 615303831d35Sstevel int (*func)(dev_info_t *dip); 615403831d35Sstevel 615503831d35Sstevel func = 615603831d35Sstevel (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach", 0); 615703831d35Sstevel 615803831d35Sstevel if (func) { 615903831d35Sstevel DRMACH_PR("calling man_dr_attach\n"); 616003831d35Sstevel (void) (*func)(ios.eri_dip); 616103831d35Sstevel } else { 616203831d35Sstevel DRMACH_PR("man_dr_attach NOT present\n"); 616303831d35Sstevel } 616403831d35Sstevel 616503831d35Sstevel /* 616603831d35Sstevel * Release hold acquired in drmach_board_find_io_insts() 616703831d35Sstevel */ 616803831d35Sstevel ndi_rele_devi(ios.eri_dip); 616903831d35Sstevel 617003831d35Sstevel } 617103831d35Sstevel 617203831d35Sstevel return (NULL); 617303831d35Sstevel } 617403831d35Sstevel 617503831d35Sstevel static sbd_error_t * 617603831d35Sstevel drmach_io_status(drmachid_t id, drmach_status_t *stat) 617703831d35Sstevel { 617803831d35Sstevel drmach_device_t *dp; 617903831d35Sstevel sbd_error_t *err; 618003831d35Sstevel int configured; 618103831d35Sstevel 618203831d35Sstevel ASSERT(DRMACH_IS_IO_ID(id)); 618303831d35Sstevel dp = id; 618403831d35Sstevel 618503831d35Sstevel err = drmach_io_is_attached(id, &configured); 618603831d35Sstevel if (err) 618703831d35Sstevel return (err); 618803831d35Sstevel 618903831d35Sstevel stat->assigned = dp->bp->assigned; 619003831d35Sstevel stat->powered = dp->bp->powered; 619103831d35Sstevel stat->configured = (configured != 0); 619203831d35Sstevel stat->busy = dp->busy; 619307d06da5SSurya Prakki (void) strncpy(stat->type, dp->type, sizeof (stat->type)); 619403831d35Sstevel stat->info[0] = '\0'; 619503831d35Sstevel 619603831d35Sstevel return (NULL); 619703831d35Sstevel } 619803831d35Sstevel 619903831d35Sstevel sbd_error_t * 620003831d35Sstevel drmach_mem_init_size(drmachid_t id) 620103831d35Sstevel { 620203831d35Sstevel drmach_mem_t *mp; 620303831d35Sstevel sbd_error_t *err; 620403831d35Sstevel gdcd_t *gdcd; 620503831d35Sstevel mem_chunk_t *chunk; 620603831d35Sstevel uint64_t chunks, pa, mask, sz; 620703831d35Sstevel 620803831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 620903831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 621003831d35Sstevel mp = id; 621103831d35Sstevel 621203831d35Sstevel err = drmach_mem_get_base_physaddr(id, &pa); 621303831d35Sstevel if (err) 621403831d35Sstevel return (err); 621503831d35Sstevel 621603831d35Sstevel mask = ~ (DRMACH_MEM_SLICE_SIZE - 1); 621703831d35Sstevel pa &= mask; 621803831d35Sstevel 621903831d35Sstevel gdcd = drmach_gdcd_new(); 622003831d35Sstevel if (gdcd == NULL) 622103831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 622203831d35Sstevel 622303831d35Sstevel sz = 0; 622403831d35Sstevel chunk = gdcd->dcd_chunk_list.dcl_chunk; 622503831d35Sstevel chunks = gdcd->dcd_chunk_list.dcl_chunks; 622603831d35Sstevel while (chunks-- != 0) { 622703831d35Sstevel if ((chunk->mc_base_pa & mask) == pa) { 622803831d35Sstevel sz += chunk->mc_mbytes * 1048576; 622903831d35Sstevel } 623003831d35Sstevel 623103831d35Sstevel ++chunk; 623203831d35Sstevel } 623303831d35Sstevel mp->nbytes = sz; 623403831d35Sstevel 623503831d35Sstevel drmach_gdcd_dispose(gdcd); 623603831d35Sstevel return (NULL); 623703831d35Sstevel } 623803831d35Sstevel 623903831d35Sstevel /* 624003831d35Sstevel * Hardware registers are organized into consecutively 624103831d35Sstevel * addressed registers. The reg property's hi and lo fields 624203831d35Sstevel * together describe the base address of the register set for 624303831d35Sstevel * this memory-controller. Register descriptions and offsets 624403831d35Sstevel * (from the base address) are as follows: 624503831d35Sstevel * 624603831d35Sstevel * Description Offset Size (bytes) 624703831d35Sstevel * Memory Timing Control Register I 0x00 8 624803831d35Sstevel * Memory Timing Control Register II 0x08 8 624903831d35Sstevel * Memory Address Decoding Register I 0x10 8 625003831d35Sstevel * Memory Address Decoding Register II 0x18 8 625103831d35Sstevel * Memory Address Decoding Register III 0x20 8 625203831d35Sstevel * Memory Address Decoding Register IV 0x28 8 625303831d35Sstevel * Memory Address Control Register 0x30 8 625403831d35Sstevel * Memory Timing Control Register III 0x38 8 625503831d35Sstevel * Memory Timing Control Register IV 0x40 8 625603831d35Sstevel * Memory Timing Control Register V 0x48 8 (Jaguar, Panther only) 625703831d35Sstevel * EMU Activity Status Register 0x50 8 (Panther only) 625803831d35Sstevel * 625903831d35Sstevel * Only the Memory Address Decoding Register and EMU Activity Status 626003831d35Sstevel * Register addresses are needed for DRMACH. 626103831d35Sstevel */ 626203831d35Sstevel static sbd_error_t * 626303831d35Sstevel drmach_mem_new(drmach_device_t *proto, drmachid_t *idp) 626403831d35Sstevel { 626503831d35Sstevel sbd_error_t *err; 626603831d35Sstevel uint64_t madr_pa; 626703831d35Sstevel drmach_mem_t *mp; 626803831d35Sstevel int bank, count; 626903831d35Sstevel 627003831d35Sstevel err = drmach_read_reg_addr(proto, &madr_pa); 627103831d35Sstevel if (err) 627203831d35Sstevel return (err); 627303831d35Sstevel 627403831d35Sstevel mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP); 627503831d35Sstevel bcopy(proto, &mp->dev, sizeof (mp->dev)); 627603831d35Sstevel mp->dev.node = drmach_node_dup(proto->node); 627703831d35Sstevel mp->dev.cm.isa = (void *)drmach_mem_new; 627803831d35Sstevel mp->dev.cm.dispose = drmach_mem_dispose; 627903831d35Sstevel mp->dev.cm.release = drmach_mem_release; 628003831d35Sstevel mp->dev.cm.status = drmach_mem_status; 628103831d35Sstevel mp->madr_pa = madr_pa; 628203831d35Sstevel 628307d06da5SSurya Prakki (void) snprintf(mp->dev.cm.name, 628407d06da5SSurya Prakki sizeof (mp->dev.cm.name), "%s", mp->dev.type); 628503831d35Sstevel 628603831d35Sstevel for (count = bank = 0; bank < DRMACH_MC_NBANKS; bank++) { 628703831d35Sstevel uint64_t madr; 628803831d35Sstevel 628903831d35Sstevel drmach_mem_read_madr(mp, bank, &madr); 629003831d35Sstevel if (madr & DRMACH_MC_VALID_MASK) { 629103831d35Sstevel count += 1; 629203831d35Sstevel break; 629303831d35Sstevel } 629403831d35Sstevel } 629503831d35Sstevel 629603831d35Sstevel /* 629703831d35Sstevel * If none of the banks had their valid bit set, that means 629803831d35Sstevel * post did not configure this MC to participate in the 629903831d35Sstevel * domain. So, pretend this node does not exist by returning 630003831d35Sstevel * a drmachid of zero. 630103831d35Sstevel */ 630203831d35Sstevel if (count == 0) { 630303831d35Sstevel /* drmach_mem_dispose frees board mem list */ 630403831d35Sstevel drmach_node_dispose(mp->dev.node); 630503831d35Sstevel kmem_free(mp, sizeof (*mp)); 630603831d35Sstevel *idp = (drmachid_t)0; 630703831d35Sstevel return (NULL); 630803831d35Sstevel } 630903831d35Sstevel 631003831d35Sstevel /* 631103831d35Sstevel * Only one mem unit per board is exposed to the 631203831d35Sstevel * PIM layer. The first mem unit encountered during 631303831d35Sstevel * tree walk is used to represent all mem units on 631403831d35Sstevel * the same board. 631503831d35Sstevel */ 631603831d35Sstevel if (mp->dev.bp->mem == NULL) { 631703831d35Sstevel /* start list of mem units on this board */ 631803831d35Sstevel mp->dev.bp->mem = mp; 631903831d35Sstevel 632003831d35Sstevel /* 632103831d35Sstevel * force unum to zero since this is the only mem unit 632203831d35Sstevel * that will be visible to the PIM layer. 632303831d35Sstevel */ 632403831d35Sstevel mp->dev.unum = 0; 632503831d35Sstevel 632603831d35Sstevel /* 632703831d35Sstevel * board memory size kept in this mem unit only 632803831d35Sstevel */ 632903831d35Sstevel err = drmach_mem_init_size(mp); 633003831d35Sstevel if (err) { 633103831d35Sstevel mp->dev.bp->mem = NULL; 633203831d35Sstevel /* drmach_mem_dispose frees board mem list */ 633303831d35Sstevel drmach_node_dispose(mp->dev.node); 633403831d35Sstevel kmem_free(mp, sizeof (*mp)); 633503831d35Sstevel *idp = (drmachid_t)0; 633603831d35Sstevel return (NULL); 633703831d35Sstevel } 633803831d35Sstevel 633903831d35Sstevel /* 634003831d35Sstevel * allow this instance (the first encountered on this board) 634103831d35Sstevel * to be visible to the PIM layer. 634203831d35Sstevel */ 634303831d35Sstevel *idp = (drmachid_t)mp; 634403831d35Sstevel } else { 634503831d35Sstevel drmach_mem_t *lp; 634603831d35Sstevel 634703831d35Sstevel /* hide this mem instance behind the first. */ 634803831d35Sstevel for (lp = mp->dev.bp->mem; lp->next; lp = lp->next) 634903831d35Sstevel ; 635003831d35Sstevel lp->next = mp; 635103831d35Sstevel 635203831d35Sstevel /* 635303831d35Sstevel * hide this instance from the caller. 635403831d35Sstevel * See drmach_board_find_devices_cb() for details. 635503831d35Sstevel */ 635603831d35Sstevel *idp = (drmachid_t)0; 635703831d35Sstevel } 635803831d35Sstevel 635903831d35Sstevel return (NULL); 636003831d35Sstevel } 636103831d35Sstevel 636203831d35Sstevel static void 636303831d35Sstevel drmach_mem_dispose(drmachid_t id) 636403831d35Sstevel { 636503831d35Sstevel drmach_mem_t *mp, *next; 636603831d35Sstevel drmach_board_t *bp; 636703831d35Sstevel 636803831d35Sstevel ASSERT(DRMACH_IS_MEM_ID(id)); 636903831d35Sstevel 637003831d35Sstevel mutex_enter(&drmach_bus_sync_lock); 637103831d35Sstevel 637203831d35Sstevel mp = id; 637303831d35Sstevel bp = mp->dev.bp; 637403831d35Sstevel 637503831d35Sstevel do { 637603831d35Sstevel if (mp->dev.node) 637703831d35Sstevel drmach_node_dispose(mp->dev.node); 637803831d35Sstevel 637903831d35Sstevel next = mp->next; 638003831d35Sstevel kmem_free(mp, sizeof (*mp)); 638103831d35Sstevel mp = next; 638203831d35Sstevel } while (mp); 638303831d35Sstevel 638403831d35Sstevel bp->mem = NULL; 638503831d35Sstevel 638603831d35Sstevel drmach_bus_sync_list_update(); 638703831d35Sstevel mutex_exit(&drmach_bus_sync_lock); 638803831d35Sstevel } 638903831d35Sstevel 639003831d35Sstevel sbd_error_t * 639103831d35Sstevel drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size) 639203831d35Sstevel { 639303831d35Sstevel pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 639403831d35Sstevel pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 639503831d35Sstevel int rv; 639603831d35Sstevel 639703831d35Sstevel ASSERT(size != 0); 639803831d35Sstevel 639903831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 640003831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 640103831d35Sstevel 640285f58038Sdp78419 rv = kcage_range_add(basepfn, npages, KCAGE_DOWN); 640303831d35Sstevel if (rv == ENOMEM) { 640403831d35Sstevel cmn_err(CE_WARN, "%lu megabytes not available" 640503831d35Sstevel " to kernel cage", size >> 20); 640603831d35Sstevel } else if (rv != 0) { 640703831d35Sstevel /* catch this in debug kernels */ 640803831d35Sstevel ASSERT(0); 640903831d35Sstevel 641003831d35Sstevel cmn_err(CE_WARN, "unexpected kcage_range_add" 641103831d35Sstevel " return value %d", rv); 641203831d35Sstevel } 641303831d35Sstevel 641403831d35Sstevel return (NULL); 641503831d35Sstevel } 641603831d35Sstevel 641703831d35Sstevel sbd_error_t * 641803831d35Sstevel drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size) 641903831d35Sstevel { 642003831d35Sstevel pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT); 642103831d35Sstevel pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT); 642203831d35Sstevel int rv; 642303831d35Sstevel 642403831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 642503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 642603831d35Sstevel 642703831d35Sstevel if (size > 0) { 642803831d35Sstevel rv = kcage_range_delete_post_mem_del(basepfn, npages); 642903831d35Sstevel if (rv != 0) { 643003831d35Sstevel cmn_err(CE_WARN, 643103831d35Sstevel "unexpected kcage_range_delete_post_mem_del" 643203831d35Sstevel " return value %d", rv); 643303831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 643403831d35Sstevel } 643503831d35Sstevel } 643603831d35Sstevel 643703831d35Sstevel return (NULL); 643803831d35Sstevel } 643903831d35Sstevel 644003831d35Sstevel sbd_error_t * 644103831d35Sstevel drmach_mem_disable(drmachid_t id) 644203831d35Sstevel { 644303831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 644403831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 644503831d35Sstevel else 644603831d35Sstevel return (NULL); 644703831d35Sstevel } 644803831d35Sstevel 644903831d35Sstevel sbd_error_t * 645003831d35Sstevel drmach_mem_enable(drmachid_t id) 645103831d35Sstevel { 645203831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 645303831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 645403831d35Sstevel else 645503831d35Sstevel return (NULL); 645603831d35Sstevel } 645703831d35Sstevel 645803831d35Sstevel sbd_error_t * 645903831d35Sstevel drmach_mem_get_alignment(drmachid_t id, uint64_t *mask) 646003831d35Sstevel { 646103831d35Sstevel #define MB(mb) ((mb) * 1048576ull) 646203831d35Sstevel 646303831d35Sstevel static struct { 646403831d35Sstevel uint_t uk; 646503831d35Sstevel uint64_t segsz; 646603831d35Sstevel } uk2segsz[] = { 646703831d35Sstevel { 0x003, MB(256) }, 646803831d35Sstevel { 0x007, MB(512) }, 646903831d35Sstevel { 0x00f, MB(1024) }, 647003831d35Sstevel { 0x01f, MB(2048) }, 647103831d35Sstevel { 0x03f, MB(4096) }, 647203831d35Sstevel { 0x07f, MB(8192) }, 647303831d35Sstevel { 0x0ff, MB(16384) }, 647403831d35Sstevel { 0x1ff, MB(32768) }, 647503831d35Sstevel { 0x3ff, MB(65536) }, 647603831d35Sstevel { 0x7ff, MB(131072) } 647703831d35Sstevel }; 647803831d35Sstevel static int len = sizeof (uk2segsz) / sizeof (uk2segsz[0]); 647903831d35Sstevel 648003831d35Sstevel #undef MB 648103831d35Sstevel 648203831d35Sstevel uint64_t largest_sz = 0; 648303831d35Sstevel drmach_mem_t *mp; 648403831d35Sstevel 648503831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 648603831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 648703831d35Sstevel 648803831d35Sstevel /* prime the result with a default value */ 648903831d35Sstevel *mask = (DRMACH_MEM_SLICE_SIZE - 1); 649003831d35Sstevel 649103831d35Sstevel for (mp = id; mp; mp = mp->next) { 649203831d35Sstevel int bank; 649303831d35Sstevel 649403831d35Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) { 649503831d35Sstevel int i; 649603831d35Sstevel uint_t uk; 649703831d35Sstevel uint64_t madr; 649803831d35Sstevel 649903831d35Sstevel /* get register value, extract uk and normalize */ 650003831d35Sstevel drmach_mem_read_madr(mp, bank, &madr); 650103831d35Sstevel 650203831d35Sstevel if (!(madr & DRMACH_MC_VALID_MASK)) 650303831d35Sstevel continue; 650403831d35Sstevel 650503831d35Sstevel uk = DRMACH_MC_UK(madr); 650603831d35Sstevel 650703831d35Sstevel /* match uk value */ 650803831d35Sstevel for (i = 0; i < len; i++) 650903831d35Sstevel if (uk == uk2segsz[i].uk) 651003831d35Sstevel break; 651103831d35Sstevel 651203831d35Sstevel if (i < len) { 651303831d35Sstevel uint64_t sz = uk2segsz[i].segsz; 651403831d35Sstevel 651503831d35Sstevel /* 651603831d35Sstevel * remember largest segment size, 651703831d35Sstevel * update mask result 651803831d35Sstevel */ 651903831d35Sstevel if (sz > largest_sz) { 652003831d35Sstevel largest_sz = sz; 652103831d35Sstevel *mask = sz - 1; 652203831d35Sstevel } 652303831d35Sstevel } else { 652403831d35Sstevel /* 652503831d35Sstevel * uk not in table, punt using 652603831d35Sstevel * entire slice size. no longer any 652703831d35Sstevel * reason to check other banks. 652803831d35Sstevel */ 652903831d35Sstevel *mask = (DRMACH_MEM_SLICE_SIZE - 1); 653003831d35Sstevel return (NULL); 653103831d35Sstevel } 653203831d35Sstevel } 653303831d35Sstevel } 653403831d35Sstevel 653503831d35Sstevel return (NULL); 653603831d35Sstevel } 653703831d35Sstevel 653803831d35Sstevel sbd_error_t * 653903831d35Sstevel drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *base_addr) 654003831d35Sstevel { 654103831d35Sstevel drmach_mem_t *mp; 654203831d35Sstevel 654303831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 654403831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 654503831d35Sstevel 654603831d35Sstevel *base_addr = (uint64_t)-1; 654703831d35Sstevel for (mp = id; mp; mp = mp->next) { 654803831d35Sstevel int bank; 654903831d35Sstevel 655003831d35Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) { 655103831d35Sstevel uint64_t addr, madr; 655203831d35Sstevel 655303831d35Sstevel drmach_mem_read_madr(mp, bank, &madr); 655403831d35Sstevel if (madr & DRMACH_MC_VALID_MASK) { 655503831d35Sstevel addr = DRMACH_MC_UM_TO_PA(madr) | 655603831d35Sstevel DRMACH_MC_LM_TO_PA(madr); 655703831d35Sstevel 655803831d35Sstevel if (addr < *base_addr) 655903831d35Sstevel *base_addr = addr; 656003831d35Sstevel } 656103831d35Sstevel } 656203831d35Sstevel } 656303831d35Sstevel 656403831d35Sstevel /* should not happen, but ... */ 656503831d35Sstevel if (*base_addr == (uint64_t)-1) 656603831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 656703831d35Sstevel 656803831d35Sstevel return (NULL); 656903831d35Sstevel } 657003831d35Sstevel 657103831d35Sstevel void 657203831d35Sstevel drmach_bus_sync_list_update(void) 657303831d35Sstevel { 657403831d35Sstevel int rv, idx, cnt = 0; 657503831d35Sstevel drmachid_t id; 657603831d35Sstevel 657703831d35Sstevel ASSERT(MUTEX_HELD(&drmach_bus_sync_lock)); 657803831d35Sstevel 657903831d35Sstevel rv = drmach_array_first(drmach_boards, &idx, &id); 658003831d35Sstevel while (rv == 0) { 658103831d35Sstevel drmach_board_t *bp = id; 658203831d35Sstevel drmach_mem_t *mp = bp->mem; 658303831d35Sstevel 658403831d35Sstevel while (mp) { 658503831d35Sstevel int bank; 658603831d35Sstevel 658703831d35Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) { 658803831d35Sstevel uint64_t madr; 658903831d35Sstevel 659003831d35Sstevel drmach_mem_read_madr(mp, bank, &madr); 659103831d35Sstevel if (madr & DRMACH_MC_VALID_MASK) { 659203831d35Sstevel uint64_t pa; 659303831d35Sstevel 659403831d35Sstevel pa = DRMACH_MC_UM_TO_PA(madr); 659503831d35Sstevel pa |= DRMACH_MC_LM_TO_PA(madr); 659603831d35Sstevel 659703831d35Sstevel /* 659803831d35Sstevel * The list is zero terminated. 659903831d35Sstevel * Offset the pa by a doubleword 660003831d35Sstevel * to avoid confusing a pa value of 660103831d35Sstevel * of zero with the terminator. 660203831d35Sstevel */ 660303831d35Sstevel pa += sizeof (uint64_t); 660403831d35Sstevel 660503831d35Sstevel drmach_bus_sync_list[cnt++] = pa; 660603831d35Sstevel } 660703831d35Sstevel } 660803831d35Sstevel 660903831d35Sstevel mp = mp->next; 661003831d35Sstevel } 661103831d35Sstevel 661203831d35Sstevel rv = drmach_array_next(drmach_boards, &idx, &id); 661303831d35Sstevel } 661403831d35Sstevel 661503831d35Sstevel drmach_bus_sync_list[cnt] = 0; 661603831d35Sstevel } 661703831d35Sstevel 661803831d35Sstevel sbd_error_t * 661903831d35Sstevel drmach_mem_get_memlist(drmachid_t id, struct memlist **ml) 662003831d35Sstevel { 662103831d35Sstevel sbd_error_t *err; 662203831d35Sstevel struct memlist *mlist; 662303831d35Sstevel gdcd_t *gdcd; 662403831d35Sstevel mem_chunk_t *chunk; 662503831d35Sstevel uint64_t chunks, pa, mask; 662603831d35Sstevel 662703831d35Sstevel err = drmach_mem_get_base_physaddr(id, &pa); 662803831d35Sstevel if (err) 662903831d35Sstevel return (err); 663003831d35Sstevel 663103831d35Sstevel gdcd = drmach_gdcd_new(); 663203831d35Sstevel if (gdcd == NULL) 663303831d35Sstevel return (DRMACH_INTERNAL_ERROR()); 663403831d35Sstevel 663503831d35Sstevel mask = ~ (DRMACH_MEM_SLICE_SIZE - 1); 663603831d35Sstevel pa &= mask; 663703831d35Sstevel 663803831d35Sstevel mlist = NULL; 663903831d35Sstevel chunk = gdcd->dcd_chunk_list.dcl_chunk; 664003831d35Sstevel chunks = gdcd->dcd_chunk_list.dcl_chunks; 664103831d35Sstevel while (chunks-- != 0) { 664203831d35Sstevel if ((chunk->mc_base_pa & mask) == pa) { 6643d3d50737SRafael Vanoni mlist = memlist_add_span(mlist, chunk->mc_base_pa, 6644d3d50737SRafael Vanoni chunk->mc_mbytes * 1048576); 664503831d35Sstevel } 664603831d35Sstevel 664703831d35Sstevel ++chunk; 664803831d35Sstevel } 664903831d35Sstevel 665003831d35Sstevel drmach_gdcd_dispose(gdcd); 665103831d35Sstevel 665203831d35Sstevel #ifdef DEBUG 665303831d35Sstevel DRMACH_PR("GDCD derived memlist:"); 665403831d35Sstevel memlist_dump(mlist); 665503831d35Sstevel #endif 665603831d35Sstevel 665703831d35Sstevel *ml = mlist; 665803831d35Sstevel return (NULL); 665903831d35Sstevel } 666003831d35Sstevel 666103831d35Sstevel sbd_error_t * 666203831d35Sstevel drmach_mem_get_size(drmachid_t id, uint64_t *bytes) 666303831d35Sstevel { 666403831d35Sstevel drmach_mem_t *mp; 666503831d35Sstevel 666603831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 666703831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 666803831d35Sstevel mp = id; 666903831d35Sstevel 667003831d35Sstevel ASSERT(mp->nbytes != 0); 667103831d35Sstevel *bytes = mp->nbytes; 667203831d35Sstevel 667303831d35Sstevel return (NULL); 667403831d35Sstevel } 667503831d35Sstevel 667603831d35Sstevel sbd_error_t * 667703831d35Sstevel drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes) 667803831d35Sstevel { 667903831d35Sstevel sbd_error_t *err; 668003831d35Sstevel drmach_device_t *mp; 668103831d35Sstevel 668203831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 668303831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 668403831d35Sstevel mp = id; 668503831d35Sstevel 668603831d35Sstevel switch (DRMACH_BNUM2SLOT(mp->bp->bnum)) { 668703831d35Sstevel case 0: *bytes = DRMACH_MEM_USABLE_SLICE_SIZE; 668803831d35Sstevel err = NULL; 668903831d35Sstevel break; 669003831d35Sstevel 669103831d35Sstevel case 1: *bytes = 0; 669203831d35Sstevel err = NULL; 669303831d35Sstevel break; 669403831d35Sstevel 669503831d35Sstevel default: 669603831d35Sstevel err = DRMACH_INTERNAL_ERROR(); 669703831d35Sstevel break; 669803831d35Sstevel } 669903831d35Sstevel 670003831d35Sstevel return (err); 670103831d35Sstevel } 670203831d35Sstevel 670303831d35Sstevel processorid_t drmach_mem_cpu_affinity_nail; 670403831d35Sstevel 670503831d35Sstevel processorid_t 670603831d35Sstevel drmach_mem_cpu_affinity(drmachid_t id) 670703831d35Sstevel { 670803831d35Sstevel drmach_device_t *mp; 670903831d35Sstevel drmach_board_t *bp; 671003831d35Sstevel processorid_t cpuid; 671103831d35Sstevel 671203831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 671303831d35Sstevel return (CPU_CURRENT); 671403831d35Sstevel 671503831d35Sstevel if (drmach_mem_cpu_affinity_nail) { 671603831d35Sstevel cpuid = drmach_mem_cpu_affinity_nail; 671703831d35Sstevel 671803831d35Sstevel if (cpuid < 0 || cpuid > NCPU) 671903831d35Sstevel return (CPU_CURRENT); 672003831d35Sstevel 672103831d35Sstevel mutex_enter(&cpu_lock); 672203831d35Sstevel if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid])) 672303831d35Sstevel cpuid = CPU_CURRENT; 672403831d35Sstevel mutex_exit(&cpu_lock); 672503831d35Sstevel 672603831d35Sstevel return (cpuid); 672703831d35Sstevel } 672803831d35Sstevel 672903831d35Sstevel /* try to choose a proc on the target board */ 673003831d35Sstevel mp = id; 673103831d35Sstevel bp = mp->bp; 673203831d35Sstevel if (bp->devices) { 673303831d35Sstevel int rv; 673403831d35Sstevel int d_idx; 673503831d35Sstevel drmachid_t d_id; 673603831d35Sstevel 673703831d35Sstevel rv = drmach_array_first(bp->devices, &d_idx, &d_id); 673803831d35Sstevel while (rv == 0) { 673903831d35Sstevel if (DRMACH_IS_CPU_ID(d_id)) { 674003831d35Sstevel drmach_cpu_t *cp = d_id; 674103831d35Sstevel 674203831d35Sstevel mutex_enter(&cpu_lock); 674303831d35Sstevel cpuid = cp->cpuid; 674403831d35Sstevel if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) { 674503831d35Sstevel mutex_exit(&cpu_lock); 674603831d35Sstevel return (cpuid); 674703831d35Sstevel } else { 674803831d35Sstevel mutex_exit(&cpu_lock); 674903831d35Sstevel } 675003831d35Sstevel } 675103831d35Sstevel 675203831d35Sstevel rv = drmach_array_next(bp->devices, &d_idx, &d_id); 675303831d35Sstevel } 675403831d35Sstevel } 675503831d35Sstevel 675603831d35Sstevel /* otherwise, this proc, wherever it is */ 675703831d35Sstevel return (CPU_CURRENT); 675803831d35Sstevel } 675903831d35Sstevel 676003831d35Sstevel static sbd_error_t * 676103831d35Sstevel drmach_mem_release(drmachid_t id) 676203831d35Sstevel { 676303831d35Sstevel if (!DRMACH_IS_MEM_ID(id)) 676403831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 676503831d35Sstevel return (NULL); 676603831d35Sstevel } 676703831d35Sstevel 676803831d35Sstevel static sbd_error_t * 676903831d35Sstevel drmach_mem_status(drmachid_t id, drmach_status_t *stat) 677003831d35Sstevel { 677103831d35Sstevel drmach_mem_t *mp; 677203831d35Sstevel sbd_error_t *err; 677303831d35Sstevel uint64_t pa, slice_size; 677403831d35Sstevel struct memlist *ml; 677503831d35Sstevel 677603831d35Sstevel ASSERT(DRMACH_IS_MEM_ID(id)); 677703831d35Sstevel mp = id; 677803831d35Sstevel 677903831d35Sstevel /* get starting physical address of target memory */ 678003831d35Sstevel err = drmach_mem_get_base_physaddr(id, &pa); 678103831d35Sstevel if (err) 678203831d35Sstevel return (err); 678303831d35Sstevel 678403831d35Sstevel /* round down to slice boundary */ 678503831d35Sstevel slice_size = DRMACH_MEM_SLICE_SIZE; 678603831d35Sstevel pa &= ~ (slice_size - 1); 678703831d35Sstevel 678803831d35Sstevel /* stop at first span that is in slice */ 678903831d35Sstevel memlist_read_lock(); 679056f33205SJonathan Adams for (ml = phys_install; ml; ml = ml->ml_next) 679156f33205SJonathan Adams if (ml->ml_address >= pa && ml->ml_address < pa + slice_size) 679203831d35Sstevel break; 679303831d35Sstevel memlist_read_unlock(); 679403831d35Sstevel 679503831d35Sstevel stat->assigned = mp->dev.bp->assigned; 679603831d35Sstevel stat->powered = mp->dev.bp->powered; 679703831d35Sstevel stat->configured = (ml != NULL); 679803831d35Sstevel stat->busy = mp->dev.busy; 679907d06da5SSurya Prakki (void) strncpy(stat->type, mp->dev.type, sizeof (stat->type)); 680003831d35Sstevel stat->info[0] = '\0'; 680103831d35Sstevel 680203831d35Sstevel return (NULL); 680303831d35Sstevel } 680403831d35Sstevel 680503831d35Sstevel sbd_error_t * 680603831d35Sstevel drmach_board_deprobe(drmachid_t id) 680703831d35Sstevel { 680803831d35Sstevel drmach_board_t *bp; 680903831d35Sstevel sbd_error_t *err = NULL; 681003831d35Sstevel 681103831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 681203831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 681303831d35Sstevel bp = id; 681403831d35Sstevel 681503831d35Sstevel if (bp->tree) { 681603831d35Sstevel drmach_node_dispose(bp->tree); 681703831d35Sstevel bp->tree = NULL; 681803831d35Sstevel } 681903831d35Sstevel if (bp->devices) { 682003831d35Sstevel drmach_array_dispose(bp->devices, drmach_device_dispose); 682103831d35Sstevel bp->devices = NULL; 682203831d35Sstevel bp->mem = NULL; /* TODO: still needed? */ 682303831d35Sstevel } 682403831d35Sstevel return (err); 682503831d35Sstevel } 682603831d35Sstevel 682703831d35Sstevel /*ARGSUSED1*/ 682803831d35Sstevel static sbd_error_t * 682903831d35Sstevel drmach_pt_showlpa(drmachid_t id, drmach_opts_t *opts) 683003831d35Sstevel { 683103831d35Sstevel drmach_device_t *dp; 683203831d35Sstevel uint64_t val; 683303831d35Sstevel int err = 1; 683403831d35Sstevel 683503831d35Sstevel if (DRMACH_IS_CPU_ID(id)) { 683603831d35Sstevel drmach_cpu_t *cp = id; 683703831d35Sstevel if (drmach_cpu_read_scr(cp, &val)) 683803831d35Sstevel err = 0; 683903831d35Sstevel } else if (DRMACH_IS_IO_ID(id) && ((drmach_io_t *)id)->scsr_pa != 0) { 684003831d35Sstevel drmach_io_t *io = id; 684103831d35Sstevel val = lddphysio(io->scsr_pa); 684203831d35Sstevel err = 0; 684303831d35Sstevel } 684403831d35Sstevel if (err) 684503831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 684603831d35Sstevel 684703831d35Sstevel dp = id; 684803831d35Sstevel uprintf("showlpa %s::%s portid %d, base pa %lx, bound pa %lx\n", 684903831d35Sstevel dp->bp->cm.name, 685003831d35Sstevel dp->cm.name, 685103831d35Sstevel dp->portid, 685207d06da5SSurya Prakki (long)(DRMACH_LPA_BASE_TO_PA(val)), 685307d06da5SSurya Prakki (long)(DRMACH_LPA_BND_TO_PA(val))); 685403831d35Sstevel 685503831d35Sstevel return (NULL); 685603831d35Sstevel } 685703831d35Sstevel 685803831d35Sstevel /*ARGSUSED*/ 685903831d35Sstevel static sbd_error_t * 686003831d35Sstevel drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts) 686103831d35Sstevel { 686203831d35Sstevel drmach_board_t *bp = (drmach_board_t *)id; 686303831d35Sstevel sbd_error_t *err; 686403831d35Sstevel sc_gptwocfg_cookie_t scc; 686503831d35Sstevel 686603831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 686703831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 686803831d35Sstevel 686903831d35Sstevel /* do saf configurator stuff */ 687003831d35Sstevel DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum); 687103831d35Sstevel scc = sc_probe_board(bp->bnum); 687203831d35Sstevel if (scc == NULL) { 687303831d35Sstevel err = drerr_new(0, ESTC_PROBE, bp->cm.name); 687403831d35Sstevel return (err); 687503831d35Sstevel } 687603831d35Sstevel 687703831d35Sstevel return (err); 687803831d35Sstevel } 687903831d35Sstevel 688003831d35Sstevel /*ARGSUSED*/ 688103831d35Sstevel static sbd_error_t * 688203831d35Sstevel drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts) 688303831d35Sstevel { 688403831d35Sstevel drmach_board_t *bp; 688503831d35Sstevel sbd_error_t *err = NULL; 688603831d35Sstevel sc_gptwocfg_cookie_t scc; 688703831d35Sstevel 688803831d35Sstevel if (!DRMACH_IS_BOARD_ID(id)) 688903831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 689003831d35Sstevel bp = id; 689103831d35Sstevel 689203831d35Sstevel cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum); 689303831d35Sstevel scc = sc_unprobe_board(bp->bnum); 689403831d35Sstevel if (scc != NULL) { 689503831d35Sstevel err = drerr_new(0, ESTC_DEPROBE, bp->cm.name); 689603831d35Sstevel } 689703831d35Sstevel 689803831d35Sstevel if (err == NULL) 689903831d35Sstevel err = drmach_board_deprobe(id); 690003831d35Sstevel 690103831d35Sstevel return (err); 690203831d35Sstevel } 690303831d35Sstevel 690403831d35Sstevel static sbd_error_t * 690503831d35Sstevel drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts) 690603831d35Sstevel { 690703831d35Sstevel _NOTE(ARGUNUSED(id)) 690803831d35Sstevel _NOTE(ARGUNUSED(opts)) 690903831d35Sstevel 691003831d35Sstevel struct memlist *ml; 691103831d35Sstevel uint64_t src_pa; 691203831d35Sstevel uint64_t dst_pa; 691303831d35Sstevel uint64_t dst; 691403831d35Sstevel 691503831d35Sstevel dst_pa = va_to_pa(&dst); 691603831d35Sstevel 691703831d35Sstevel memlist_read_lock(); 691856f33205SJonathan Adams for (ml = phys_install; ml; ml = ml->ml_next) { 691903831d35Sstevel uint64_t nbytes; 692003831d35Sstevel 692156f33205SJonathan Adams src_pa = ml->ml_address; 692256f33205SJonathan Adams nbytes = ml->ml_size; 692303831d35Sstevel 692403831d35Sstevel while (nbytes != 0ull) { 692503831d35Sstevel 692603831d35Sstevel /* copy 32 bytes at src_pa to dst_pa */ 692703831d35Sstevel bcopy32_il(src_pa, dst_pa); 692803831d35Sstevel 692903831d35Sstevel /* increment by 32 bytes */ 693003831d35Sstevel src_pa += (4 * sizeof (uint64_t)); 693103831d35Sstevel 693203831d35Sstevel /* decrement by 32 bytes */ 693303831d35Sstevel nbytes -= (4 * sizeof (uint64_t)); 693403831d35Sstevel } 693503831d35Sstevel } 693603831d35Sstevel memlist_read_unlock(); 693703831d35Sstevel 693803831d35Sstevel return (NULL); 693903831d35Sstevel } 694003831d35Sstevel 694103831d35Sstevel static sbd_error_t * 694203831d35Sstevel drmach_pt_recovercpu(drmachid_t id, drmach_opts_t *opts) 694303831d35Sstevel { 694403831d35Sstevel _NOTE(ARGUNUSED(opts)) 694503831d35Sstevel 694603831d35Sstevel drmach_cpu_t *cp; 694703831d35Sstevel 694803831d35Sstevel if (!DRMACH_IS_CPU_ID(id)) 694903831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 695003831d35Sstevel cp = id; 695103831d35Sstevel 695203831d35Sstevel mutex_enter(&cpu_lock); 695303831d35Sstevel (void) drmach_iocage_cpu_return(&(cp->dev), 695403831d35Sstevel CPU_ENABLE | CPU_EXISTS | CPU_READY | CPU_RUNNING); 695503831d35Sstevel mutex_exit(&cpu_lock); 695603831d35Sstevel 695703831d35Sstevel return (NULL); 695803831d35Sstevel } 695903831d35Sstevel 696003831d35Sstevel /* 696103831d35Sstevel * Starcat DR passthrus are for debugging purposes only. 696203831d35Sstevel */ 696303831d35Sstevel static struct { 696403831d35Sstevel const char *name; 696503831d35Sstevel sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts); 696603831d35Sstevel } drmach_pt_arr[] = { 696703831d35Sstevel { "showlpa", drmach_pt_showlpa }, 696803831d35Sstevel { "ikprobe", drmach_pt_ikprobe }, 696903831d35Sstevel { "ikdeprobe", drmach_pt_ikdeprobe }, 697003831d35Sstevel { "readmem", drmach_pt_readmem }, 697103831d35Sstevel { "recovercpu", drmach_pt_recovercpu }, 697203831d35Sstevel 697303831d35Sstevel /* the following line must always be last */ 697403831d35Sstevel { NULL, NULL } 697503831d35Sstevel }; 697603831d35Sstevel 697703831d35Sstevel /*ARGSUSED*/ 697803831d35Sstevel sbd_error_t * 697903831d35Sstevel drmach_passthru(drmachid_t id, drmach_opts_t *opts) 698003831d35Sstevel { 698103831d35Sstevel int i; 698203831d35Sstevel sbd_error_t *err; 698303831d35Sstevel 698403831d35Sstevel i = 0; 698503831d35Sstevel while (drmach_pt_arr[i].name != NULL) { 698603831d35Sstevel int len = strlen(drmach_pt_arr[i].name); 698703831d35Sstevel 698803831d35Sstevel if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0) 698903831d35Sstevel break; 699003831d35Sstevel 699103831d35Sstevel i += 1; 699203831d35Sstevel } 699303831d35Sstevel 699403831d35Sstevel if (drmach_pt_arr[i].name == NULL) 699503831d35Sstevel err = drerr_new(0, ESTC_UNKPTCMD, opts->copts); 699603831d35Sstevel else 699703831d35Sstevel err = (*drmach_pt_arr[i].handler)(id, opts); 699803831d35Sstevel 699903831d35Sstevel return (err); 700003831d35Sstevel } 700103831d35Sstevel 700203831d35Sstevel sbd_error_t * 700303831d35Sstevel drmach_release(drmachid_t id) 700403831d35Sstevel { 700503831d35Sstevel drmach_common_t *cp; 700603831d35Sstevel 700703831d35Sstevel if (!DRMACH_IS_DEVICE_ID(id)) 700803831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 700903831d35Sstevel cp = id; 701003831d35Sstevel 701103831d35Sstevel return (cp->release(id)); 701203831d35Sstevel } 701303831d35Sstevel 701403831d35Sstevel sbd_error_t * 701503831d35Sstevel drmach_status(drmachid_t id, drmach_status_t *stat) 701603831d35Sstevel { 701703831d35Sstevel drmach_common_t *cp; 701803831d35Sstevel sbd_error_t *err; 701903831d35Sstevel 702003831d35Sstevel rw_enter(&drmach_boards_rwlock, RW_READER); 702103831d35Sstevel 702203831d35Sstevel if (!DRMACH_IS_ID(id)) { 702303831d35Sstevel rw_exit(&drmach_boards_rwlock); 702403831d35Sstevel return (drerr_new(0, ESTC_NOTID, NULL)); 702503831d35Sstevel } 702603831d35Sstevel 702703831d35Sstevel cp = id; 702803831d35Sstevel 702903831d35Sstevel err = cp->status(id, stat); 703003831d35Sstevel rw_exit(&drmach_boards_rwlock); 703103831d35Sstevel return (err); 703203831d35Sstevel } 703303831d35Sstevel 703403831d35Sstevel static sbd_error_t * 703503831d35Sstevel drmach_i_status(drmachid_t id, drmach_status_t *stat) 703603831d35Sstevel { 703703831d35Sstevel drmach_common_t *cp; 703803831d35Sstevel 703903831d35Sstevel if (!DRMACH_IS_ID(id)) 704003831d35Sstevel return (drerr_new(0, ESTC_NOTID, NULL)); 704103831d35Sstevel cp = id; 704203831d35Sstevel 704303831d35Sstevel return (cp->status(id, stat)); 704403831d35Sstevel } 704503831d35Sstevel 704603831d35Sstevel /*ARGSUSED*/ 704703831d35Sstevel sbd_error_t * 704803831d35Sstevel drmach_unconfigure(drmachid_t id, int flags) 704903831d35Sstevel { 705003831d35Sstevel drmach_device_t *dp; 705103831d35Sstevel dev_info_t *rdip; 705203831d35Sstevel 705303831d35Sstevel char name[OBP_MAXDRVNAME]; 705403831d35Sstevel int rv; 705503831d35Sstevel 705603831d35Sstevel /* 705703831d35Sstevel * Since CPU nodes are not configured, it is 705803831d35Sstevel * necessary to skip the unconfigure step as 705903831d35Sstevel * well. 706003831d35Sstevel */ 706103831d35Sstevel if (DRMACH_IS_CPU_ID(id)) { 706203831d35Sstevel return (NULL); 706303831d35Sstevel } 706403831d35Sstevel 706503831d35Sstevel for (; id; ) { 706603831d35Sstevel dev_info_t *fdip = NULL; 706703831d35Sstevel 706803831d35Sstevel if (!DRMACH_IS_DEVICE_ID(id)) 706903831d35Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL)); 707003831d35Sstevel dp = id; 707103831d35Sstevel 707203831d35Sstevel rdip = dp->node->n_getdip(dp->node); 707303831d35Sstevel 707403831d35Sstevel /* 707503831d35Sstevel * drmach_unconfigure() is always called on a configured branch. 707603831d35Sstevel * So the root of the branch was held earlier and must exist. 707703831d35Sstevel */ 707803831d35Sstevel ASSERT(rdip); 707903831d35Sstevel 708003831d35Sstevel DRMACH_PR("drmach_unconfigure: unconfiguring DDI branch"); 708103831d35Sstevel 708203831d35Sstevel rv = dp->node->n_getprop(dp->node, 708303831d35Sstevel "name", name, OBP_MAXDRVNAME); 708403831d35Sstevel 708503831d35Sstevel /* The node must have a name */ 708603831d35Sstevel if (rv) 708703831d35Sstevel return (0); 708803831d35Sstevel 708903831d35Sstevel if (drmach_name2type_idx(name) < 0) { 709003831d35Sstevel if (DRMACH_IS_MEM_ID(id)) { 709103831d35Sstevel drmach_mem_t *mp = id; 709203831d35Sstevel id = mp->next; 709303831d35Sstevel } else { 709403831d35Sstevel id = NULL; 709503831d35Sstevel } 709603831d35Sstevel continue; 709703831d35Sstevel } 709803831d35Sstevel 709903831d35Sstevel /* 710003831d35Sstevel * NOTE: FORCE flag is no longer needed under devfs 710103831d35Sstevel */ 710203831d35Sstevel ASSERT(e_ddi_branch_held(rdip)); 710303831d35Sstevel if (e_ddi_branch_unconfigure(rdip, &fdip, 0) != 0) { 710403831d35Sstevel sbd_error_t *err = NULL; 7105d3d50737SRafael Vanoni char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 710603831d35Sstevel 710703831d35Sstevel /* 710803831d35Sstevel * If non-NULL, fdip is returned held and must be 710903831d35Sstevel * released. 711003831d35Sstevel */ 711103831d35Sstevel if (fdip != NULL) { 711203831d35Sstevel (void) ddi_pathname(fdip, path); 711303831d35Sstevel ddi_release_devi(fdip); 711403831d35Sstevel } else { 711503831d35Sstevel (void) ddi_pathname(rdip, path); 711603831d35Sstevel } 711703831d35Sstevel 711803831d35Sstevel err = drerr_new(1, ESTC_DRVFAIL, path); 711903831d35Sstevel 712003831d35Sstevel kmem_free(path, MAXPATHLEN); 712103831d35Sstevel 712203831d35Sstevel /* 712303831d35Sstevel * If we were unconfiguring an IO board, a call was 712403831d35Sstevel * made to man_dr_detach. We now need to call 712503831d35Sstevel * man_dr_attach to regain man use of the eri. 712603831d35Sstevel */ 712703831d35Sstevel if (DRMACH_IS_IO_ID(id)) { 712803831d35Sstevel int (*func)(dev_info_t *dip); 712903831d35Sstevel 713003831d35Sstevel func = (int (*)(dev_info_t *))kobj_getsymvalue\ 713103831d35Sstevel ("man_dr_attach", 0); 713203831d35Sstevel 713303831d35Sstevel if (func) { 713403831d35Sstevel drmach_io_inst_t ios; 713503831d35Sstevel dev_info_t *pdip; 713603831d35Sstevel int circ; 713703831d35Sstevel 713803831d35Sstevel /* 713903831d35Sstevel * Walk device tree to find rio dip for 714003831d35Sstevel * the board 714103831d35Sstevel * Since we are not interested in iosram 714203831d35Sstevel * instance here, initialize it to 0, so 714303831d35Sstevel * that the walk terminates as soon as 714403831d35Sstevel * eri dip is found. 714503831d35Sstevel */ 714603831d35Sstevel ios.iosram_inst = 0; 714703831d35Sstevel ios.eri_dip = NULL; 714803831d35Sstevel ios.bnum = dp->bp->bnum; 714903831d35Sstevel 715003831d35Sstevel if (pdip = ddi_get_parent(rdip)) { 715103831d35Sstevel ndi_hold_devi(pdip); 715203831d35Sstevel ndi_devi_enter(pdip, &circ); 715303831d35Sstevel } 715403831d35Sstevel /* 715503831d35Sstevel * Root node doesn't have to be held in 715603831d35Sstevel * any way. 715703831d35Sstevel */ 715803831d35Sstevel ASSERT(e_ddi_branch_held(rdip)); 715903831d35Sstevel ddi_walk_devs(rdip, 716003831d35Sstevel drmach_board_find_io_insts, 716103831d35Sstevel (void *)&ios); 716203831d35Sstevel 716303831d35Sstevel DRMACH_PR("drmach_unconfigure: bnum=%d" 716403831d35Sstevel " eri=0x%p\n", 716507d06da5SSurya Prakki ios.bnum, (void *)ios.eri_dip); 716603831d35Sstevel 716703831d35Sstevel if (pdip) { 716803831d35Sstevel ndi_devi_exit(pdip, circ); 716903831d35Sstevel ndi_rele_devi(pdip); 717003831d35Sstevel } 717103831d35Sstevel 717203831d35Sstevel if (ios.eri_dip) { 717303831d35Sstevel DRMACH_PR("calling" 717403831d35Sstevel " man_dr_attach\n"); 717503831d35Sstevel (void) (*func)(ios.eri_dip); 717603831d35Sstevel /* 717703831d35Sstevel * Release hold acquired in 717803831d35Sstevel * drmach_board_find_io_insts() 717903831d35Sstevel */ 718003831d35Sstevel ndi_rele_devi(ios.eri_dip); 718103831d35Sstevel } 718203831d35Sstevel } 718303831d35Sstevel } 718403831d35Sstevel return (err); 718503831d35Sstevel } 718603831d35Sstevel 718703831d35Sstevel if (DRMACH_IS_MEM_ID(id)) { 718803831d35Sstevel drmach_mem_t *mp = id; 718903831d35Sstevel id = mp->next; 719003831d35Sstevel } else { 719103831d35Sstevel id = NULL; 719203831d35Sstevel } 719303831d35Sstevel } 719403831d35Sstevel 719503831d35Sstevel return (NULL); 719603831d35Sstevel } 719703831d35Sstevel 719803831d35Sstevel /* 719903831d35Sstevel * drmach interfaces to legacy Starfire platmod logic 720003831d35Sstevel * linkage via runtime symbol look up, called from plat_cpu_power* 720103831d35Sstevel */ 720203831d35Sstevel 720303831d35Sstevel /* 720403831d35Sstevel * Start up a cpu. It is possible that we're attempting to restart 720503831d35Sstevel * the cpu after an UNCONFIGURE in which case the cpu will be 720603831d35Sstevel * spinning in its cache. So, all we have to do is wakeup him up. 720703831d35Sstevel * Under normal circumstances the cpu will be coming from a previous 720803831d35Sstevel * CONNECT and thus will be spinning in OBP. In both cases, the 720903831d35Sstevel * startup sequence is the same. 721003831d35Sstevel */ 721103831d35Sstevel int 721203831d35Sstevel drmach_cpu_poweron(struct cpu *cp) 721303831d35Sstevel { 721403831d35Sstevel DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id); 721503831d35Sstevel 721603831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 721703831d35Sstevel 721803831d35Sstevel if (drmach_cpu_start(cp) != 0) 721903831d35Sstevel return (EBUSY); 722003831d35Sstevel else 722103831d35Sstevel return (0); 722203831d35Sstevel } 722303831d35Sstevel 722403831d35Sstevel int 722503831d35Sstevel drmach_cpu_poweroff(struct cpu *cp) 722603831d35Sstevel { 722703831d35Sstevel int ntries; 722803831d35Sstevel processorid_t cpuid; 722903831d35Sstevel void drmach_cpu_shutdown_self(void); 723003831d35Sstevel 723103831d35Sstevel DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id); 723203831d35Sstevel 723303831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 723403831d35Sstevel 723503831d35Sstevel /* 723603831d35Sstevel * XXX CHEETAH SUPPORT 723703831d35Sstevel * for cheetah, we need to grab the iocage lock since iocage 723803831d35Sstevel * memory is used for e$ flush. 723903831d35Sstevel */ 724003831d35Sstevel if (drmach_is_cheetah) { 724103831d35Sstevel mutex_enter(&drmach_iocage_lock); 724203831d35Sstevel while (drmach_iocage_is_busy) 724303831d35Sstevel cv_wait(&drmach_iocage_cv, &drmach_iocage_lock); 724403831d35Sstevel drmach_iocage_is_busy = 1; 724503831d35Sstevel drmach_iocage_mem_scrub(ecache_size * 2); 724603831d35Sstevel mutex_exit(&drmach_iocage_lock); 724703831d35Sstevel } 724803831d35Sstevel 724903831d35Sstevel cpuid = cp->cpu_id; 725003831d35Sstevel 725103831d35Sstevel /* 725203831d35Sstevel * Set affinity to ensure consistent reading and writing of 725303831d35Sstevel * drmach_xt_mb[cpuid] by one "master" CPU directing 725403831d35Sstevel * the shutdown of the target CPU. 725503831d35Sstevel */ 725603831d35Sstevel affinity_set(CPU->cpu_id); 725703831d35Sstevel 725803831d35Sstevel /* 725903831d35Sstevel * Capture all CPUs (except for detaching proc) to prevent 726003831d35Sstevel * crosscalls to the detaching proc until it has cleared its 726103831d35Sstevel * bit in cpu_ready_set. 726203831d35Sstevel * 726303831d35Sstevel * The CPUs remain paused and the prom_mutex is known to be free. 726403831d35Sstevel * This prevents blocking when doing prom IEEE-1275 calls at a 726503831d35Sstevel * high PIL level. 726603831d35Sstevel */ 726703831d35Sstevel promsafe_pause_cpus(); 726803831d35Sstevel 726903831d35Sstevel /* 727003831d35Sstevel * Quiesce interrupts on the target CPU. We do this by setting 727103831d35Sstevel * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to 727203831d35Sstevel * prevent it from receiving cross calls and cross traps. 727303831d35Sstevel * This prevents the processor from receiving any new soft interrupts. 727403831d35Sstevel */ 727503831d35Sstevel mp_cpu_quiesce(cp); 727603831d35Sstevel 727707d06da5SSurya Prakki (void) prom_hotremovecpu(cpuid); 727803831d35Sstevel 727903831d35Sstevel start_cpus(); 728003831d35Sstevel 728103831d35Sstevel /* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */ 728203831d35Sstevel drmach_xt_mb[cpuid] = 0x80; 728303831d35Sstevel 728403831d35Sstevel xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall, 728503831d35Sstevel (uint64_t)drmach_cpu_shutdown_self, NULL); 728603831d35Sstevel 728703831d35Sstevel ntries = drmach_cpu_ntries; 728803831d35Sstevel while (drmach_xt_mb[cpuid] && ntries) { 728903831d35Sstevel DELAY(drmach_cpu_delay); 729003831d35Sstevel ntries--; 729103831d35Sstevel } 729203831d35Sstevel 729303831d35Sstevel drmach_xt_mb[cpuid] = 0; /* steal the cache line back */ 729403831d35Sstevel 729503831d35Sstevel membar_sync(); /* make sure copy-back retires */ 729603831d35Sstevel 729703831d35Sstevel affinity_clear(); 729803831d35Sstevel 729903831d35Sstevel /* 730003831d35Sstevel * XXX CHEETAH SUPPORT 730103831d35Sstevel */ 730203831d35Sstevel if (drmach_is_cheetah) { 730303831d35Sstevel mutex_enter(&drmach_iocage_lock); 730403831d35Sstevel drmach_iocage_mem_scrub(ecache_size * 2); 730503831d35Sstevel drmach_iocage_is_busy = 0; 730603831d35Sstevel cv_signal(&drmach_iocage_cv); 730703831d35Sstevel mutex_exit(&drmach_iocage_lock); 730803831d35Sstevel } 730903831d35Sstevel 731003831d35Sstevel DRMACH_PR("waited %d out of %d tries for " 731103831d35Sstevel "drmach_cpu_shutdown_self on cpu%d", 731203831d35Sstevel drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id); 731303831d35Sstevel 731403831d35Sstevel /* 731503831d35Sstevel * Do this here instead of drmach_cpu_shutdown_self() to 731603831d35Sstevel * avoid an assertion failure panic in turnstile.c. 731703831d35Sstevel */ 731803831d35Sstevel CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid); 731903831d35Sstevel 732003831d35Sstevel return (0); 732103831d35Sstevel } 732203831d35Sstevel 732303831d35Sstevel void 732403831d35Sstevel drmach_iocage_mem_scrub(uint64_t nbytes) 732503831d35Sstevel { 732607d06da5SSurya Prakki extern uint32_t drmach_bc_bzero(void*, size_t); 732707d06da5SSurya Prakki uint32_t rv; 732803831d35Sstevel 732903831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 733003831d35Sstevel 733103831d35Sstevel affinity_set(CPU->cpu_id); 733203831d35Sstevel 733303831d35Sstevel rv = drmach_bc_bzero(drmach_iocage_vaddr, nbytes); 733403831d35Sstevel if (rv != 0) { 733503831d35Sstevel DRMACH_PR( 733603831d35Sstevel "iocage scrub failed, drmach_bc_bzero returned %d\n", rv); 7337d3d50737SRafael Vanoni rv = drmach_bc_bzero(drmach_iocage_vaddr, drmach_iocage_size); 733803831d35Sstevel if (rv != 0) 733903831d35Sstevel cmn_err(CE_PANIC, 734003831d35Sstevel "iocage scrub failed, drmach_bc_bzero rv=%d\n", 734103831d35Sstevel rv); 734203831d35Sstevel } 734303831d35Sstevel 734403831d35Sstevel cpu_flush_ecache(); 734503831d35Sstevel 734603831d35Sstevel affinity_clear(); 734703831d35Sstevel } 734803831d35Sstevel 734903831d35Sstevel #define ALIGN(x, a) ((a) == 0 ? (uintptr_t)(x) : \ 735003831d35Sstevel (((uintptr_t)(x) + (uintptr_t)(a) - 1l) & ~((uintptr_t)(a) - 1l))) 735103831d35Sstevel 735203831d35Sstevel static sbd_error_t * 735303831d35Sstevel drmach_iocage_mem_get(dr_testboard_req_t *tbrq) 735403831d35Sstevel { 735503831d35Sstevel pfn_t basepfn; 735603831d35Sstevel pgcnt_t npages; 735703831d35Sstevel extern int memscrub_delete_span(pfn_t, pgcnt_t); 735803831d35Sstevel uint64_t drmach_iocage_paddr_mbytes; 735903831d35Sstevel 736003831d35Sstevel ASSERT(drmach_iocage_paddr != -1); 736103831d35Sstevel 736203831d35Sstevel basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT); 736303831d35Sstevel npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT); 736403831d35Sstevel 736507d06da5SSurya Prakki (void) memscrub_delete_span(basepfn, npages); 736603831d35Sstevel 736703831d35Sstevel mutex_enter(&cpu_lock); 736803831d35Sstevel drmach_iocage_mem_scrub(drmach_iocage_size); 736903831d35Sstevel mutex_exit(&cpu_lock); 737003831d35Sstevel 737103831d35Sstevel /* 737203831d35Sstevel * HPOST wants the address of the cage to be 64 megabyte-aligned 737303831d35Sstevel * and in megabyte units. 737403831d35Sstevel * The size of the cage is also in megabyte units. 737503831d35Sstevel */ 737603831d35Sstevel ASSERT(drmach_iocage_paddr == ALIGN(drmach_iocage_paddr, 0x4000000)); 737703831d35Sstevel 737803831d35Sstevel drmach_iocage_paddr_mbytes = drmach_iocage_paddr / 0x100000; 737903831d35Sstevel 738003831d35Sstevel tbrq->memaddrhi = (uint32_t)(drmach_iocage_paddr_mbytes >> 32); 738103831d35Sstevel tbrq->memaddrlo = (uint32_t)drmach_iocage_paddr_mbytes; 738203831d35Sstevel tbrq->memlen = drmach_iocage_size / 0x100000; 738303831d35Sstevel 738403831d35Sstevel DRMACH_PR("drmach_iocage_mem_get: hi: 0x%x", tbrq->memaddrhi); 738503831d35Sstevel DRMACH_PR("drmach_iocage_mem_get: lo: 0x%x", tbrq->memaddrlo); 738603831d35Sstevel DRMACH_PR("drmach_iocage_mem_get: size: 0x%x", tbrq->memlen); 738703831d35Sstevel 738803831d35Sstevel return (NULL); 738903831d35Sstevel } 739003831d35Sstevel 739103831d35Sstevel static sbd_error_t * 739203831d35Sstevel drmach_iocage_mem_return(dr_testboard_reply_t *tbr) 739303831d35Sstevel { 739403831d35Sstevel _NOTE(ARGUNUSED(tbr)) 739503831d35Sstevel 739603831d35Sstevel pfn_t basepfn; 739703831d35Sstevel pgcnt_t npages; 739803831d35Sstevel extern int memscrub_add_span(pfn_t, pgcnt_t); 739903831d35Sstevel 740003831d35Sstevel ASSERT(drmach_iocage_paddr != -1); 740103831d35Sstevel 740203831d35Sstevel basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT); 740303831d35Sstevel npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT); 740403831d35Sstevel 740507d06da5SSurya Prakki (void) memscrub_add_span(basepfn, npages); 740603831d35Sstevel 740703831d35Sstevel mutex_enter(&cpu_lock); 740803831d35Sstevel mutex_enter(&drmach_iocage_lock); 740903831d35Sstevel drmach_iocage_mem_scrub(drmach_iocage_size); 741003831d35Sstevel drmach_iocage_is_busy = 0; 741103831d35Sstevel cv_signal(&drmach_iocage_cv); 741203831d35Sstevel mutex_exit(&drmach_iocage_lock); 741303831d35Sstevel mutex_exit(&cpu_lock); 741403831d35Sstevel 741503831d35Sstevel return (NULL); 741603831d35Sstevel } 741703831d35Sstevel 741803831d35Sstevel static int 741903831d35Sstevel drmach_cpu_intr_disable(cpu_t *cp) 742003831d35Sstevel { 742103831d35Sstevel if (cpu_intr_disable(cp) != 0) 742203831d35Sstevel return (-1); 742303831d35Sstevel return (0); 742403831d35Sstevel } 742503831d35Sstevel 742603831d35Sstevel static int 742703831d35Sstevel drmach_iocage_cpu_acquire(drmach_device_t *dp, cpu_flag_t *oflags) 742803831d35Sstevel { 742903831d35Sstevel struct cpu *cp; 743003831d35Sstevel processorid_t cpuid; 743103831d35Sstevel static char *fn = "drmach_iocage_cpu_acquire"; 743203831d35Sstevel sbd_error_t *err; 743303831d35Sstevel int impl; 743403831d35Sstevel 743503831d35Sstevel ASSERT(DRMACH_IS_CPU_ID(dp)); 743603831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 743703831d35Sstevel 743803831d35Sstevel cpuid = ((drmach_cpu_t *)dp)->cpuid; 743903831d35Sstevel 744003831d35Sstevel DRMACH_PR("%s: attempting to acquire CPU id %d", fn, cpuid); 744103831d35Sstevel 744203831d35Sstevel if (dp->busy) 744303831d35Sstevel return (-1); 744403831d35Sstevel 744503831d35Sstevel if ((cp = cpu_get(cpuid)) == NULL) { 744603831d35Sstevel DRMACH_PR("%s: cpu_get(%d) returned NULL", fn, cpuid); 744703831d35Sstevel return (-1); 744803831d35Sstevel } 744903831d35Sstevel 745003831d35Sstevel if (!CPU_ACTIVE(cp)) { 745103831d35Sstevel DRMACH_PR("%s: skipping offlined CPU id %d", fn, cpuid); 745203831d35Sstevel return (-1); 745303831d35Sstevel } 745403831d35Sstevel 745503831d35Sstevel /* 745603831d35Sstevel * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0) 745703831d35Sstevel * can fail to receive an XIR. To workaround this issue until a hardware 745803831d35Sstevel * fix is implemented, we will exclude the selection of these CPUs. 745903831d35Sstevel * 746003831d35Sstevel * Once a fix is implemented in hardware, this code should be updated 746103831d35Sstevel * to allow Jaguar CPUs that have the fix to be used. However, support 746203831d35Sstevel * must be retained to skip revisions that do not have this fix. 746303831d35Sstevel */ 746403831d35Sstevel 746503831d35Sstevel err = drmach_cpu_get_impl(dp, &impl); 746603831d35Sstevel if (err) { 746703831d35Sstevel DRMACH_PR("%s: error getting impl. of CPU id %d", fn, cpuid); 746803831d35Sstevel sbd_err_clear(&err); 746903831d35Sstevel return (-1); 747003831d35Sstevel } 747103831d35Sstevel 747203831d35Sstevel if (IS_JAGUAR(impl) && (STARCAT_CPUID_TO_LPORT(cpuid) == 0) && 747303831d35Sstevel drmach_iocage_exclude_jaguar_port_zero) { 747403831d35Sstevel DRMACH_PR("%s: excluding CPU id %d: port 0 on jaguar", 747503831d35Sstevel fn, cpuid); 747603831d35Sstevel return (-1); 747703831d35Sstevel } 747803831d35Sstevel 747903831d35Sstevel ASSERT(oflags); 748003831d35Sstevel *oflags = cp->cpu_flags; 748103831d35Sstevel 748203831d35Sstevel if (cpu_offline(cp, 0)) { 748303831d35Sstevel DRMACH_PR("%s: cpu_offline failed for CPU id %d", fn, cpuid); 748403831d35Sstevel return (-1); 748503831d35Sstevel } 748603831d35Sstevel 748703831d35Sstevel if (cpu_poweroff(cp)) { 748803831d35Sstevel DRMACH_PR("%s: cpu_poweroff failed for CPU id %d", fn, cpuid); 748903831d35Sstevel if (cpu_online(cp)) { 749003831d35Sstevel cmn_err(CE_WARN, "failed to online CPU id %d " 749103831d35Sstevel "during I/O cage test selection", cpuid); 749203831d35Sstevel } 749303831d35Sstevel if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) && 749403831d35Sstevel drmach_cpu_intr_disable(cp) != 0) { 749503831d35Sstevel cmn_err(CE_WARN, "failed to restore CPU id %d " 749603831d35Sstevel "no-intr during I/O cage test selection", cpuid); 749703831d35Sstevel } 749803831d35Sstevel return (-1); 749903831d35Sstevel } 750003831d35Sstevel 750103831d35Sstevel if (cpu_unconfigure(cpuid)) { 750203831d35Sstevel DRMACH_PR("%s: cpu_unconfigure failed for CPU id %d", fn, 750303831d35Sstevel cpuid); 750403831d35Sstevel (void) cpu_configure(cpuid); 750503831d35Sstevel if ((cp = cpu_get(cpuid)) == NULL) { 750603831d35Sstevel cmn_err(CE_WARN, "failed to reconfigure CPU id %d " 750703831d35Sstevel "during I/O cage test selection", cpuid); 750803831d35Sstevel dp->busy = 1; 750903831d35Sstevel return (-1); 751003831d35Sstevel } 751103831d35Sstevel if (cpu_poweron(cp) || cpu_online(cp)) { 751203831d35Sstevel cmn_err(CE_WARN, "failed to %s CPU id %d " 751303831d35Sstevel "during I/O cage test selection", 751403831d35Sstevel cpu_is_poweredoff(cp) ? 751503831d35Sstevel "poweron" : "online", cpuid); 751603831d35Sstevel } 751703831d35Sstevel if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) && 751803831d35Sstevel drmach_cpu_intr_disable(cp) != 0) { 751903831d35Sstevel cmn_err(CE_WARN, "failed to restore CPU id %d " 752003831d35Sstevel "no-intr during I/O cage test selection", cpuid); 752103831d35Sstevel } 752203831d35Sstevel return (-1); 752303831d35Sstevel } 752403831d35Sstevel 752503831d35Sstevel dp->busy = 1; 752603831d35Sstevel 752703831d35Sstevel DRMACH_PR("%s: acquired CPU id %d", fn, cpuid); 752803831d35Sstevel 752903831d35Sstevel return (0); 753003831d35Sstevel } 753103831d35Sstevel 753203831d35Sstevel /* 753303831d35Sstevel * Attempt to acquire all the CPU devices passed in. It is 753403831d35Sstevel * assumed that all the devices in the list are the cores of 753503831d35Sstevel * a single CMP device. Non CMP devices can be handled as a 753603831d35Sstevel * single core CMP by passing in a one element list. 753703831d35Sstevel * 753803831d35Sstevel * Success is only returned if *all* the devices in the list 753903831d35Sstevel * can be acquired. In the failure case, none of the devices 754003831d35Sstevel * in the list will be held as acquired. 754103831d35Sstevel */ 754203831d35Sstevel static int 754303831d35Sstevel drmach_iocage_cmp_acquire(drmach_device_t **dpp, cpu_flag_t *oflags) 754403831d35Sstevel { 754503831d35Sstevel int curr; 754603831d35Sstevel int i; 754703831d35Sstevel int rv = 0; 754803831d35Sstevel 754903831d35Sstevel ASSERT((dpp != NULL) && (*dpp != NULL)); 755003831d35Sstevel 755103831d35Sstevel /* 755203831d35Sstevel * Walk the list of CPU devices (cores of a CMP) 755303831d35Sstevel * and attempt to acquire them. Bail out if an 755403831d35Sstevel * error is encountered. 755503831d35Sstevel */ 755625cf1a30Sjl139090 for (curr = 0; curr < MAX_CORES_PER_CMP; curr++) { 755703831d35Sstevel 755803831d35Sstevel /* check for the end of the list */ 755903831d35Sstevel if (dpp[curr] == NULL) { 756003831d35Sstevel break; 756103831d35Sstevel } 756203831d35Sstevel 756303831d35Sstevel ASSERT(DRMACH_IS_CPU_ID(dpp[curr])); 756403831d35Sstevel ASSERT(dpp[curr]->portid == (*dpp)->portid); 756503831d35Sstevel 756603831d35Sstevel rv = drmach_iocage_cpu_acquire(dpp[curr], &oflags[curr]); 756703831d35Sstevel if (rv != 0) { 756803831d35Sstevel break; 756903831d35Sstevel } 757003831d35Sstevel } 757103831d35Sstevel 757203831d35Sstevel /* 757303831d35Sstevel * Check for an error. 757403831d35Sstevel */ 757503831d35Sstevel if (rv != 0) { 757603831d35Sstevel /* 757703831d35Sstevel * Make a best effort attempt to return any cores 757803831d35Sstevel * that were already acquired before the error was 757903831d35Sstevel * encountered. 758003831d35Sstevel */ 758103831d35Sstevel for (i = 0; i < curr; i++) { 758203831d35Sstevel (void) drmach_iocage_cpu_return(dpp[i], oflags[i]); 758303831d35Sstevel } 758403831d35Sstevel } 758503831d35Sstevel 758603831d35Sstevel return (rv); 758703831d35Sstevel } 758803831d35Sstevel 758903831d35Sstevel static int 759003831d35Sstevel drmach_iocage_cpu_return(drmach_device_t *dp, cpu_flag_t oflags) 759103831d35Sstevel { 759203831d35Sstevel processorid_t cpuid; 759303831d35Sstevel struct cpu *cp; 759403831d35Sstevel int rv = 0; 759503831d35Sstevel static char *fn = "drmach_iocage_cpu_return"; 759603831d35Sstevel 759703831d35Sstevel ASSERT(DRMACH_IS_CPU_ID(dp)); 759803831d35Sstevel ASSERT(MUTEX_HELD(&cpu_lock)); 759903831d35Sstevel 760003831d35Sstevel cpuid = ((drmach_cpu_t *)dp)->cpuid; 760103831d35Sstevel 760203831d35Sstevel DRMACH_PR("%s: attempting to return CPU id: %d", fn, cpuid); 760303831d35Sstevel 760403831d35Sstevel if (cpu_configure(cpuid)) { 760503831d35Sstevel cmn_err(CE_WARN, "failed to reconfigure CPU id %d " 760603831d35Sstevel "after I/O cage test", cpuid); 760703831d35Sstevel /* 760803831d35Sstevel * The component was never set to unconfigured during the IO 760903831d35Sstevel * cage test, so we need to leave marked as busy to prevent 761003831d35Sstevel * further DR operations involving this component. 761103831d35Sstevel */ 761203831d35Sstevel return (-1); 761303831d35Sstevel } 761403831d35Sstevel 761503831d35Sstevel if ((cp = cpu_get(cpuid)) == NULL) { 761603831d35Sstevel cmn_err(CE_WARN, "cpu_get failed on CPU id %d after " 761703831d35Sstevel "I/O cage test", cpuid); 761803831d35Sstevel dp->busy = 0; 761903831d35Sstevel return (-1); 762003831d35Sstevel } 762103831d35Sstevel 762203831d35Sstevel if (cpu_poweron(cp) || cpu_online(cp)) { 762303831d35Sstevel cmn_err(CE_WARN, "failed to %s CPU id %d after I/O " 762403831d35Sstevel "cage test", cpu_is_poweredoff(cp) ? 762503831d35Sstevel "poweron" : "online", cpuid); 762603831d35Sstevel rv = -1; 762703831d35Sstevel } 762803831d35Sstevel 762903831d35Sstevel /* 763003831d35Sstevel * drmach_iocage_cpu_acquire will accept cpus in state P_ONLINE or 763103831d35Sstevel * P_NOINTR. Need to return to previous user-visible state. 763203831d35Sstevel */ 763303831d35Sstevel if (CPU_ACTIVE(cp) && cpu_flagged_nointr(oflags) && 763403831d35Sstevel drmach_cpu_intr_disable(cp) != 0) { 763503831d35Sstevel cmn_err(CE_WARN, "failed to restore CPU id %d " 763603831d35Sstevel "no-intr after I/O cage test", cpuid); 763703831d35Sstevel rv = -1; 763803831d35Sstevel } 763903831d35Sstevel 764003831d35Sstevel dp->busy = 0; 764103831d35Sstevel 764203831d35Sstevel DRMACH_PR("%s: returned CPU id: %d", fn, cpuid); 764303831d35Sstevel 764403831d35Sstevel return (rv); 764503831d35Sstevel } 764603831d35Sstevel 764703831d35Sstevel static sbd_error_t * 764803831d35Sstevel drmach_iocage_cpu_get(dr_testboard_req_t *tbrq, drmach_device_t **dpp, 764903831d35Sstevel cpu_flag_t *oflags) 765003831d35Sstevel { 765103831d35Sstevel drmach_board_t *bp; 765203831d35Sstevel int b_rv; 765303831d35Sstevel int b_idx; 765403831d35Sstevel drmachid_t b_id; 765503831d35Sstevel int found; 765603831d35Sstevel 765703831d35Sstevel mutex_enter(&cpu_lock); 765803831d35Sstevel 765903831d35Sstevel ASSERT(drmach_boards != NULL); 766003831d35Sstevel 766103831d35Sstevel found = 0; 766203831d35Sstevel 766303831d35Sstevel /* 766403831d35Sstevel * Walk the board list. 766503831d35Sstevel */ 766603831d35Sstevel b_rv = drmach_array_first(drmach_boards, &b_idx, &b_id); 766703831d35Sstevel 766803831d35Sstevel while (b_rv == 0) { 766903831d35Sstevel 767003831d35Sstevel int d_rv; 767103831d35Sstevel int d_idx; 767203831d35Sstevel drmachid_t d_id; 767303831d35Sstevel 767403831d35Sstevel bp = b_id; 767503831d35Sstevel 767603831d35Sstevel if (bp->connected == 0 || bp->devices == NULL) { 767703831d35Sstevel b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id); 767803831d35Sstevel continue; 767903831d35Sstevel } 768003831d35Sstevel 768103831d35Sstevel /* An AXQ restriction disqualifies MCPU's as candidates. */ 768203831d35Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 1) { 768303831d35Sstevel b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id); 768403831d35Sstevel continue; 768503831d35Sstevel } 768603831d35Sstevel 768703831d35Sstevel /* 768803831d35Sstevel * Walk the device list of this board. 768903831d35Sstevel */ 769003831d35Sstevel d_rv = drmach_array_first(bp->devices, &d_idx, &d_id); 769103831d35Sstevel 769203831d35Sstevel while (d_rv == 0) { 769303831d35Sstevel 769403831d35Sstevel drmach_device_t *ndp; 769503831d35Sstevel 769603831d35Sstevel /* only interested in CPU devices */ 769703831d35Sstevel if (!DRMACH_IS_CPU_ID(d_id)) { 769803831d35Sstevel d_rv = drmach_array_next(bp->devices, &d_idx, 769903831d35Sstevel &d_id); 770003831d35Sstevel continue; 770103831d35Sstevel } 770203831d35Sstevel 770303831d35Sstevel /* 770403831d35Sstevel * The following code assumes two properties 770503831d35Sstevel * of a CMP device: 770603831d35Sstevel * 770703831d35Sstevel * 1. All cores of a CMP are grouped together 770803831d35Sstevel * in the device list. 770903831d35Sstevel * 771003831d35Sstevel * 2. There will only be a maximum of two cores 771103831d35Sstevel * present in the CMP. 771203831d35Sstevel * 771303831d35Sstevel * If either of these two properties change, 771403831d35Sstevel * this code will have to be revisited. 771503831d35Sstevel */ 771603831d35Sstevel 771703831d35Sstevel dpp[0] = d_id; 771803831d35Sstevel dpp[1] = NULL; 771903831d35Sstevel 772003831d35Sstevel /* 772103831d35Sstevel * Get the next device. It may or may not be used. 772203831d35Sstevel */ 772303831d35Sstevel d_rv = drmach_array_next(bp->devices, &d_idx, &d_id); 772403831d35Sstevel ndp = d_id; 772503831d35Sstevel 772603831d35Sstevel if ((d_rv == 0) && DRMACH_IS_CPU_ID(d_id)) { 772703831d35Sstevel /* 772803831d35Sstevel * The second device is only interesting for 772903831d35Sstevel * this pass if it has the same portid as the 773003831d35Sstevel * first device. This implies that both are 773103831d35Sstevel * cores of the same CMP. 773203831d35Sstevel */ 773303831d35Sstevel if (dpp[0]->portid == ndp->portid) { 773403831d35Sstevel dpp[1] = d_id; 773503831d35Sstevel } 773603831d35Sstevel } 773703831d35Sstevel 773803831d35Sstevel /* 773903831d35Sstevel * Attempt to acquire all cores of the CMP. 774003831d35Sstevel */ 774103831d35Sstevel if (drmach_iocage_cmp_acquire(dpp, oflags) == 0) { 774203831d35Sstevel found = 1; 774303831d35Sstevel break; 774403831d35Sstevel } 774503831d35Sstevel 774603831d35Sstevel /* 774703831d35Sstevel * Check if the search for the second core was 774803831d35Sstevel * successful. If not, the next iteration should 774903831d35Sstevel * use that device. 775003831d35Sstevel */ 775103831d35Sstevel if (dpp[1] == NULL) { 775203831d35Sstevel continue; 775303831d35Sstevel } 775403831d35Sstevel 775503831d35Sstevel d_rv = drmach_array_next(bp->devices, &d_idx, &d_id); 775603831d35Sstevel } 775703831d35Sstevel 775803831d35Sstevel if (found) 775903831d35Sstevel break; 776003831d35Sstevel 776103831d35Sstevel b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id); 776203831d35Sstevel } 776303831d35Sstevel 776403831d35Sstevel mutex_exit(&cpu_lock); 776503831d35Sstevel 776603831d35Sstevel if (!found) { 776703831d35Sstevel return (drerr_new(1, ESTC_IOCAGE_NO_CPU_AVAIL, NULL)); 776803831d35Sstevel } 776903831d35Sstevel 777003831d35Sstevel tbrq->cpu_portid = (*dpp)->portid; 777103831d35Sstevel 777203831d35Sstevel return (NULL); 777303831d35Sstevel } 777403831d35Sstevel 777503831d35Sstevel /* 777603831d35Sstevel * Setup an iocage by acquiring a cpu and memory. 777703831d35Sstevel */ 777803831d35Sstevel static sbd_error_t * 777903831d35Sstevel drmach_iocage_setup(dr_testboard_req_t *tbrq, drmach_device_t **dpp, 778003831d35Sstevel cpu_flag_t *oflags) 778103831d35Sstevel { 778203831d35Sstevel sbd_error_t *err; 778303831d35Sstevel 778403831d35Sstevel err = drmach_iocage_cpu_get(tbrq, dpp, oflags); 778503831d35Sstevel if (!err) { 778603831d35Sstevel mutex_enter(&drmach_iocage_lock); 778703831d35Sstevel while (drmach_iocage_is_busy) 778803831d35Sstevel cv_wait(&drmach_iocage_cv, &drmach_iocage_lock); 778903831d35Sstevel drmach_iocage_is_busy = 1; 779003831d35Sstevel mutex_exit(&drmach_iocage_lock); 779103831d35Sstevel err = drmach_iocage_mem_get(tbrq); 779203831d35Sstevel if (err) { 779303831d35Sstevel mutex_enter(&drmach_iocage_lock); 779403831d35Sstevel drmach_iocage_is_busy = 0; 779503831d35Sstevel cv_signal(&drmach_iocage_cv); 779603831d35Sstevel mutex_exit(&drmach_iocage_lock); 779703831d35Sstevel } 779803831d35Sstevel } 779903831d35Sstevel return (err); 780003831d35Sstevel } 780103831d35Sstevel 780203831d35Sstevel #define DRMACH_SCHIZO_PCI_LEAF_MAX 2 780303831d35Sstevel #define DRMACH_SCHIZO_PCI_SLOT_MAX 8 780403831d35Sstevel #define DRMACH_S1P_SAMPLE_MAX 2 780503831d35Sstevel 780603831d35Sstevel typedef enum { 780703831d35Sstevel DRMACH_POST_SUSPEND = 0, 780803831d35Sstevel DRMACH_PRE_RESUME 780903831d35Sstevel } drmach_sr_iter_t; 781003831d35Sstevel 781103831d35Sstevel typedef struct { 781203831d35Sstevel dev_info_t *dip; 781303831d35Sstevel uint32_t portid; 781403831d35Sstevel uint32_t pcr_sel_save; 781503831d35Sstevel uint32_t pic_l2_io_q[DRMACH_S1P_SAMPLE_MAX]; 781603831d35Sstevel uint64_t reg_basepa; 781703831d35Sstevel } drmach_s1p_axq_t; 781803831d35Sstevel 781903831d35Sstevel typedef struct { 782003831d35Sstevel dev_info_t *dip; 782103831d35Sstevel uint32_t portid; 782203831d35Sstevel uint64_t csr_basepa; 782303831d35Sstevel struct { 782403831d35Sstevel uint64_t slot_intr_state_diag; 782503831d35Sstevel uint64_t obio_intr_state_diag; 782603831d35Sstevel uint_t nmap_regs; 782703831d35Sstevel uint64_t *intr_map_regs; 782803831d35Sstevel } regs[DRMACH_S1P_SAMPLE_MAX]; 782903831d35Sstevel } drmach_s1p_pci_t; 783003831d35Sstevel 783103831d35Sstevel typedef struct { 783203831d35Sstevel uint64_t csr_basepa; 783303831d35Sstevel struct { 783403831d35Sstevel uint64_t csr; 783503831d35Sstevel uint64_t errctrl; 783603831d35Sstevel uint64_t errlog; 783703831d35Sstevel } regs[DRMACH_S1P_SAMPLE_MAX]; 783803831d35Sstevel drmach_s1p_pci_t pci[DRMACH_SCHIZO_PCI_LEAF_MAX]; 783903831d35Sstevel } drmach_s1p_schizo_t; 784003831d35Sstevel 784103831d35Sstevel typedef struct { 784203831d35Sstevel drmach_s1p_axq_t axq; 784303831d35Sstevel drmach_s1p_schizo_t schizo[STARCAT_SLOT1_IO_MAX]; 784403831d35Sstevel } drmach_slot1_pause_t; 784503831d35Sstevel 784603831d35Sstevel /* 784703831d35Sstevel * Table of saved state for paused slot1 devices. 784803831d35Sstevel */ 784903831d35Sstevel static drmach_slot1_pause_t *drmach_slot1_paused[STARCAT_BDSET_MAX]; 785003831d35Sstevel static int drmach_slot1_pause_init = 1; 785103831d35Sstevel 785203831d35Sstevel #ifdef DEBUG 785303831d35Sstevel int drmach_slot1_pause_debug = 1; 785403831d35Sstevel #else 785503831d35Sstevel int drmach_slot1_pause_debug = 0; 785603831d35Sstevel #endif /* DEBUG */ 785703831d35Sstevel 785803831d35Sstevel static int 785903831d35Sstevel drmach_is_slot1_pause_axq(dev_info_t *dip, char *name, int *id, uint64_t *reg) 786003831d35Sstevel { 786103831d35Sstevel int portid, exp, slot, i; 786203831d35Sstevel drmach_reg_t regs[2]; 786303831d35Sstevel int reglen = sizeof (regs); 786403831d35Sstevel 786503831d35Sstevel if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip, 786603831d35Sstevel DDI_PROP_DONTPASS, "portid", -1)) == -1) { 786703831d35Sstevel return (0); 786803831d35Sstevel } 786903831d35Sstevel 787003831d35Sstevel exp = (portid >> 5) & 0x1f; 787103831d35Sstevel slot = portid & 0x1; 787203831d35Sstevel 787303831d35Sstevel if (slot == 0 || strncmp(name, DRMACH_AXQ_NAMEPROP, 787403831d35Sstevel strlen(DRMACH_AXQ_NAMEPROP))) { 787503831d35Sstevel return (0); 787603831d35Sstevel } 787703831d35Sstevel 787803831d35Sstevel mutex_enter(&cpu_lock); 787903831d35Sstevel for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) { 788003831d35Sstevel if (cpu[MAKE_CPUID(exp, slot, i)]) { 788103831d35Sstevel /* maxcat cpu present */ 788203831d35Sstevel mutex_exit(&cpu_lock); 788303831d35Sstevel return (0); 788403831d35Sstevel } 788503831d35Sstevel } 788603831d35Sstevel mutex_exit(&cpu_lock); 788703831d35Sstevel 788803831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 788903831d35Sstevel "reg", (caddr_t)regs, ®len) != DDI_PROP_SUCCESS) { 789003831d35Sstevel DRMACH_PR("drmach_is_slot1_pause_axq: no reg prop for " 789107d06da5SSurya Prakki "axq dip=%p\n", (void *)dip); 789203831d35Sstevel return (0); 789303831d35Sstevel } 789403831d35Sstevel 789503831d35Sstevel ASSERT(id && reg); 789603831d35Sstevel *reg = (uint64_t)regs[0].reg_addr_hi << 32; 789703831d35Sstevel *reg |= (uint64_t)regs[0].reg_addr_lo; 789803831d35Sstevel *id = portid; 789903831d35Sstevel 790003831d35Sstevel return (1); 790103831d35Sstevel } 790203831d35Sstevel 790303831d35Sstevel /* 790403831d35Sstevel * Allocate an entry in the slot1_paused state table. 790503831d35Sstevel */ 790603831d35Sstevel static void 790703831d35Sstevel drmach_slot1_pause_add_axq(dev_info_t *axq_dip, char *axq_name, int axq_portid, 790803831d35Sstevel uint64_t reg, drmach_slot1_pause_t **slot1_paused) 790903831d35Sstevel { 791003831d35Sstevel int axq_exp; 791103831d35Sstevel drmach_slot1_pause_t *slot1; 791203831d35Sstevel 791303831d35Sstevel axq_exp = (axq_portid >> 5) & 0x1f; 791403831d35Sstevel 791503831d35Sstevel ASSERT(axq_portid & 0x1); 791603831d35Sstevel ASSERT(slot1_paused[axq_exp] == NULL); 791703831d35Sstevel ASSERT(strncmp(axq_name, DRMACH_AXQ_NAMEPROP, 791803831d35Sstevel strlen(DRMACH_AXQ_NAMEPROP)) == 0); 791903831d35Sstevel 792003831d35Sstevel slot1 = kmem_zalloc(sizeof (*slot1), KM_SLEEP); 792103831d35Sstevel 792203831d35Sstevel /* 792303831d35Sstevel * XXX This dip should really be held (via ndi_hold_devi()) 792403831d35Sstevel * before saving it in the axq pause structure. However that 792503831d35Sstevel * would prevent DR as the pause data structures persist until 792603831d35Sstevel * the next suspend. drmach code should be modified to free the 792703831d35Sstevel * the slot 1 pause data structures for a boardset when its 792803831d35Sstevel * slot 1 board is DRed out. The dip can then be released via 792903831d35Sstevel * ndi_rele_devi() when the pause data structure is freed 793003831d35Sstevel * allowing DR to proceed. Until this change is made, drmach 793103831d35Sstevel * code should be careful about dereferencing the saved dip 793203831d35Sstevel * as it may no longer exist. 793303831d35Sstevel */ 793403831d35Sstevel slot1->axq.dip = axq_dip; 793503831d35Sstevel slot1->axq.portid = axq_portid; 793603831d35Sstevel slot1->axq.reg_basepa = reg; 793703831d35Sstevel slot1_paused[axq_exp] = slot1; 793803831d35Sstevel } 793903831d35Sstevel 794003831d35Sstevel static void 794103831d35Sstevel drmach_s1p_pci_free(drmach_s1p_pci_t *pci) 794203831d35Sstevel { 794303831d35Sstevel int i; 794403831d35Sstevel 794503831d35Sstevel for (i = 0; i < DRMACH_S1P_SAMPLE_MAX; i++) { 794603831d35Sstevel if (pci->regs[i].intr_map_regs != NULL) { 794703831d35Sstevel ASSERT(pci->regs[i].nmap_regs > 0); 794803831d35Sstevel kmem_free(pci->regs[i].intr_map_regs, 794903831d35Sstevel pci->regs[i].nmap_regs * sizeof (uint64_t)); 795003831d35Sstevel } 795103831d35Sstevel } 795203831d35Sstevel } 795303831d35Sstevel 795403831d35Sstevel static void 795503831d35Sstevel drmach_slot1_pause_free(drmach_slot1_pause_t **slot1_paused) 795603831d35Sstevel { 795703831d35Sstevel int i, j, k; 795803831d35Sstevel drmach_slot1_pause_t *slot1; 795903831d35Sstevel 796003831d35Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) { 796103831d35Sstevel if ((slot1 = slot1_paused[i]) == NULL) 796203831d35Sstevel continue; 796303831d35Sstevel 796403831d35Sstevel for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) 796503831d35Sstevel for (k = 0; k < DRMACH_SCHIZO_PCI_LEAF_MAX; k++) 796603831d35Sstevel drmach_s1p_pci_free(&slot1->schizo[j].pci[k]); 796703831d35Sstevel 796803831d35Sstevel kmem_free(slot1, sizeof (*slot1)); 796903831d35Sstevel slot1_paused[i] = NULL; 797003831d35Sstevel } 797103831d35Sstevel } 797203831d35Sstevel 797303831d35Sstevel /* 797403831d35Sstevel * Tree walk callback routine. If dip represents a Schizo PCI leaf, 797503831d35Sstevel * fill in the appropriate info in the slot1_paused state table. 797603831d35Sstevel */ 797703831d35Sstevel static int 797803831d35Sstevel drmach_find_slot1_io(dev_info_t *dip, void *arg) 797903831d35Sstevel { 798003831d35Sstevel int portid, exp, ioc_unum, leaf_unum; 798103831d35Sstevel char buf[OBP_MAXDRVNAME]; 798203831d35Sstevel int buflen = sizeof (buf); 798303831d35Sstevel drmach_reg_t regs[3]; 798403831d35Sstevel int reglen = sizeof (regs); 798503831d35Sstevel uint32_t leaf_offset; 798603831d35Sstevel uint64_t schizo_csr_pa, pci_csr_pa; 798703831d35Sstevel drmach_s1p_pci_t *pci; 798803831d35Sstevel drmach_slot1_pause_t **slot1_paused = (drmach_slot1_pause_t **)arg; 798903831d35Sstevel 799003831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 799103831d35Sstevel "name", (caddr_t)buf, &buflen) != DDI_PROP_SUCCESS || 799203831d35Sstevel strncmp(buf, DRMACH_PCI_NAMEPROP, strlen(DRMACH_PCI_NAMEPROP))) { 799303831d35Sstevel return (DDI_WALK_CONTINUE); 799403831d35Sstevel } 799503831d35Sstevel 799603831d35Sstevel if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip, 799703831d35Sstevel DDI_PROP_DONTPASS, "portid", -1)) == -1) { 799803831d35Sstevel return (DDI_WALK_CONTINUE); 799903831d35Sstevel } 800003831d35Sstevel 800103831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 800203831d35Sstevel "reg", (caddr_t)regs, ®len) != DDI_PROP_SUCCESS) { 800303831d35Sstevel DRMACH_PR("drmach_find_slot1_io: no reg prop for pci " 800407d06da5SSurya Prakki "dip=%p\n", (void *)dip); 800503831d35Sstevel return (DDI_WALK_CONTINUE); 800603831d35Sstevel } 800703831d35Sstevel 800803831d35Sstevel exp = portid >> 5; 800903831d35Sstevel ioc_unum = portid & 0x1; 801003831d35Sstevel leaf_offset = regs[0].reg_addr_lo & 0x7fffff; 801103831d35Sstevel pci_csr_pa = (uint64_t)regs[0].reg_addr_hi << 32; 801203831d35Sstevel pci_csr_pa |= (uint64_t)regs[0].reg_addr_lo; 801303831d35Sstevel schizo_csr_pa = (uint64_t)regs[1].reg_addr_hi << 32; 801403831d35Sstevel schizo_csr_pa |= (uint64_t)regs[1].reg_addr_lo; 801503831d35Sstevel 801603831d35Sstevel ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX); 801703831d35Sstevel ASSERT(slot1_paused[exp] != NULL); 801803831d35Sstevel ASSERT(leaf_offset == 0x600000 || leaf_offset == 0x700000); 801903831d35Sstevel ASSERT(slot1_paused[exp]->schizo[ioc_unum].csr_basepa == 0x0UL || 802003831d35Sstevel slot1_paused[exp]->schizo[ioc_unum].csr_basepa == schizo_csr_pa); 802103831d35Sstevel 802203831d35Sstevel leaf_unum = (leaf_offset == 0x600000) ? 0 : 1; 802303831d35Sstevel slot1_paused[exp]->schizo[ioc_unum].csr_basepa = schizo_csr_pa; 802403831d35Sstevel pci = &slot1_paused[exp]->schizo[ioc_unum].pci[leaf_unum]; 802503831d35Sstevel 802603831d35Sstevel /* 802703831d35Sstevel * XXX This dip should really be held (via ndi_hold_devi()) 802803831d35Sstevel * before saving it in the pci pause structure. However that 802903831d35Sstevel * would prevent DR as the pause data structures persist until 803003831d35Sstevel * the next suspend. drmach code should be modified to free the 803103831d35Sstevel * the slot 1 pause data structures for a boardset when its 803203831d35Sstevel * slot 1 board is DRed out. The dip can then be released via 803303831d35Sstevel * ndi_rele_devi() when the pause data structure is freed 803403831d35Sstevel * allowing DR to proceed. Until this change is made, drmach 803503831d35Sstevel * code should be careful about dereferencing the saved dip as 803603831d35Sstevel * it may no longer exist. 803703831d35Sstevel */ 803803831d35Sstevel pci->dip = dip; 803903831d35Sstevel pci->portid = portid; 804003831d35Sstevel pci->csr_basepa = pci_csr_pa; 804103831d35Sstevel 804203831d35Sstevel DRMACH_PR("drmach_find_slot1_io: name=%s, portid=0x%x, dip=%p\n", 804307d06da5SSurya Prakki buf, portid, (void *)dip); 804403831d35Sstevel 804503831d35Sstevel return (DDI_WALK_PRUNECHILD); 804603831d35Sstevel } 804703831d35Sstevel 804803831d35Sstevel static void 804903831d35Sstevel drmach_slot1_pause_add_io(drmach_slot1_pause_t **slot1_paused) 805003831d35Sstevel { 805103831d35Sstevel /* 805203831d35Sstevel * Root node doesn't have to be held 805303831d35Sstevel */ 805403831d35Sstevel ddi_walk_devs(ddi_root_node(), drmach_find_slot1_io, 805503831d35Sstevel (void *)slot1_paused); 805603831d35Sstevel } 805703831d35Sstevel 805803831d35Sstevel /* 805903831d35Sstevel * Save the interrupt mapping registers for each non-idle interrupt 806003831d35Sstevel * represented by the bit pairs in the saved interrupt state 806103831d35Sstevel * diagnostic registers for this PCI leaf. 806203831d35Sstevel */ 806303831d35Sstevel static void 806403831d35Sstevel drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t *pci, drmach_sr_iter_t iter) 806503831d35Sstevel { 806603831d35Sstevel int i, cnt, ino; 806703831d35Sstevel uint64_t reg; 806803831d35Sstevel char *dname; 806903831d35Sstevel uchar_t Xmits; 807003831d35Sstevel 807103831d35Sstevel dname = ddi_binding_name(pci->dip); 807203831d35Sstevel Xmits = (strcmp(dname, XMITS_BINDING_NAME) == 0) ? 1 : 0; 807303831d35Sstevel 807403831d35Sstevel /* 807503831d35Sstevel * 1st pass allocates, 2nd pass populates. 807603831d35Sstevel */ 807703831d35Sstevel for (i = 0; i < 2; i++) { 807803831d35Sstevel cnt = ino = 0; 807903831d35Sstevel 808003831d35Sstevel /* 808103831d35Sstevel * PCI slot interrupts 808203831d35Sstevel */ 808303831d35Sstevel reg = pci->regs[iter].slot_intr_state_diag; 808403831d35Sstevel while (reg) { 808503831d35Sstevel /* 808603831d35Sstevel * Xmits Interrupt Number Offset(ino) Assignments 808703831d35Sstevel * 00-17 PCI Slot Interrupts 808803831d35Sstevel * 18-1f Not Used 808903831d35Sstevel */ 809003831d35Sstevel if ((Xmits) && (ino > 0x17)) 809103831d35Sstevel break; 809203831d35Sstevel if ((reg & COMMON_CLEAR_INTR_REG_MASK) != 809303831d35Sstevel COMMON_CLEAR_INTR_REG_IDLE) { 809403831d35Sstevel if (i) { 809503831d35Sstevel pci->regs[iter].intr_map_regs[cnt] = 809603831d35Sstevel lddphysio(pci->csr_basepa + 809703831d35Sstevel SCHIZO_IB_INTR_MAP_REG_OFFSET + 809803831d35Sstevel ino * sizeof (reg)); 809903831d35Sstevel } 810003831d35Sstevel ++cnt; 810103831d35Sstevel } 810203831d35Sstevel ++ino; 810303831d35Sstevel reg >>= 2; 810403831d35Sstevel } 810503831d35Sstevel 810603831d35Sstevel /* 810703831d35Sstevel * Xmits Interrupt Number Offset(ino) Assignments 810803831d35Sstevel * 20-2f Not Used 810903831d35Sstevel * 30-37 Internal interrupts 811003831d35Sstevel * 38-3e Not Used 811103831d35Sstevel */ 811203831d35Sstevel ino = (Xmits) ? 0x30 : 0x20; 811303831d35Sstevel 811403831d35Sstevel /* 811503831d35Sstevel * OBIO and internal schizo interrupts 811603831d35Sstevel * Each PCI leaf has a set of mapping registers for all 811703831d35Sstevel * possible interrupt sources except the NewLink interrupts. 811803831d35Sstevel */ 811903831d35Sstevel reg = pci->regs[iter].obio_intr_state_diag; 812003831d35Sstevel while (reg && ino <= 0x38) { 812103831d35Sstevel if ((reg & COMMON_CLEAR_INTR_REG_MASK) != 812203831d35Sstevel COMMON_CLEAR_INTR_REG_IDLE) { 812303831d35Sstevel if (i) { 812403831d35Sstevel pci->regs[iter].intr_map_regs[cnt] = 812503831d35Sstevel lddphysio(pci->csr_basepa + 812603831d35Sstevel SCHIZO_IB_INTR_MAP_REG_OFFSET + 812703831d35Sstevel ino * sizeof (reg)); 812803831d35Sstevel } 812903831d35Sstevel ++cnt; 813003831d35Sstevel } 813103831d35Sstevel ++ino; 813203831d35Sstevel reg >>= 2; 813303831d35Sstevel } 813403831d35Sstevel 813503831d35Sstevel if (!i) { 813603831d35Sstevel pci->regs[iter].nmap_regs = cnt; 813703831d35Sstevel pci->regs[iter].intr_map_regs = 813803831d35Sstevel kmem_zalloc(cnt * sizeof (reg), KM_SLEEP); 813903831d35Sstevel } 814003831d35Sstevel } 814103831d35Sstevel } 814203831d35Sstevel 814303831d35Sstevel static void 814403831d35Sstevel drmach_s1p_axq_update(drmach_s1p_axq_t *axq, drmach_sr_iter_t iter) 814503831d35Sstevel { 814603831d35Sstevel uint32_t reg; 814703831d35Sstevel 814803831d35Sstevel if (axq->reg_basepa == 0x0UL) 814903831d35Sstevel return; 815003831d35Sstevel 815103831d35Sstevel if (iter == DRMACH_POST_SUSPEND) { 815203831d35Sstevel axq->pcr_sel_save = ldphysio(axq->reg_basepa + 815303831d35Sstevel AXQ_SLOT1_PERFCNT_SEL); 815403831d35Sstevel /* 815503831d35Sstevel * Select l2_io_queue counter by writing L2_IO_Q mux 815603831d35Sstevel * input to bits 0-6 of perf cntr select reg. 815703831d35Sstevel */ 815803831d35Sstevel reg = axq->pcr_sel_save; 815903831d35Sstevel reg &= ~AXQ_PIC_CLEAR_MASK; 816003831d35Sstevel reg |= L2_IO_Q; 816103831d35Sstevel 816203831d35Sstevel stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL, reg); 816303831d35Sstevel } 816403831d35Sstevel 816503831d35Sstevel axq->pic_l2_io_q[iter] = ldphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT0); 816603831d35Sstevel 816703831d35Sstevel if (iter == DRMACH_PRE_RESUME) { 816803831d35Sstevel stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL, 816903831d35Sstevel axq->pcr_sel_save); 817003831d35Sstevel } 817103831d35Sstevel 817203831d35Sstevel DRMACH_PR("drmach_s1p_axq_update: axq #%d pic_l2_io_q[%d]=%d\n", 817303831d35Sstevel ddi_get_instance(axq->dip), iter, axq->pic_l2_io_q[iter]); 817403831d35Sstevel } 817503831d35Sstevel 817603831d35Sstevel static void 817703831d35Sstevel drmach_s1p_schizo_update(drmach_s1p_schizo_t *schizo, drmach_sr_iter_t iter) 817803831d35Sstevel { 817903831d35Sstevel int i; 818003831d35Sstevel drmach_s1p_pci_t *pci; 818103831d35Sstevel 818203831d35Sstevel if (schizo->csr_basepa == 0x0UL) 818303831d35Sstevel return; 818403831d35Sstevel 818503831d35Sstevel schizo->regs[iter].csr = 818603831d35Sstevel lddphysio(schizo->csr_basepa + SCHIZO_CB_CSR_OFFSET); 818703831d35Sstevel schizo->regs[iter].errctrl = 818803831d35Sstevel lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRCTRL_OFFSET); 818903831d35Sstevel schizo->regs[iter].errlog = 819003831d35Sstevel lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRLOG_OFFSET); 819103831d35Sstevel 819203831d35Sstevel for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) { 819303831d35Sstevel pci = &schizo->pci[i]; 819403831d35Sstevel if (pci->dip != NULL && pci->csr_basepa != 0x0UL) { 819503831d35Sstevel pci->regs[iter].slot_intr_state_diag = 819603831d35Sstevel lddphysio(pci->csr_basepa + 819703831d35Sstevel COMMON_IB_SLOT_INTR_STATE_DIAG_REG); 819803831d35Sstevel 819903831d35Sstevel pci->regs[iter].obio_intr_state_diag = 820003831d35Sstevel lddphysio(pci->csr_basepa + 820103831d35Sstevel COMMON_IB_OBIO_INTR_STATE_DIAG_REG); 820203831d35Sstevel 820303831d35Sstevel drmach_s1p_intr_map_reg_save(pci, iter); 820403831d35Sstevel } 820503831d35Sstevel } 820603831d35Sstevel } 820703831d35Sstevel 820803831d35Sstevel /* 820903831d35Sstevel * Called post-suspend and pre-resume to snapshot the suspend state 821003831d35Sstevel * of slot1 AXQs and Schizos. 821103831d35Sstevel */ 821203831d35Sstevel static void 821303831d35Sstevel drmach_slot1_pause_update(drmach_slot1_pause_t **slot1_paused, 821403831d35Sstevel drmach_sr_iter_t iter) 821503831d35Sstevel { 821603831d35Sstevel int i, j; 821703831d35Sstevel drmach_slot1_pause_t *slot1; 821803831d35Sstevel 821903831d35Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) { 822003831d35Sstevel if ((slot1 = slot1_paused[i]) == NULL) 822103831d35Sstevel continue; 822203831d35Sstevel 822303831d35Sstevel drmach_s1p_axq_update(&slot1->axq, iter); 822403831d35Sstevel for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) 822503831d35Sstevel drmach_s1p_schizo_update(&slot1->schizo[j], iter); 822603831d35Sstevel } 822703831d35Sstevel } 822803831d35Sstevel 822903831d35Sstevel /* 823003831d35Sstevel * Starcat hPCI Schizo devices. 823103831d35Sstevel * 823203831d35Sstevel * The name field is overloaded. NULL means the slot (interrupt concentrator 823303831d35Sstevel * bus) is not used. intr_mask is a bit mask representing the 4 possible 823403831d35Sstevel * interrupts per slot, on if valid (rio does not use interrupt lines 0, 1). 823503831d35Sstevel */ 823603831d35Sstevel static struct { 823703831d35Sstevel char *name; 823803831d35Sstevel uint8_t intr_mask; 823903831d35Sstevel } drmach_schz_slot_intr[][DRMACH_SCHIZO_PCI_LEAF_MAX] = { 824003831d35Sstevel /* Schizo 0 */ /* Schizo 1 */ 824103831d35Sstevel {{"C3V0", 0xf}, {"C3V1", 0xf}}, /* slot 0 */ 824203831d35Sstevel {{"C5V0", 0xf}, {"C5V1", 0xf}}, /* slot 1 */ 824303831d35Sstevel {{"rio", 0xc}, {NULL, 0x0}}, /* slot 2 */ 824403831d35Sstevel {{NULL, 0x0}, {NULL, 0x0}}, /* slot 3 */ 824503831d35Sstevel {{"sbbc", 0xf}, {NULL, 0x0}}, /* slot 4 */ 824603831d35Sstevel {{NULL, 0x0}, {NULL, 0x0}}, /* slot 5 */ 824703831d35Sstevel {{NULL, 0x0}, {NULL, 0x0}}, /* slot 6 */ 824803831d35Sstevel {{NULL, 0x0}, {NULL, 0x0}} /* slot 7 */ 824903831d35Sstevel }; 825003831d35Sstevel 825103831d35Sstevel /* 825203831d35Sstevel * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.4.4 825303831d35Sstevel * "Interrupt Registers", Table 22-69, page 306. 825403831d35Sstevel */ 825503831d35Sstevel static char * 825603831d35Sstevel drmach_schz_internal_ino2str(int ino) 825703831d35Sstevel { 825803831d35Sstevel int intr; 825903831d35Sstevel 826003831d35Sstevel ASSERT(ino >= 0x30 && ino <= 0x37); 826103831d35Sstevel 826203831d35Sstevel intr = ino & 0x7; 826303831d35Sstevel switch (intr) { 826403831d35Sstevel case (0x0): return ("Uncorrectable ECC error"); 826503831d35Sstevel case (0x1): return ("Correctable ECC error"); 826603831d35Sstevel case (0x2): return ("PCI Bus A Error"); 826703831d35Sstevel case (0x3): return ("PCI Bus B Error"); 826803831d35Sstevel case (0x4): return ("Safari Bus Error"); 826903831d35Sstevel default: return ("Reserved"); 827003831d35Sstevel } 827103831d35Sstevel } 827203831d35Sstevel 827303831d35Sstevel #define DRMACH_INTR_MASK_SHIFT(ino) ((ino) << 1) 827403831d35Sstevel 827503831d35Sstevel static void 827603831d35Sstevel drmach_s1p_decode_slot_intr(int exp, int unum, drmach_s1p_pci_t *pci, 827703831d35Sstevel int ino, drmach_sr_iter_t iter) 827803831d35Sstevel { 827903831d35Sstevel uint8_t intr_mask; 828003831d35Sstevel char *slot_devname; 828103831d35Sstevel char namebuf[OBP_MAXDRVNAME]; 828203831d35Sstevel int slot, intr_line, slot_valid, intr_valid; 828303831d35Sstevel 828403831d35Sstevel ASSERT(ino >= 0 && ino <= 0x1f); 828503831d35Sstevel ASSERT((pci->regs[iter].slot_intr_state_diag & 828603831d35Sstevel (COMMON_CLEAR_INTR_REG_MASK << DRMACH_INTR_MASK_SHIFT(ino))) != 828703831d35Sstevel COMMON_CLEAR_INTR_REG_IDLE); 828803831d35Sstevel 828903831d35Sstevel slot = (ino >> 2) & 0x7; 829003831d35Sstevel intr_line = ino & 0x3; 829103831d35Sstevel 829203831d35Sstevel slot_devname = drmach_schz_slot_intr[slot][unum].name; 829303831d35Sstevel slot_valid = (slot_devname == NULL) ? 0 : 1; 829403831d35Sstevel if (!slot_valid) { 829507d06da5SSurya Prakki (void) snprintf(namebuf, sizeof (namebuf), "slot %d (INVALID)", 829607d06da5SSurya Prakki slot); 829703831d35Sstevel slot_devname = namebuf; 829803831d35Sstevel } 829903831d35Sstevel 830003831d35Sstevel intr_mask = drmach_schz_slot_intr[slot][unum].intr_mask; 830103831d35Sstevel intr_valid = (1 << intr_line) & intr_mask; 830203831d35Sstevel 830303831d35Sstevel prom_printf("IO%d/P%d PCI slot interrupt: ino=0x%x, source device=%s, " 830403831d35Sstevel "interrupt line=%d%s\n", exp, unum, ino, slot_devname, intr_line, 830503831d35Sstevel (slot_valid && !intr_valid) ? " (INVALID)" : ""); 830603831d35Sstevel } 830703831d35Sstevel 830803831d35Sstevel /* 830903831d35Sstevel * Log interrupt source device info for all valid, pending interrupts 831003831d35Sstevel * on each Schizo PCI leaf. Called if Schizo has logged a Safari bus 831103831d35Sstevel * error in the error ctrl reg. 831203831d35Sstevel */ 831303831d35Sstevel static void 831403831d35Sstevel drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t *schizo, int exp, 831503831d35Sstevel int unum, drmach_sr_iter_t iter) 831603831d35Sstevel { 831703831d35Sstevel uint64_t reg; 831803831d35Sstevel int i, n, ino; 831903831d35Sstevel drmach_s1p_pci_t *pci; 832003831d35Sstevel 832103831d35Sstevel ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX); 832203831d35Sstevel ASSERT(unum < STARCAT_SLOT1_IO_MAX); 832303831d35Sstevel 832403831d35Sstevel /* 832503831d35Sstevel * Check the saved interrupt mapping registers. If interrupt is valid, 832603831d35Sstevel * map the ino to the Schizo source device and check that the pci 832703831d35Sstevel * slot and interrupt line are valid. 832803831d35Sstevel */ 832903831d35Sstevel for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) { 833003831d35Sstevel pci = &schizo->pci[i]; 833103831d35Sstevel for (n = 0; n < pci->regs[iter].nmap_regs; n++) { 833203831d35Sstevel reg = pci->regs[iter].intr_map_regs[n]; 833303831d35Sstevel if (reg & COMMON_INTR_MAP_REG_VALID) { 833403831d35Sstevel ino = reg & COMMON_INTR_MAP_REG_INO; 833503831d35Sstevel 833603831d35Sstevel if (ino <= 0x1f) { 833703831d35Sstevel /* 833803831d35Sstevel * PCI slot interrupt 833903831d35Sstevel */ 834003831d35Sstevel drmach_s1p_decode_slot_intr(exp, unum, 834103831d35Sstevel pci, ino, iter); 834203831d35Sstevel } else if (ino <= 0x2f) { 834303831d35Sstevel /* 834403831d35Sstevel * OBIO interrupt 834503831d35Sstevel */ 834603831d35Sstevel prom_printf("IO%d/P%d OBIO interrupt: " 834703831d35Sstevel "ino=0x%x\n", exp, unum, ino); 834803831d35Sstevel } else if (ino <= 0x37) { 834903831d35Sstevel /* 835003831d35Sstevel * Internal interrupt 835103831d35Sstevel */ 835203831d35Sstevel prom_printf("IO%d/P%d Internal " 835303831d35Sstevel "interrupt: ino=0x%x (%s)\n", 835403831d35Sstevel exp, unum, ino, 835503831d35Sstevel drmach_schz_internal_ino2str(ino)); 835603831d35Sstevel } else { 835703831d35Sstevel /* 835803831d35Sstevel * NewLink interrupt 835903831d35Sstevel */ 836003831d35Sstevel prom_printf("IO%d/P%d NewLink " 836103831d35Sstevel "interrupt: ino=0x%x\n", exp, 836203831d35Sstevel unum, ino); 836303831d35Sstevel } 836403831d35Sstevel 836503831d35Sstevel DRMACH_PR("drmach_s1p_schizo_log_intr: " 836603831d35Sstevel "exp=%d, schizo=%d, pci_leaf=%c, " 836703831d35Sstevel "ino=0x%x, intr_map_reg=0x%lx\n", 836803831d35Sstevel exp, unum, (i == 0) ? 'A' : 'B', ino, reg); 836903831d35Sstevel } 837003831d35Sstevel } 837103831d35Sstevel } 837203831d35Sstevel } 837303831d35Sstevel 837403831d35Sstevel /* 837503831d35Sstevel * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.2.4 837603831d35Sstevel * "Safari Error Control/Log Registers", Table 22-11, page 248. 837703831d35Sstevel */ 837803831d35Sstevel #define DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR (0x1ull << 4) 837903831d35Sstevel 838003831d35Sstevel /* 838103831d35Sstevel * Check for possible error indicators prior to resuming the 838203831d35Sstevel * AXQ driver, which will de-assert slot1 AXQ_DOMCTRL_PAUSE. 838303831d35Sstevel */ 838403831d35Sstevel static void 838503831d35Sstevel drmach_slot1_pause_verify(drmach_slot1_pause_t **slot1_paused, 838603831d35Sstevel drmach_sr_iter_t iter) 838703831d35Sstevel { 838803831d35Sstevel int i, j; 838903831d35Sstevel int errflag = 0; 839003831d35Sstevel drmach_slot1_pause_t *slot1; 839103831d35Sstevel 839203831d35Sstevel /* 839303831d35Sstevel * Check for logged schizo bus error and pending interrupts. 839403831d35Sstevel */ 839503831d35Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) { 839603831d35Sstevel if ((slot1 = slot1_paused[i]) == NULL) 839703831d35Sstevel continue; 839803831d35Sstevel 839903831d35Sstevel for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) { 840003831d35Sstevel if (slot1->schizo[j].csr_basepa == 0x0UL) 840103831d35Sstevel continue; 840203831d35Sstevel 840303831d35Sstevel if (slot1->schizo[j].regs[iter].errlog & 840403831d35Sstevel DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR) { 840503831d35Sstevel if (!errflag) { 840603831d35Sstevel prom_printf("DR WARNING: interrupt " 840703831d35Sstevel "attempt detected during " 840803831d35Sstevel "copy-rename (%s):\n", 840903831d35Sstevel (iter == DRMACH_POST_SUSPEND) ? 841003831d35Sstevel "post suspend" : "pre resume"); 841103831d35Sstevel ++errflag; 841203831d35Sstevel } 841303831d35Sstevel drmach_s1p_schizo_log_intr(&slot1->schizo[j], 841403831d35Sstevel i, j, iter); 841503831d35Sstevel } 841603831d35Sstevel } 841703831d35Sstevel } 841803831d35Sstevel 841903831d35Sstevel /* 842003831d35Sstevel * Check for changes in axq l2_io_q performance counters (2nd pass only) 842103831d35Sstevel */ 842203831d35Sstevel if (iter == DRMACH_PRE_RESUME) { 842303831d35Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) { 842403831d35Sstevel if ((slot1 = slot1_paused[i]) == NULL) 842503831d35Sstevel continue; 842603831d35Sstevel 842703831d35Sstevel if (slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND] != 842803831d35Sstevel slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]) { 842903831d35Sstevel prom_printf("DR WARNING: IO transactions " 843003831d35Sstevel "detected on IO%d during copy-rename: " 843103831d35Sstevel "AXQ l2_io_q performance counter " 843203831d35Sstevel "start=%d, end=%d\n", i, 843303831d35Sstevel slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND], 843403831d35Sstevel slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]); 843503831d35Sstevel } 843603831d35Sstevel } 843703831d35Sstevel } 843803831d35Sstevel } 843903831d35Sstevel 844003831d35Sstevel struct drmach_sr_list { 844103831d35Sstevel dev_info_t *dip; 844203831d35Sstevel struct drmach_sr_list *next; 844303831d35Sstevel struct drmach_sr_list *prev; 844403831d35Sstevel }; 844503831d35Sstevel 844603831d35Sstevel static struct drmach_sr_ordered { 844703831d35Sstevel char *name; 844803831d35Sstevel struct drmach_sr_list *ring; 844903831d35Sstevel } drmach_sr_ordered[] = { 845003831d35Sstevel { "iosram", NULL }, 845103831d35Sstevel { "address-extender-queue", NULL }, 845203831d35Sstevel { NULL, NULL }, /* terminator -- required */ 845303831d35Sstevel }; 845403831d35Sstevel 845503831d35Sstevel static void 845603831d35Sstevel drmach_sr_insert(struct drmach_sr_list **lp, dev_info_t *dip) 845703831d35Sstevel { 845803831d35Sstevel struct drmach_sr_list *np; 845903831d35Sstevel 846007d06da5SSurya Prakki DRMACH_PR("drmach_sr_insert: adding dip %p\n", (void *)dip); 846103831d35Sstevel 846203831d35Sstevel np = (struct drmach_sr_list *)kmem_alloc( 846303831d35Sstevel sizeof (struct drmach_sr_list), KM_SLEEP); 846403831d35Sstevel 846503831d35Sstevel ndi_hold_devi(dip); 846603831d35Sstevel np->dip = dip; 846703831d35Sstevel 846803831d35Sstevel if (*lp == NULL) { 846903831d35Sstevel /* establish list */ 847003831d35Sstevel *lp = np->next = np->prev = np; 847103831d35Sstevel } else { 847203831d35Sstevel /* place new node behind head node on ring list */ 847303831d35Sstevel np->prev = (*lp)->prev; 847403831d35Sstevel np->next = *lp; 847503831d35Sstevel np->prev->next = np; 847603831d35Sstevel np->next->prev = np; 847703831d35Sstevel } 847803831d35Sstevel } 847903831d35Sstevel 848003831d35Sstevel static void 848103831d35Sstevel drmach_sr_delete(struct drmach_sr_list **lp, dev_info_t *dip) 848203831d35Sstevel { 848307d06da5SSurya Prakki DRMACH_PR("drmach_sr_delete: searching for dip %p\n", (void *)dip); 848403831d35Sstevel 848503831d35Sstevel if (*lp) { 848603831d35Sstevel struct drmach_sr_list *xp; 848703831d35Sstevel 848803831d35Sstevel /* start search with mostly likely node */ 848903831d35Sstevel xp = (*lp)->prev; 849003831d35Sstevel do { 849103831d35Sstevel if (xp->dip == dip) { 849203831d35Sstevel xp->prev->next = xp->next; 849303831d35Sstevel xp->next->prev = xp->prev; 849403831d35Sstevel 849503831d35Sstevel if (xp == *lp) 849603831d35Sstevel *lp = xp->next; 849703831d35Sstevel if (xp == *lp) 849803831d35Sstevel *lp = NULL; 849903831d35Sstevel xp->dip = NULL; 850003831d35Sstevel ndi_rele_devi(dip); 850103831d35Sstevel kmem_free(xp, sizeof (*xp)); 850203831d35Sstevel 850303831d35Sstevel DRMACH_PR("drmach_sr_delete:" 850407d06da5SSurya Prakki " disposed sr node for dip %p", 850507d06da5SSurya Prakki (void *)dip); 850603831d35Sstevel return; 850703831d35Sstevel } 850803831d35Sstevel 850903831d35Sstevel DRMACH_PR("drmach_sr_delete: still searching\n"); 851003831d35Sstevel 851103831d35Sstevel xp = xp->prev; 851203831d35Sstevel } while (xp != (*lp)->prev); 851303831d35Sstevel } 851403831d35Sstevel 851503831d35Sstevel /* every dip should be found during resume */ 851607d06da5SSurya Prakki DRMACH_PR("ERROR: drmach_sr_delete: can't find dip %p", (void *)dip); 851703831d35Sstevel } 851803831d35Sstevel 851903831d35Sstevel int 852003831d35Sstevel drmach_verify_sr(dev_info_t *dip, int sflag) 852103831d35Sstevel { 852203831d35Sstevel int rv; 852303831d35Sstevel int len; 852403831d35Sstevel char name[OBP_MAXDRVNAME]; 852503831d35Sstevel 852603831d35Sstevel if (drmach_slot1_pause_debug) { 852703831d35Sstevel if (sflag && drmach_slot1_pause_init) { 852803831d35Sstevel drmach_slot1_pause_free(drmach_slot1_paused); 852903831d35Sstevel drmach_slot1_pause_init = 0; 853003831d35Sstevel } else if (!sflag && !drmach_slot1_pause_init) { 853103831d35Sstevel /* schedule init for next suspend */ 853203831d35Sstevel drmach_slot1_pause_init = 1; 853303831d35Sstevel } 853403831d35Sstevel } 853503831d35Sstevel 853603831d35Sstevel rv = ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 853703831d35Sstevel "name", &len); 853803831d35Sstevel if (rv == DDI_PROP_SUCCESS) { 853903831d35Sstevel int portid; 854003831d35Sstevel uint64_t reg; 854103831d35Sstevel struct drmach_sr_ordered *op; 854203831d35Sstevel 854303831d35Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 854403831d35Sstevel DDI_PROP_DONTPASS, "name", (caddr_t)name, &len); 854503831d35Sstevel 854603831d35Sstevel if (rv != DDI_PROP_SUCCESS) 854703831d35Sstevel return (0); 854803831d35Sstevel 854903831d35Sstevel if (drmach_slot1_pause_debug && sflag && 855003831d35Sstevel drmach_is_slot1_pause_axq(dip, name, &portid, ®)) { 855103831d35Sstevel drmach_slot1_pause_add_axq(dip, name, portid, reg, 855203831d35Sstevel drmach_slot1_paused); 855303831d35Sstevel } 855403831d35Sstevel 855503831d35Sstevel for (op = drmach_sr_ordered; op->name; op++) { 855603831d35Sstevel if (strncmp(op->name, name, strlen(op->name)) == 0) { 855703831d35Sstevel if (sflag) 855803831d35Sstevel drmach_sr_insert(&op->ring, dip); 855903831d35Sstevel else 856003831d35Sstevel drmach_sr_delete(&op->ring, dip); 856103831d35Sstevel return (1); 856203831d35Sstevel } 856303831d35Sstevel } 856403831d35Sstevel } 856503831d35Sstevel 856603831d35Sstevel return (0); 856703831d35Sstevel } 856803831d35Sstevel 856903831d35Sstevel static void 857003831d35Sstevel drmach_sr_dip(dev_info_t *dip, int suspend) 857103831d35Sstevel { 857203831d35Sstevel int rv; 857303831d35Sstevel major_t maj; 857403831d35Sstevel char *name, *name_addr, *aka; 857503831d35Sstevel 857603831d35Sstevel if ((name = ddi_get_name(dip)) == NULL) 857703831d35Sstevel name = "<null name>"; 857803831d35Sstevel else if ((maj = ddi_name_to_major(name)) != -1) 857903831d35Sstevel aka = ddi_major_to_name(maj); 858003831d35Sstevel else 858103831d35Sstevel aka = "<unknown>"; 858203831d35Sstevel 858303831d35Sstevel if ((name_addr = ddi_get_name_addr(dip)) == NULL) 858403831d35Sstevel name_addr = "<null>"; 858503831d35Sstevel 858603831d35Sstevel prom_printf("\t%s %s@%s (aka %s)\n", 858703831d35Sstevel suspend ? "suspending" : "resuming", 858803831d35Sstevel name, name_addr, aka); 858903831d35Sstevel 859003831d35Sstevel if (suspend) { 859103831d35Sstevel rv = devi_detach(dip, DDI_SUSPEND); 859203831d35Sstevel } else { 859303831d35Sstevel rv = devi_attach(dip, DDI_RESUME); 859403831d35Sstevel } 859503831d35Sstevel 859603831d35Sstevel if (rv != DDI_SUCCESS) { 859703831d35Sstevel prom_printf("\tFAILED to %s %s@%s\n", 859803831d35Sstevel suspend ? "suspend" : "resume", 859903831d35Sstevel name, name_addr); 860003831d35Sstevel } 860103831d35Sstevel } 860203831d35Sstevel 860303831d35Sstevel void 860403831d35Sstevel drmach_suspend_last() 860503831d35Sstevel { 860603831d35Sstevel struct drmach_sr_ordered *op; 860703831d35Sstevel 860803831d35Sstevel if (drmach_slot1_pause_debug) 860903831d35Sstevel drmach_slot1_pause_add_io(drmach_slot1_paused); 861003831d35Sstevel 861103831d35Sstevel /* 861203831d35Sstevel * The ordering array declares the strict sequence in which 861303831d35Sstevel * the named drivers are to suspended. Each element in 861403831d35Sstevel * the array may have a double-linked ring list of driver 861503831d35Sstevel * instances (dip) in the order in which they were presented 861603831d35Sstevel * to drmach_verify_sr. If present, walk the list in the 861703831d35Sstevel * forward direction to suspend each instance. 861803831d35Sstevel */ 861903831d35Sstevel for (op = drmach_sr_ordered; op->name; op++) { 862003831d35Sstevel if (op->ring) { 862103831d35Sstevel struct drmach_sr_list *rp; 862203831d35Sstevel 862303831d35Sstevel rp = op->ring; 862403831d35Sstevel do { 862503831d35Sstevel drmach_sr_dip(rp->dip, 1); 862603831d35Sstevel rp = rp->next; 862703831d35Sstevel } while (rp != op->ring); 862803831d35Sstevel } 862903831d35Sstevel } 863003831d35Sstevel 863103831d35Sstevel if (drmach_slot1_pause_debug) { 863203831d35Sstevel drmach_slot1_pause_update(drmach_slot1_paused, 863303831d35Sstevel DRMACH_POST_SUSPEND); 863403831d35Sstevel drmach_slot1_pause_verify(drmach_slot1_paused, 863503831d35Sstevel DRMACH_POST_SUSPEND); 863603831d35Sstevel } 863703831d35Sstevel } 863803831d35Sstevel 863903831d35Sstevel void 864003831d35Sstevel drmach_resume_first() 864103831d35Sstevel { 864203831d35Sstevel struct drmach_sr_ordered *op = drmach_sr_ordered + 864303831d35Sstevel (sizeof (drmach_sr_ordered) / sizeof (drmach_sr_ordered[0])); 864403831d35Sstevel 864503831d35Sstevel if (drmach_slot1_pause_debug) { 864603831d35Sstevel drmach_slot1_pause_update(drmach_slot1_paused, 864703831d35Sstevel DRMACH_PRE_RESUME); 864803831d35Sstevel drmach_slot1_pause_verify(drmach_slot1_paused, 864903831d35Sstevel DRMACH_PRE_RESUME); 865003831d35Sstevel } 865103831d35Sstevel 865203831d35Sstevel op -= 1; /* point at terminating element */ 865303831d35Sstevel 865403831d35Sstevel /* 865503831d35Sstevel * walk ordering array and rings backwards to resume dips 865603831d35Sstevel * in reverse order in which they were suspended 865703831d35Sstevel */ 865803831d35Sstevel while (--op >= drmach_sr_ordered) { 865903831d35Sstevel if (op->ring) { 866003831d35Sstevel struct drmach_sr_list *rp; 866103831d35Sstevel 866203831d35Sstevel rp = op->ring->prev; 866303831d35Sstevel do { 866403831d35Sstevel drmach_sr_dip(rp->dip, 0); 866503831d35Sstevel rp = rp->prev; 866603831d35Sstevel } while (rp != op->ring->prev); 866703831d35Sstevel } 866803831d35Sstevel } 866903831d35Sstevel } 867003831d35Sstevel 867103831d35Sstevel /* 867203831d35Sstevel * Log a DR sysevent. 867303831d35Sstevel * Return value: 0 success, non-zero failure. 867403831d35Sstevel */ 867503831d35Sstevel int 867603831d35Sstevel drmach_log_sysevent(int board, char *hint, int flag, int verbose) 867703831d35Sstevel { 867803831d35Sstevel sysevent_t *ev; 867903831d35Sstevel sysevent_id_t eid; 868003831d35Sstevel int rv, km_flag; 868103831d35Sstevel sysevent_value_t evnt_val; 868203831d35Sstevel sysevent_attr_list_t *evnt_attr_list = NULL; 868303831d35Sstevel char attach_pnt[MAXNAMELEN]; 868403831d35Sstevel 868503831d35Sstevel km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP; 868603831d35Sstevel attach_pnt[0] = '\0'; 868703831d35Sstevel if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) { 868803831d35Sstevel rv = -1; 868903831d35Sstevel goto logexit; 869003831d35Sstevel } 869103831d35Sstevel if (verbose) 869203831d35Sstevel DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n", 869303831d35Sstevel attach_pnt, hint, flag, verbose); 869403831d35Sstevel 869503831d35Sstevel if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, 869603831d35Sstevel SUNW_KERN_PUB"dr", km_flag)) == NULL) { 869703831d35Sstevel rv = -2; 869803831d35Sstevel goto logexit; 869903831d35Sstevel } 870003831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING; 870103831d35Sstevel evnt_val.value.sv_string = attach_pnt; 870203831d35Sstevel if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, 870303831d35Sstevel &evnt_val, km_flag)) != 0) 870403831d35Sstevel goto logexit; 870503831d35Sstevel 870603831d35Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING; 870703831d35Sstevel evnt_val.value.sv_string = hint; 870803831d35Sstevel if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, 870903831d35Sstevel &evnt_val, km_flag)) != 0) { 871003831d35Sstevel sysevent_free_attr(evnt_attr_list); 871103831d35Sstevel goto logexit; 871203831d35Sstevel } 871303831d35Sstevel 871403831d35Sstevel (void) sysevent_attach_attributes(ev, evnt_attr_list); 871503831d35Sstevel 871603831d35Sstevel /* 871703831d35Sstevel * Log the event but do not sleep waiting for its 871803831d35Sstevel * delivery. This provides insulation from syseventd. 871903831d35Sstevel */ 872003831d35Sstevel rv = log_sysevent(ev, SE_NOSLEEP, &eid); 872103831d35Sstevel 872203831d35Sstevel logexit: 872303831d35Sstevel if (ev) 872403831d35Sstevel sysevent_free(ev); 872503831d35Sstevel if ((rv != 0) && verbose) 872603831d35Sstevel cmn_err(CE_WARN, 872703831d35Sstevel "drmach_log_sysevent failed (rv %d) for %s %s\n", 872803831d35Sstevel rv, attach_pnt, hint); 872903831d35Sstevel 873003831d35Sstevel return (rv); 873103831d35Sstevel } 873203831d35Sstevel 873303831d35Sstevel /* 873403831d35Sstevel * Initialize the mem_slice portion of a claim/unconfig/unclaim mailbox message. 873503831d35Sstevel * Only the valid entries are modified, so the array should be zeroed out 873603831d35Sstevel * initially. 873703831d35Sstevel */ 873803831d35Sstevel static void 873903831d35Sstevel drmach_msg_memslice_init(dr_memslice_t slice_arr[]) { 874003831d35Sstevel int i; 874103831d35Sstevel char c; 874203831d35Sstevel 874303831d35Sstevel ASSERT(mutex_owned(&drmach_slice_table_lock)); 874403831d35Sstevel 874503831d35Sstevel for (i = 0; i < AXQ_MAX_EXP; i++) { 874603831d35Sstevel c = drmach_slice_table[i]; 874703831d35Sstevel 874803831d35Sstevel if (c & 0x20) { 874903831d35Sstevel slice_arr[i].valid = 1; 875003831d35Sstevel slice_arr[i].slice = c & 0x1f; 875103831d35Sstevel } 875203831d35Sstevel } 875303831d35Sstevel } 875403831d35Sstevel 875503831d35Sstevel /* 875603831d35Sstevel * Initialize the mem_regs portion of a claim/unconfig/unclaim mailbox message. 875703831d35Sstevel * Only the valid entries are modified, so the array should be zeroed out 875803831d35Sstevel * initially. 875903831d35Sstevel */ 876003831d35Sstevel static void 876103831d35Sstevel drmach_msg_memregs_init(dr_memregs_t regs_arr[]) { 876203831d35Sstevel int rv, exp, mcnum, bank; 876303831d35Sstevel uint64_t madr; 876403831d35Sstevel drmachid_t id; 876503831d35Sstevel drmach_board_t *bp; 876603831d35Sstevel drmach_mem_t *mp; 876703831d35Sstevel dr_memregs_t *memregs; 876803831d35Sstevel 876903831d35Sstevel /* CONSTCOND */ 877003831d35Sstevel ASSERT(DRMACH_MC_NBANKS == (PMBANKS_PER_PORT * LMBANKS_PER_PMBANK)); 877103831d35Sstevel 877203831d35Sstevel for (exp = 0; exp < 18; exp++) { 877303831d35Sstevel rv = drmach_array_get(drmach_boards, 877403831d35Sstevel DRMACH_EXPSLOT2BNUM(exp, 0), &id); 877503831d35Sstevel ASSERT(rv == 0); /* should never be out of bounds */ 877603831d35Sstevel if (id == NULL) { 877703831d35Sstevel continue; 877803831d35Sstevel } 877903831d35Sstevel 878003831d35Sstevel memregs = ®s_arr[exp]; 878103831d35Sstevel bp = (drmach_board_t *)id; 878203831d35Sstevel for (mp = bp->mem; mp != NULL; mp = mp->next) { 878303831d35Sstevel mcnum = mp->dev.portid & 0x3; 878403831d35Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) { 878503831d35Sstevel drmach_mem_read_madr(mp, bank, &madr); 878603831d35Sstevel if (madr & DRMACH_MC_VALID_MASK) { 878703831d35Sstevel DRMACH_PR("%d.%d.%d.madr = 0x%lx\n", 878803831d35Sstevel exp, mcnum, bank, madr); 878903831d35Sstevel memregs->madr[mcnum][bank].hi = 879003831d35Sstevel DRMACH_U64_TO_MCREGHI(madr); 879103831d35Sstevel memregs->madr[mcnum][bank].lo = 879203831d35Sstevel DRMACH_U64_TO_MCREGLO(madr); 879303831d35Sstevel } 879403831d35Sstevel } 879503831d35Sstevel } 879603831d35Sstevel } 879703831d35Sstevel } 879803831d35Sstevel 879903831d35Sstevel /* 880003831d35Sstevel * Do not allow physical address range modification if either board on this 880103831d35Sstevel * expander has processors in NULL LPA mode (CBASE=CBND=NULL). 880203831d35Sstevel * 880303831d35Sstevel * A side effect of NULL proc LPA mode in Starcat SSM is that local reads will 880403831d35Sstevel * install the cache line as owned/dirty as a result of the RTSR transaction. 880503831d35Sstevel * See section 5.2.3 of the Safari spec. All processors will read the bus sync 880603831d35Sstevel * list before the rename after flushing local caches. When copy-rename 880703831d35Sstevel * requires changing the physical address ranges (i.e. smaller memory target), 880803831d35Sstevel * the bus sync list contains physical addresses that will not exist after the 880903831d35Sstevel * rename. If these cache lines are owned due to a RTSR, a system error can 881003831d35Sstevel * occur following the rename when these cache lines are evicted and a writeback 881103831d35Sstevel * is attempted. 881203831d35Sstevel * 881303831d35Sstevel * Incoming parameter represents either the copy-rename source or a candidate 881403831d35Sstevel * target memory board. On Starcat, only slot0 boards may have memory. 881503831d35Sstevel */ 881603831d35Sstevel int 881703831d35Sstevel drmach_allow_memrange_modify(drmachid_t s0id) 881803831d35Sstevel { 881903831d35Sstevel drmach_board_t *s0bp, *s1bp; 882003831d35Sstevel drmachid_t s1id; 882103831d35Sstevel int rv; 882203831d35Sstevel 882303831d35Sstevel s0bp = s0id; 882403831d35Sstevel 882503831d35Sstevel ASSERT(DRMACH_IS_BOARD_ID(s0id)); 882603831d35Sstevel ASSERT(DRMACH_BNUM2SLOT(s0bp->bnum) == 0); 882703831d35Sstevel 882803831d35Sstevel if (s0bp->flags & DRMACH_NULL_PROC_LPA) { 882903831d35Sstevel /* 883003831d35Sstevel * This is reason enough to fail the request, no need 883103831d35Sstevel * to check the device list for cpus. 883203831d35Sstevel */ 883303831d35Sstevel return (0); 883403831d35Sstevel } 883503831d35Sstevel 883603831d35Sstevel /* 883703831d35Sstevel * Check for MCPU board on the same expander. 883803831d35Sstevel * 883903831d35Sstevel * The board flag DRMACH_NULL_PROC_LPA can be set for all board 884003831d35Sstevel * types, as it is derived at from the POST gdcd board flag 884103831d35Sstevel * L1SSFLG_THIS_L1_NULL_PROC_LPA, which can be set (and should be 884203831d35Sstevel * ignored) for boards with no processors. Since NULL proc LPA 884303831d35Sstevel * applies only to processors, we walk the devices array to detect 884403831d35Sstevel * MCPUs. 884503831d35Sstevel */ 884603831d35Sstevel rv = drmach_array_get(drmach_boards, s0bp->bnum + 1, &s1id); 884703831d35Sstevel s1bp = s1id; 884803831d35Sstevel if (rv == 0 && s1bp != NULL) { 884903831d35Sstevel 885003831d35Sstevel ASSERT(DRMACH_IS_BOARD_ID(s1id)); 885103831d35Sstevel ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1); 885203831d35Sstevel ASSERT(DRMACH_BNUM2EXP(s0bp->bnum) == 885303831d35Sstevel DRMACH_BNUM2EXP(s1bp->bnum)); 885403831d35Sstevel 885503831d35Sstevel if ((s1bp->flags & DRMACH_NULL_PROC_LPA) && 885603831d35Sstevel s1bp->devices != NULL) { 885703831d35Sstevel int d_idx; 885803831d35Sstevel drmachid_t d_id; 885903831d35Sstevel 886003831d35Sstevel rv = drmach_array_first(s1bp->devices, &d_idx, &d_id); 886103831d35Sstevel while (rv == 0) { 886203831d35Sstevel if (DRMACH_IS_CPU_ID(d_id)) { 886303831d35Sstevel /* 886403831d35Sstevel * Fail MCPU in NULL LPA mode. 886503831d35Sstevel */ 886603831d35Sstevel return (0); 886703831d35Sstevel } 886803831d35Sstevel 886903831d35Sstevel rv = drmach_array_next(s1bp->devices, &d_idx, 887003831d35Sstevel &d_id); 887103831d35Sstevel } 887203831d35Sstevel } 887303831d35Sstevel } 887403831d35Sstevel 887503831d35Sstevel return (1); 887603831d35Sstevel } 8877