11708Sstevel /*
21708Sstevel * CDDL HEADER START
31708Sstevel *
41708Sstevel * The contents of this file are subject to the terms of the
51772Sjl139090 * Common Development and Distribution License (the "License").
61772Sjl139090 * You may not use this file except in compliance with the License.
71708Sstevel *
81708Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel * or http://www.opensolaris.org/os/licensing.
101708Sstevel * See the License for the specific language governing permissions
111708Sstevel * and limitations under the License.
121708Sstevel *
131708Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel *
191708Sstevel * CDDL HEADER END
201708Sstevel */
2111311SSurya.Prakki@Sun.COM
221708Sstevel /*
23*11474SJonathan.Adams@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
241708Sstevel * Use is subject to license terms.
251708Sstevel */
261708Sstevel
271708Sstevel #include <sys/note.h>
281708Sstevel #include <sys/debug.h>
291708Sstevel #include <sys/types.h>
301708Sstevel #include <sys/varargs.h>
311708Sstevel #include <sys/errno.h>
321708Sstevel #include <sys/cred.h>
331708Sstevel #include <sys/dditypes.h>
341708Sstevel #include <sys/devops.h>
351708Sstevel #include <sys/modctl.h>
361708Sstevel #include <sys/poll.h>
371708Sstevel #include <sys/conf.h>
381708Sstevel #include <sys/ddi.h>
391708Sstevel #include <sys/sunddi.h>
401708Sstevel #include <sys/sunndi.h>
411708Sstevel #include <sys/ndi_impldefs.h>
421708Sstevel #include <sys/stat.h>
431708Sstevel #include <sys/kmem.h>
441708Sstevel #include <sys/vmem.h>
451708Sstevel #include <sys/disp.h>
461708Sstevel #include <sys/processor.h>
471708Sstevel #include <sys/cheetahregs.h>
481708Sstevel #include <sys/cpuvar.h>
491708Sstevel #include <sys/mem_config.h>
501708Sstevel #include <sys/ddi_impldefs.h>
511708Sstevel #include <sys/systm.h>
521708Sstevel #include <sys/machsystm.h>
531708Sstevel #include <sys/autoconf.h>
541708Sstevel #include <sys/cmn_err.h>
551708Sstevel #include <sys/sysmacros.h>
561708Sstevel #include <sys/x_call.h>
571708Sstevel #include <sys/promif.h>
581708Sstevel #include <sys/prom_plat.h>
591708Sstevel #include <sys/membar.h>
601708Sstevel #include <vm/seg_kmem.h>
611708Sstevel #include <sys/mem_cage.h>
621708Sstevel #include <sys/stack.h>
631708Sstevel #include <sys/archsystm.h>
641708Sstevel #include <vm/hat_sfmmu.h>
651708Sstevel #include <sys/pte.h>
661708Sstevel #include <sys/mmu.h>
671708Sstevel #include <sys/cpu_module.h>
681708Sstevel #include <sys/obpdefs.h>
691708Sstevel #include <sys/mboxsc.h>
701708Sstevel #include <sys/plat_ecc_dimm.h>
711708Sstevel
721708Sstevel #include <sys/hotplug/hpctrl.h> /* XXX should be included by schpc.h */
731708Sstevel #include <sys/schpc.h>
741708Sstevel #include <sys/pci.h>
751708Sstevel
761708Sstevel #include <sys/starcat.h>
771708Sstevel #include <sys/cpu_sgnblk_defs.h>
781708Sstevel #include <sys/drmach.h>
791708Sstevel #include <sys/dr_util.h>
801708Sstevel #include <sys/dr_mbx.h>
811708Sstevel #include <sys/sc_gptwocfg.h>
821708Sstevel #include <sys/iosramreg.h>
831708Sstevel #include <sys/iosramio.h>
841708Sstevel #include <sys/iosramvar.h>
851708Sstevel #include <sys/axq.h>
861708Sstevel #include <sys/post/scat_dcd.h>
871708Sstevel #include <sys/kobj.h>
881708Sstevel #include <sys/taskq.h>
891708Sstevel #include <sys/cmp.h>
901708Sstevel #include <sys/sbd_ioctl.h>
911708Sstevel
921708Sstevel #include <sys/sysevent.h>
931708Sstevel #include <sys/sysevent/dr.h>
941708Sstevel #include <sys/sysevent/eventdefs.h>
951708Sstevel
961708Sstevel #include <sys/pci/pcisch.h>
971708Sstevel #include <sys/pci/pci_regs.h>
981708Sstevel
991708Sstevel #include <sys/ontrap.h>
1001708Sstevel
1011708Sstevel /* defined in ../ml/drmach.il.cpp */
1021708Sstevel extern void bcopy32_il(uint64_t, uint64_t);
1031708Sstevel extern void flush_ecache_il(int64_t physaddr, int size, int linesz);
1041708Sstevel extern void flush_dcache_il(void);
1051708Sstevel extern void flush_icache_il(void);
1061708Sstevel extern void flush_pcache_il(void);
1071708Sstevel
1081708Sstevel /* defined in ../ml/drmach_asm.s */
1091708Sstevel extern uint64_t lddmcdecode(uint64_t physaddr);
1101708Sstevel extern uint64_t lddsafconfig(void);
1111708Sstevel
1121708Sstevel /* XXX here until provided by sys/dman.h */
1131708Sstevel extern int man_dr_attach(dev_info_t *);
1141708Sstevel extern int man_dr_detach(dev_info_t *);
1151708Sstevel
1161708Sstevel #define DRMACH_BNUM2EXP(bnum) ((bnum) >> 1)
1171708Sstevel #define DRMACH_BNUM2SLOT(bnum) ((bnum) & 1)
1181708Sstevel #define DRMACH_EXPSLOT2BNUM(exp, slot) (((exp) << 1) + (slot))
1191708Sstevel
1201708Sstevel #define DRMACH_SLICE_MASK 0x1Full
1211708Sstevel #define DRMACH_SLICE_TO_PA(s) (((s) & DRMACH_SLICE_MASK) << 37)
1221708Sstevel #define DRMACH_PA_TO_SLICE(a) (((a) >> 37) & DRMACH_SLICE_MASK)
1231708Sstevel
1241708Sstevel /*
1251708Sstevel * DRMACH_MEM_SLICE_SIZE and DRMACH_MEM_USABLE_SLICE_SIZE define the
1261708Sstevel * available address space and the usable address space for every slice.
1271708Sstevel * There must be a distinction between the available and usable do to a
1281708Sstevel * restriction imposed by CDC memory size.
1291708Sstevel */
1301708Sstevel
1311708Sstevel #define DRMACH_MEM_SLICE_SIZE (1ull << 37) /* 128GB */
1321708Sstevel #define DRMACH_MEM_USABLE_SLICE_SIZE (1ull << 36) /* 64GB */
1331708Sstevel
1341708Sstevel #define DRMACH_MC_NBANKS 4
1351708Sstevel
1361708Sstevel #define DRMACH_MC_ADDR(mp, bank) ((mp)->madr_pa + 16 + 8 * (bank))
1371708Sstevel #define DRMACH_MC_ASI_ADDR(mp, bank) (DRMACH_MC_ADDR(mp, bank) & 0xFF)
1381708Sstevel
1391708Sstevel #define DRMACH_EMU_ACT_STATUS_OFFSET 0x50
1401708Sstevel #define DRMACH_EMU_ACT_STATUS_ADDR(mp) \
1411708Sstevel ((mp)->madr_pa + DRMACH_EMU_ACT_STATUS_OFFSET)
1421708Sstevel
1431708Sstevel /*
1441708Sstevel * The Cheetah's Safari Configuration Register and the Schizo's
1451708Sstevel * Safari Control/Status Register place the LPA base and bound fields in
1461708Sstevel * same bit locations with in their register word. This source code takes
1471708Sstevel * advantage of this by defining only one set of LPA encoding/decoding macros
1481708Sstevel * which are shared by various Cheetah and Schizo drmach routines.
1491708Sstevel */
1501708Sstevel #define DRMACH_LPA_BASE_MASK (0x3Full << 3)
1511708Sstevel #define DRMACH_LPA_BND_MASK (0x3Full << 9)
1521708Sstevel
1531708Sstevel #define DRMACH_LPA_BASE_TO_PA(scr) (((scr) & DRMACH_LPA_BASE_MASK) << 34)
1541708Sstevel #define DRMACH_LPA_BND_TO_PA(scr) (((scr) & DRMACH_LPA_BND_MASK) << 28)
1551708Sstevel #define DRMACH_PA_TO_LPA_BASE(pa) (((pa) >> 34) & DRMACH_LPA_BASE_MASK)
1561708Sstevel #define DRMACH_PA_TO_LPA_BND(pa) (((pa) >> 28) & DRMACH_LPA_BND_MASK)
1571708Sstevel
1581708Sstevel #define DRMACH_L1_SET_LPA(b) \
1591708Sstevel (((b)->flags & DRMACH_NULL_PROC_LPA) == 0)
1601708Sstevel
1611708Sstevel #define DRMACH_CPU_SRAM_ADDR 0x7fff0900000ull
1621708Sstevel #define DRMACH_CPU_SRAM_SIZE 0x20000ull
1631708Sstevel
1641708Sstevel /*
1651708Sstevel * Name properties for frequently accessed device nodes.
1661708Sstevel */
1671708Sstevel #define DRMACH_CPU_NAMEPROP "cpu"
1681708Sstevel #define DRMACH_CMP_NAMEPROP "cmp"
1691708Sstevel #define DRMACH_AXQ_NAMEPROP "address-extender-queue"
1701708Sstevel #define DRMACH_PCI_NAMEPROP "pci"
1711708Sstevel
1721708Sstevel /*
1731708Sstevel * Maximum value of processor Safari Timeout Log (TOL) field of
1741708Sstevel * Safari Config reg (7 secs).
1751708Sstevel */
1761708Sstevel #define DRMACH_SAF_TOL_MAX 7 * 1000000
1771708Sstevel
1781708Sstevel /*
1791708Sstevel * drmach_board_t flag definitions
1801708Sstevel */
1811708Sstevel #define DRMACH_NULL_PROC_LPA 0x1
1821708Sstevel
1831708Sstevel typedef struct {
1841708Sstevel uint32_t reg_addr_hi;
1851708Sstevel uint32_t reg_addr_lo;
1861708Sstevel uint32_t reg_size_hi;
1871708Sstevel uint32_t reg_size_lo;
1881708Sstevel } drmach_reg_t;
1891708Sstevel
1901708Sstevel typedef struct {
1911708Sstevel struct drmach_node *node;
1921708Sstevel void *data;
1931708Sstevel } drmach_node_walk_args_t;
1941708Sstevel
1951708Sstevel typedef struct drmach_node {
1961708Sstevel void *here;
1971708Sstevel
1981708Sstevel pnode_t (*get_dnode)(struct drmach_node *node);
1991708Sstevel int (*walk)(struct drmach_node *node, void *data,
2001708Sstevel int (*cb)(drmach_node_walk_args_t *args));
2011708Sstevel dev_info_t *(*n_getdip)(struct drmach_node *node);
2021708Sstevel int (*n_getproplen)(struct drmach_node *node, char *name,
2031708Sstevel int *len);
2041708Sstevel int (*n_getprop)(struct drmach_node *node, char *name,
2051708Sstevel void *buf, int len);
2061708Sstevel int (*get_parent)(struct drmach_node *node,
2071708Sstevel struct drmach_node *pnode);
2081708Sstevel } drmach_node_t;
2091708Sstevel
2101708Sstevel typedef struct {
2111708Sstevel int min_index;
2121708Sstevel int max_index;
2131708Sstevel int arr_sz;
2141708Sstevel drmachid_t *arr;
2151708Sstevel } drmach_array_t;
2161708Sstevel
2171708Sstevel typedef struct {
2181708Sstevel void *isa;
2191708Sstevel
2201708Sstevel void (*dispose)(drmachid_t);
2211708Sstevel sbd_error_t *(*release)(drmachid_t);
2221708Sstevel sbd_error_t *(*status)(drmachid_t, drmach_status_t *);
2231708Sstevel
2241708Sstevel char name[MAXNAMELEN];
2251708Sstevel } drmach_common_t;
2261708Sstevel
2271708Sstevel struct drmach_board;
2281708Sstevel typedef struct drmach_board drmach_board_t;
2291708Sstevel
2301708Sstevel typedef struct {
2311708Sstevel drmach_common_t cm;
2321708Sstevel const char *type;
2331708Sstevel drmach_board_t *bp;
2341708Sstevel drmach_node_t *node;
2351708Sstevel int portid;
2361708Sstevel int unum;
2371708Sstevel int busy;
2381708Sstevel int powered;
2391708Sstevel } drmach_device_t;
2401708Sstevel
2411708Sstevel typedef struct drmach_cpu {
2421708Sstevel drmach_device_t dev;
2431708Sstevel uint64_t scr_pa;
2441708Sstevel processorid_t cpuid;
2451708Sstevel int coreid;
2461708Sstevel } drmach_cpu_t;
2471708Sstevel
2481708Sstevel typedef struct drmach_mem {
2491708Sstevel drmach_device_t dev;
2501708Sstevel struct drmach_mem *next;
2511708Sstevel uint64_t nbytes;
2521708Sstevel uint64_t madr_pa;
2531708Sstevel } drmach_mem_t;
2541708Sstevel
2551708Sstevel typedef struct drmach_io {
2561708Sstevel drmach_device_t dev;
2571708Sstevel uint64_t scsr_pa; /* PA of Schizo Control/Status Register */
2581708Sstevel } drmach_io_t;
2591708Sstevel
2601708Sstevel struct drmach_board {
2611708Sstevel drmach_common_t cm;
2621708Sstevel int bnum;
2631708Sstevel int assigned;
2641708Sstevel int powered;
2651708Sstevel int connected;
2661708Sstevel int empty;
2671708Sstevel int cond;
2681708Sstevel uint_t cpu_impl;
2691708Sstevel uint_t flags;
2701708Sstevel drmach_node_t *tree;
2711708Sstevel drmach_array_t *devices;
2721708Sstevel drmach_mem_t *mem;
2731708Sstevel uint64_t stardrb_offset;
2741708Sstevel char type[BD_TYPELEN];
2751708Sstevel };
2761708Sstevel
2771708Sstevel typedef struct {
2781708Sstevel int flags;
2791708Sstevel drmach_device_t *dp;
2801708Sstevel sbd_error_t *err;
2811708Sstevel dev_info_t *fdip;
2821708Sstevel } drmach_config_args_t;
2831708Sstevel
2841708Sstevel typedef struct {
2851708Sstevel drmach_board_t *obj;
2861708Sstevel int ndevs;
2871708Sstevel void *a;
2881708Sstevel sbd_error_t *(*found)(void *a, const char *, int, drmachid_t);
2891708Sstevel sbd_error_t *err;
2901708Sstevel } drmach_board_cb_data_t;
2911708Sstevel
2921708Sstevel typedef struct drmach_casmslot {
2931708Sstevel int valid;
2941708Sstevel int slice;
2951708Sstevel } drmach_casmslot_t;
2961708Sstevel
2971708Sstevel typedef enum {
2981708Sstevel DRMACH_CR_OK,
2991708Sstevel DRMACH_CR_MC_IDLE_ERR,
3001708Sstevel DRMACH_CR_IOPAUSE_ERR,
3011708Sstevel DRMACH_CR_ONTRAP_ERR
3021708Sstevel } drmach_cr_err_t;
3031708Sstevel
3041708Sstevel typedef struct {
3051708Sstevel void *isa;
3061708Sstevel caddr_t data;
3071708Sstevel drmach_mem_t *s_mp;
3081708Sstevel drmach_mem_t *t_mp;
3091708Sstevel struct memlist *c_ml;
3101708Sstevel uint64_t s_copybasepa;
3111708Sstevel uint64_t t_copybasepa;
3121708Sstevel drmach_cr_err_t ecode;
3131708Sstevel void *earg;
3141708Sstevel } drmach_copy_rename_t;
3151708Sstevel
3161708Sstevel /*
3171708Sstevel * The following global is read as a boolean value, non-zero is true.
3181708Sstevel * If zero, DR copy-rename and cpu poweron will not set the processor
3191708Sstevel * LPA settings (CBASE, CBND of Safari config register) to correspond
3201708Sstevel * to the current memory slice map. LPAs of processors present at boot
3211708Sstevel * will remain as programmed by POST. LPAs of processors on boards added
3221708Sstevel * by DR will remain NULL, as programmed by POST. This can be used to
3231708Sstevel * to override the per-board L1SSFLG_THIS_L1_NULL_PROC_LPA flag set by
3241708Sstevel * POST in the LDCD (and copied to the GDCD by SMS).
3251708Sstevel *
3261708Sstevel * drmach_reprogram_lpa and L1SSFLG_THIS_L1_NULL_PROC_LPA do not apply
3271708Sstevel * to Schizo device LPAs. These are always set by DR.
3281708Sstevel */
3291708Sstevel static int drmach_reprogram_lpa = 1;
3301708Sstevel
3311708Sstevel /*
3321708Sstevel * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
3331708Sstevel * can fail to receive an XIR. To workaround this issue until a hardware
3341708Sstevel * fix is implemented, we will exclude the selection of these CPUs.
3351708Sstevel * Setting this to 0 will allow their selection again.
3361708Sstevel */
3371708Sstevel static int drmach_iocage_exclude_jaguar_port_zero = 1;
3381708Sstevel
3391708Sstevel static int drmach_initialized;
3401708Sstevel static drmach_array_t *drmach_boards;
3411708Sstevel
3421708Sstevel static int drmach_cpu_delay = 1000;
3431708Sstevel static int drmach_cpu_ntries = 50000;
3441708Sstevel
3451708Sstevel static uint32_t drmach_slice_table[AXQ_MAX_EXP];
3461708Sstevel static kmutex_t drmach_slice_table_lock;
3471708Sstevel
3481708Sstevel tte_t drmach_cpu_sram_tte[NCPU];
3491708Sstevel caddr_t drmach_cpu_sram_va;
3501708Sstevel
3511708Sstevel /*
3521708Sstevel * Setting to non-zero will enable delay before all disconnect ops.
3531708Sstevel */
3541708Sstevel static int drmach_unclaim_delay_all;
3551708Sstevel /*
3561708Sstevel * Default delay is slightly greater than the max processor Safari timeout.
3571708Sstevel * This delay is intended to ensure the outstanding Safari activity has
3581708Sstevel * retired on this board prior to a board disconnect.
3591708Sstevel */
3601708Sstevel static clock_t drmach_unclaim_usec_delay = DRMACH_SAF_TOL_MAX + 10;
3611708Sstevel
3621708Sstevel /*
3631708Sstevel * By default, DR of non-Panther procs is not allowed into a Panther
3641708Sstevel * domain with large page sizes enabled. Setting this to 0 will remove
3651708Sstevel * the restriction.
3661708Sstevel */
3671708Sstevel static int drmach_large_page_restriction = 1;
3681708Sstevel
3691708Sstevel /*
3701708Sstevel * Used to pass updated LPA values to procs.
3711708Sstevel * Protocol is to clear the array before use.
3721708Sstevel */
3731708Sstevel volatile uchar_t *drmach_xt_mb;
3741708Sstevel volatile uint64_t drmach_xt_ready;
3751708Sstevel static kmutex_t drmach_xt_mb_lock;
3761708Sstevel static int drmach_xt_mb_size;
3771708Sstevel
3781708Sstevel uint64_t drmach_bus_sync_list[18 * 4 * 4 + 1];
3791708Sstevel static kmutex_t drmach_bus_sync_lock;
3801708Sstevel
3811708Sstevel static sbd_error_t *drmach_device_new(drmach_node_t *,
3821708Sstevel drmach_board_t *, int, drmachid_t *);
3831708Sstevel static sbd_error_t *drmach_cpu_new(drmach_device_t *, drmachid_t *);
3841708Sstevel static sbd_error_t *drmach_mem_new(drmach_device_t *, drmachid_t *);
3851708Sstevel static sbd_error_t *drmach_pci_new(drmach_device_t *, drmachid_t *);
3861708Sstevel static sbd_error_t *drmach_io_new(drmach_device_t *, drmachid_t *);
3871708Sstevel
3881708Sstevel static dev_info_t *drmach_node_ddi_get_dip(drmach_node_t *np);
3891708Sstevel static int drmach_node_ddi_get_prop(drmach_node_t *np,
3901708Sstevel char *name, void *buf, int len);
3911708Sstevel static int drmach_node_ddi_get_proplen(drmach_node_t *np,
3921708Sstevel char *name, int *len);
3931708Sstevel
3941708Sstevel static dev_info_t *drmach_node_obp_get_dip(drmach_node_t *np);
3951708Sstevel static int drmach_node_obp_get_prop(drmach_node_t *np,
3961708Sstevel char *name, void *buf, int len);
3971708Sstevel static int drmach_node_obp_get_proplen(drmach_node_t *np,
3981708Sstevel char *name, int *len);
3991708Sstevel
4001708Sstevel static sbd_error_t *drmach_mbox_trans(uint8_t msgtype, int bnum,
4011708Sstevel caddr_t obufp, int olen,
4021708Sstevel caddr_t ibufp, int ilen);
4031708Sstevel
4041708Sstevel sbd_error_t *drmach_io_post_attach(drmachid_t id);
4051708Sstevel sbd_error_t *drmach_io_post_release(drmachid_t id);
4061708Sstevel
4071708Sstevel static sbd_error_t *drmach_iocage_setup(dr_testboard_req_t *,
4081708Sstevel drmach_device_t **dpp, cpu_flag_t *oflags);
4091708Sstevel static int drmach_iocage_cpu_return(drmach_device_t *dp,
4101708Sstevel cpu_flag_t oflags);
4111708Sstevel static sbd_error_t *drmach_iocage_mem_return(dr_testboard_reply_t *tbr);
4121708Sstevel void drmach_iocage_mem_scrub(uint64_t nbytes);
4131708Sstevel
4141708Sstevel static sbd_error_t *drmach_i_status(drmachid_t id, drmach_status_t *stat);
4151708Sstevel
4161708Sstevel static void drmach_slot1_lpa_set(drmach_board_t *bp);
4171708Sstevel
4181708Sstevel static void drmach_cpu_read(uint64_t arg1, uint64_t arg2);
4191708Sstevel static int drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr);
4201708Sstevel
4211708Sstevel static void drmach_bus_sync_list_update(void);
4221708Sstevel static void drmach_slice_table_update(drmach_board_t *, int);
4231708Sstevel static int drmach_portid2bnum(int);
4241708Sstevel
4251708Sstevel static void drmach_msg_memslice_init(dr_memslice_t slice_arr[]);
4261708Sstevel static void drmach_msg_memregs_init(dr_memregs_t regs_arr[]);
4271708Sstevel
4281708Sstevel static int drmach_panther_boards(void);
4291708Sstevel
4301708Sstevel static int drmach_name2type_idx(char *);
4311708Sstevel
4321708Sstevel #ifdef DEBUG
4331708Sstevel
4341708Sstevel #define DRMACH_PR if (drmach_debug) printf
4351708Sstevel #define DRMACH_MEMLIST_DUMP if (drmach_debug) MEMLIST_DUMP
4361708Sstevel int drmach_debug = 0; /* set to non-zero to enable debug messages */
4371708Sstevel #else
4381708Sstevel
4391708Sstevel #define DRMACH_PR _NOTE(CONSTANTCONDITION) if (0) printf
4401708Sstevel #define DRMACH_MEMLIST_DUMP _NOTE(CONSTANTCONDITION) if (0) MEMLIST_DUMP
4411708Sstevel #endif /* DEBUG */
4421708Sstevel
4431708Sstevel #define DRMACH_OBJ(id) ((drmach_common_t *)id)
4441708Sstevel
4451708Sstevel #define DRMACH_IS_BOARD_ID(id) \
4461708Sstevel ((id != 0) && \
4471708Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_board_new))
4481708Sstevel
4491708Sstevel #define DRMACH_IS_CPU_ID(id) \
4501708Sstevel ((id != 0) && \
4511708Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new))
4521708Sstevel
4531708Sstevel #define DRMACH_IS_MEM_ID(id) \
4541708Sstevel ((id != 0) && \
4551708Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_mem_new))
4561708Sstevel
4571708Sstevel #define DRMACH_IS_IO_ID(id) \
4581708Sstevel ((id != 0) && \
4591708Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
4601708Sstevel
4611708Sstevel #define DRMACH_IS_DEVICE_ID(id) \
4621708Sstevel ((id != 0) && \
4631708Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
4641708Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
4651708Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
4661708Sstevel
4671708Sstevel #define DRMACH_IS_ID(id) \
4681708Sstevel ((id != 0) && \
4691708Sstevel (DRMACH_OBJ(id)->isa == (void *)drmach_board_new || \
4701708Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_cpu_new || \
4711708Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_mem_new || \
4721708Sstevel DRMACH_OBJ(id)->isa == (void *)drmach_io_new))
4731708Sstevel
4741708Sstevel #define DRMACH_INTERNAL_ERROR() \
4751708Sstevel drerr_new(1, ESTC_INTERNAL, drmach_ie_fmt, __LINE__)
4761708Sstevel static char *drmach_ie_fmt = "drmach.c %d";
4771708Sstevel
4781708Sstevel static struct {
4791708Sstevel const char *name;
4801708Sstevel const char *type;
4811708Sstevel sbd_error_t *(*new)(drmach_device_t *, drmachid_t *);
4821708Sstevel } drmach_name2type[] = {
4831708Sstevel {"cmp", DRMACH_DEVTYPE_CMP, NULL },
4841708Sstevel {"cpu", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
4851708Sstevel {"SUNW,UltraSPARC-III", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
4861708Sstevel {"SUNW,UltraSPARC-III+", DRMACH_DEVTYPE_CPU, drmach_cpu_new },
4871708Sstevel {"memory-controller", DRMACH_DEVTYPE_MEM, drmach_mem_new },
4881708Sstevel {"pci", DRMACH_DEVTYPE_PCI, drmach_pci_new },
4891708Sstevel {"SUNW,wci", DRMACH_DEVTYPE_WCI, drmach_io_new },
4901708Sstevel };
4911708Sstevel
4921708Sstevel /*
4931708Sstevel * drmach autoconfiguration data structures and interfaces
4941708Sstevel */
4951708Sstevel
4961708Sstevel extern struct mod_ops mod_miscops;
4971708Sstevel
4981708Sstevel static struct modlmisc modlmisc = {
4991708Sstevel &mod_miscops,
5007799SRichard.Bean@Sun.COM "Sun Fire 15000 DR"
5011708Sstevel };
5021708Sstevel
5031708Sstevel static struct modlinkage modlinkage = {
5041708Sstevel MODREV_1,
5051708Sstevel (void *)&modlmisc,
5061708Sstevel NULL
5071708Sstevel };
5081708Sstevel
5091708Sstevel /*
5101708Sstevel * drmach_boards_rwlock is used to synchronize read/write
5111708Sstevel * access to drmach_boards array between status and board lookup
5121708Sstevel * as READERS, and assign, and unassign threads as WRITERS.
5131708Sstevel */
5141708Sstevel static krwlock_t drmach_boards_rwlock;
5151708Sstevel
5161708Sstevel static kmutex_t drmach_i_lock;
5171708Sstevel static kmutex_t drmach_iocage_lock;
5181708Sstevel static kcondvar_t drmach_iocage_cv;
5191708Sstevel static int drmach_iocage_is_busy = 0;
5201708Sstevel uint64_t drmach_iocage_paddr;
5211708Sstevel static caddr_t drmach_iocage_vaddr;
5221708Sstevel static int drmach_iocage_size = 0;
5231708Sstevel static int drmach_is_cheetah = -1;
5241708Sstevel
5251708Sstevel int
_init(void)5261708Sstevel _init(void)
5271708Sstevel {
5281708Sstevel int err;
5291708Sstevel
5301708Sstevel mutex_init(&drmach_i_lock, NULL, MUTEX_DRIVER, NULL);
5311708Sstevel rw_init(&drmach_boards_rwlock, NULL, RW_DEFAULT, NULL);
5321708Sstevel drmach_xt_mb_size = NCPU * sizeof (uchar_t);
5331708Sstevel drmach_xt_mb = (uchar_t *)vmem_alloc(static_alloc_arena,
5341708Sstevel drmach_xt_mb_size, VM_SLEEP);
5351708Sstevel bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
5361708Sstevel if ((err = mod_install(&modlinkage)) != 0) {
5371708Sstevel mutex_destroy(&drmach_i_lock);
5381708Sstevel rw_destroy(&drmach_boards_rwlock);
5391708Sstevel vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
5401708Sstevel drmach_xt_mb_size);
5411708Sstevel }
5421708Sstevel
5431708Sstevel return (err);
5441708Sstevel }
5451708Sstevel
5461708Sstevel int
_fini(void)5471708Sstevel _fini(void)
5481708Sstevel {
5491708Sstevel static void drmach_fini(void);
5501708Sstevel int err;
5511708Sstevel
5521708Sstevel if ((err = mod_remove(&modlinkage)) == 0)
5531708Sstevel drmach_fini();
5541708Sstevel
5551708Sstevel return (err);
5561708Sstevel }
5571708Sstevel
5581708Sstevel int
_info(struct modinfo * modinfop)5591708Sstevel _info(struct modinfo *modinfop)
5601708Sstevel {
5611708Sstevel return (mod_info(&modlinkage, modinfop));
5621708Sstevel }
5631708Sstevel
5641708Sstevel /*
5651708Sstevel * drmach_node_* routines serve the purpose of separating the
5661708Sstevel * rest of the code from the device tree and OBP. This is necessary
5671708Sstevel * because of In-Kernel-Probing. Devices probed after stod, are probed
5681708Sstevel * by the in-kernel-prober, not OBP. These devices, therefore, do not
5691708Sstevel * have dnode ids.
5701708Sstevel */
5711708Sstevel
5721708Sstevel static int
drmach_node_obp_get_parent(drmach_node_t * np,drmach_node_t * pp)5731708Sstevel drmach_node_obp_get_parent(drmach_node_t *np, drmach_node_t *pp)
5741708Sstevel {
5751708Sstevel pnode_t nodeid;
5761708Sstevel static char *fn = "drmach_node_obp_get_parent";
5771708Sstevel
5781708Sstevel nodeid = np->get_dnode(np);
5791708Sstevel if (nodeid == OBP_NONODE) {
5801708Sstevel cmn_err(CE_WARN, "%s: invalid dnode", fn);
5811708Sstevel return (-1);
5821708Sstevel }
5831708Sstevel
5841708Sstevel bcopy(np, pp, sizeof (drmach_node_t));
5851708Sstevel
5861708Sstevel pp->here = (void *)(uintptr_t)prom_parentnode(nodeid);
5871708Sstevel if (pp->here == OBP_NONODE) {
5881708Sstevel cmn_err(CE_WARN, "%s: invalid parent dnode", fn);
5891708Sstevel return (-1);
5901708Sstevel }
5911708Sstevel
5921708Sstevel return (0);
5931708Sstevel }
5941708Sstevel
5951708Sstevel static pnode_t
drmach_node_obp_get_dnode(drmach_node_t * np)5961708Sstevel drmach_node_obp_get_dnode(drmach_node_t *np)
5971708Sstevel {
5981708Sstevel return ((pnode_t)(uintptr_t)np->here);
5991708Sstevel }
6001708Sstevel
6011708Sstevel typedef struct {
6021708Sstevel drmach_node_walk_args_t *nwargs;
6031708Sstevel int (*cb)(drmach_node_walk_args_t *args);
6041708Sstevel int err;
6051708Sstevel } drmach_node_ddi_walk_args_t;
6061708Sstevel
6071708Sstevel int
drmach_node_ddi_walk_cb(dev_info_t * dip,void * arg)6081708Sstevel drmach_node_ddi_walk_cb(dev_info_t *dip, void *arg)
6091708Sstevel {
6101708Sstevel drmach_node_ddi_walk_args_t *nargs;
6111708Sstevel
6121708Sstevel nargs = (drmach_node_ddi_walk_args_t *)arg;
6131708Sstevel
6141708Sstevel /*
6151708Sstevel * dip doesn't have to be held here as we are called
6161708Sstevel * from ddi_walk_devs() which holds the dip.
6171708Sstevel */
6181708Sstevel nargs->nwargs->node->here = (void *)dip;
6191708Sstevel
6201708Sstevel nargs->err = nargs->cb(nargs->nwargs);
6211708Sstevel
6221708Sstevel /*
6231708Sstevel * Set "here" to NULL so that unheld dip is not accessible
6241708Sstevel * outside ddi_walk_devs()
6251708Sstevel */
6261708Sstevel nargs->nwargs->node->here = NULL;
6271708Sstevel
6281708Sstevel if (nargs->err)
6291708Sstevel return (DDI_WALK_TERMINATE);
6301708Sstevel else
6311708Sstevel return (DDI_WALK_CONTINUE);
6321708Sstevel }
6331708Sstevel
6341708Sstevel static int
drmach_node_ddi_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))6351708Sstevel drmach_node_ddi_walk(drmach_node_t *np, void *data,
6361708Sstevel int (*cb)(drmach_node_walk_args_t *args))
6371708Sstevel {
6381708Sstevel drmach_node_walk_args_t args;
6391708Sstevel drmach_node_ddi_walk_args_t nargs;
6401708Sstevel
6411708Sstevel /* initialized args structure for callback */
6421708Sstevel args.node = np;
6431708Sstevel args.data = data;
6441708Sstevel
6451708Sstevel nargs.nwargs = &args;
6461708Sstevel nargs.cb = cb;
6471708Sstevel nargs.err = 0;
6481708Sstevel
6491708Sstevel /*
6501708Sstevel * Root node doesn't have to be held in any way.
6511708Sstevel */
65211066Srafael.vanoni@sun.com ddi_walk_devs(ddi_root_node(), drmach_node_ddi_walk_cb, (void *)&nargs);
6531708Sstevel
6541708Sstevel return (nargs.err);
6551708Sstevel }
6561708Sstevel
6571708Sstevel static int
drmach_node_obp_walk(drmach_node_t * np,void * data,int (* cb)(drmach_node_walk_args_t * args))6581708Sstevel drmach_node_obp_walk(drmach_node_t *np, void *data,
6591708Sstevel int (*cb)(drmach_node_walk_args_t *args))
6601708Sstevel {
6611708Sstevel pnode_t nodeid;
6621708Sstevel int rv;
6631708Sstevel drmach_node_walk_args_t args;
6641708Sstevel
6651708Sstevel /* initialized args structure for callback */
6661708Sstevel args.node = np;
6671708Sstevel args.data = data;
6681708Sstevel
6691708Sstevel nodeid = prom_childnode(prom_rootnode());
6701708Sstevel
6711708Sstevel /* save our new position within the tree */
6721708Sstevel np->here = (void *)(uintptr_t)nodeid;
6731708Sstevel
6741708Sstevel rv = 0;
6751708Sstevel while (nodeid != OBP_NONODE) {
6761708Sstevel
6771708Sstevel pnode_t child;
6781708Sstevel
6791708Sstevel rv = (*cb)(&args);
6801708Sstevel if (rv)
6811708Sstevel break;
6821708Sstevel
6831708Sstevel child = prom_childnode(nodeid);
6841708Sstevel np->here = (void *)(uintptr_t)child;
6851708Sstevel
6861708Sstevel while (child != OBP_NONODE) {
6871708Sstevel rv = (*cb)(&args);
6881708Sstevel if (rv)
6891708Sstevel break;
6901708Sstevel
6911708Sstevel child = prom_nextnode(child);
6921708Sstevel np->here = (void *)(uintptr_t)child;
6931708Sstevel }
6941708Sstevel
6951708Sstevel nodeid = prom_nextnode(nodeid);
6961708Sstevel
6971708Sstevel /* save our new position within the tree */
6981708Sstevel np->here = (void *)(uintptr_t)nodeid;
6991708Sstevel }
7001708Sstevel
7011708Sstevel return (rv);
7021708Sstevel }
7031708Sstevel
7041708Sstevel static int
drmach_node_ddi_get_parent(drmach_node_t * np,drmach_node_t * pp)7051708Sstevel drmach_node_ddi_get_parent(drmach_node_t *np, drmach_node_t *pp)
7061708Sstevel {
7071708Sstevel dev_info_t *ndip;
7081708Sstevel static char *fn = "drmach_node_ddi_get_parent";
7091708Sstevel
7101708Sstevel ndip = np->n_getdip(np);
7111708Sstevel if (ndip == NULL) {
7121708Sstevel cmn_err(CE_WARN, "%s: NULL dip", fn);
7131708Sstevel return (-1);
7141708Sstevel }
7151708Sstevel
7161708Sstevel bcopy(np, pp, sizeof (drmach_node_t));
7171708Sstevel
7181708Sstevel pp->here = (void *)ddi_get_parent(ndip);
7191708Sstevel if (pp->here == NULL) {
7201708Sstevel cmn_err(CE_WARN, "%s: NULL parent dip", fn);
7211708Sstevel return (-1);
7221708Sstevel }
7231708Sstevel
7241708Sstevel return (0);
7251708Sstevel }
7261708Sstevel
7271708Sstevel /*ARGSUSED*/
7281708Sstevel static pnode_t
drmach_node_ddi_get_dnode(drmach_node_t * np)7291708Sstevel drmach_node_ddi_get_dnode(drmach_node_t *np)
7301708Sstevel {
7311708Sstevel return ((pnode_t)NULL);
7321708Sstevel }
7331708Sstevel
7341708Sstevel static drmach_node_t *
drmach_node_new(void)7351708Sstevel drmach_node_new(void)
7361708Sstevel {
7371708Sstevel drmach_node_t *np;
7381708Sstevel
7391708Sstevel np = kmem_zalloc(sizeof (drmach_node_t), KM_SLEEP);
7401708Sstevel
7411708Sstevel if (drmach_initialized) {
7421708Sstevel np->get_dnode = drmach_node_ddi_get_dnode;
7431708Sstevel np->walk = drmach_node_ddi_walk;
7441708Sstevel np->n_getdip = drmach_node_ddi_get_dip;
7451708Sstevel np->n_getproplen = drmach_node_ddi_get_proplen;
7461708Sstevel np->n_getprop = drmach_node_ddi_get_prop;
7471708Sstevel np->get_parent = drmach_node_ddi_get_parent;
7481708Sstevel } else {
7491708Sstevel np->get_dnode = drmach_node_obp_get_dnode;
7501708Sstevel np->walk = drmach_node_obp_walk;
7511708Sstevel np->n_getdip = drmach_node_obp_get_dip;
7521708Sstevel np->n_getproplen = drmach_node_obp_get_proplen;
7531708Sstevel np->n_getprop = drmach_node_obp_get_prop;
7541708Sstevel np->get_parent = drmach_node_obp_get_parent;
7551708Sstevel }
7561708Sstevel
7571708Sstevel return (np);
7581708Sstevel }
7591708Sstevel
7601708Sstevel static void
drmach_node_dispose(drmach_node_t * np)7611708Sstevel drmach_node_dispose(drmach_node_t *np)
7621708Sstevel {
7631708Sstevel kmem_free(np, sizeof (*np));
7641708Sstevel }
7651708Sstevel
7661708Sstevel /*
7671708Sstevel * Check if a CPU node is part of a CMP.
7681708Sstevel */
7691708Sstevel static int
drmach_is_cmp_child(dev_info_t * dip)7701708Sstevel drmach_is_cmp_child(dev_info_t *dip)
7711708Sstevel {
7721708Sstevel dev_info_t *pdip;
7731708Sstevel
7741708Sstevel if (strcmp(ddi_node_name(dip), DRMACH_CPU_NAMEPROP) != 0) {
7751708Sstevel return (0);
7761708Sstevel }
7771708Sstevel
7781708Sstevel pdip = ddi_get_parent(dip);
7791708Sstevel
7801708Sstevel ASSERT(pdip);
7811708Sstevel
7821708Sstevel if (strcmp(ddi_node_name(pdip), DRMACH_CMP_NAMEPROP) == 0) {
7831708Sstevel return (1);
7841708Sstevel }
7851708Sstevel
7861708Sstevel return (0);
7871708Sstevel }
7881708Sstevel
7891708Sstevel static dev_info_t *
drmach_node_obp_get_dip(drmach_node_t * np)7901708Sstevel drmach_node_obp_get_dip(drmach_node_t *np)
7911708Sstevel {
7921708Sstevel pnode_t nodeid;
7931708Sstevel dev_info_t *dip;
7941708Sstevel
7951708Sstevel nodeid = np->get_dnode(np);
7961708Sstevel if (nodeid == OBP_NONODE)
7971708Sstevel return (NULL);
7981708Sstevel
7991708Sstevel dip = e_ddi_nodeid_to_dip(nodeid);
8001708Sstevel if (dip) {
8011708Sstevel /*
8021708Sstevel * The branch rooted at dip will have been previously
8031708Sstevel * held, or it will be the child of a CMP. In either
8041708Sstevel * case, the hold acquired in e_ddi_nodeid_to_dip()
8051708Sstevel * is not needed.
8061708Sstevel */
8071708Sstevel ddi_release_devi(dip);
8081708Sstevel ASSERT(drmach_is_cmp_child(dip) || e_ddi_branch_held(dip));
8091708Sstevel }
8101708Sstevel
8111708Sstevel return (dip);
8121708Sstevel }
8131708Sstevel
8141708Sstevel static dev_info_t *
drmach_node_ddi_get_dip(drmach_node_t * np)8151708Sstevel drmach_node_ddi_get_dip(drmach_node_t *np)
8161708Sstevel {
8171708Sstevel return ((dev_info_t *)np->here);
8181708Sstevel }
8191708Sstevel
8201708Sstevel static int
drmach_node_walk(drmach_node_t * np,void * param,int (* cb)(drmach_node_walk_args_t * args))8211708Sstevel drmach_node_walk(drmach_node_t *np, void *param,
8221708Sstevel int (*cb)(drmach_node_walk_args_t *args))
8231708Sstevel {
8241708Sstevel return (np->walk(np, param, cb));
8251708Sstevel }
8261708Sstevel
8271708Sstevel static int
drmach_node_ddi_get_prop(drmach_node_t * np,char * name,void * buf,int len)8281708Sstevel drmach_node_ddi_get_prop(drmach_node_t *np, char *name, void *buf, int len)
8291708Sstevel {
8301708Sstevel int rv = 0;
8311708Sstevel dev_info_t *ndip;
8321708Sstevel static char *fn = "drmach_node_ddi_get_prop";
8331708Sstevel
8341708Sstevel ndip = np->n_getdip(np);
8351708Sstevel if (ndip == NULL) {
8361708Sstevel cmn_err(CE_WARN, "%s: NULL dip", fn);
8371708Sstevel rv = -1;
8381708Sstevel } else if (ddi_getlongprop_buf(DDI_DEV_T_ANY, ndip,
8391708Sstevel DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, name,
8401708Sstevel (caddr_t)buf, &len) != DDI_PROP_SUCCESS) {
8411708Sstevel rv = -1;
8421708Sstevel }
8431708Sstevel
8441708Sstevel return (rv);
8451708Sstevel }
8461708Sstevel
8471708Sstevel /* ARGSUSED */
8481708Sstevel static int
drmach_node_obp_get_prop(drmach_node_t * np,char * name,void * buf,int len)8491708Sstevel drmach_node_obp_get_prop(drmach_node_t *np, char *name, void *buf, int len)
8501708Sstevel {
8511708Sstevel int rv = 0;
8521708Sstevel pnode_t nodeid;
8531708Sstevel static char *fn = "drmach_node_obp_get_prop";
8541708Sstevel
8551708Sstevel nodeid = np->get_dnode(np);
8561708Sstevel if (nodeid == OBP_NONODE) {
8571708Sstevel cmn_err(CE_WARN, "%s: invalid dnode", fn);
8581708Sstevel rv = -1;
8591708Sstevel } else if (prom_getproplen(nodeid, (caddr_t)name) < 0) {
8601708Sstevel rv = -1;
8611708Sstevel } else {
8621708Sstevel (void) prom_getprop(nodeid, (caddr_t)name, (caddr_t)buf);
8631708Sstevel }
8641708Sstevel
8651708Sstevel return (rv);
8661708Sstevel }
8671708Sstevel
8681708Sstevel static int
drmach_node_ddi_get_proplen(drmach_node_t * np,char * name,int * len)8691708Sstevel drmach_node_ddi_get_proplen(drmach_node_t *np, char *name, int *len)
8701708Sstevel {
8711708Sstevel int rv = 0;
8721708Sstevel dev_info_t *ndip;
8731708Sstevel
8741708Sstevel ndip = np->n_getdip(np);
8751708Sstevel if (ndip == NULL) {
8761708Sstevel rv = -1;
8771708Sstevel } else if (ddi_getproplen(DDI_DEV_T_ANY, ndip, DDI_PROP_DONTPASS,
87811066Srafael.vanoni@sun.com name, len) != DDI_PROP_SUCCESS) {
8791708Sstevel rv = -1;
8801708Sstevel }
8811708Sstevel
8821708Sstevel return (rv);
8831708Sstevel }
8841708Sstevel
8851708Sstevel static int
drmach_node_obp_get_proplen(drmach_node_t * np,char * name,int * len)8861708Sstevel drmach_node_obp_get_proplen(drmach_node_t *np, char *name, int *len)
8871708Sstevel {
8881708Sstevel pnode_t nodeid;
8891708Sstevel int rv;
8901708Sstevel
8911708Sstevel nodeid = np->get_dnode(np);
8921708Sstevel if (nodeid == OBP_NONODE)
8931708Sstevel rv = -1;
8941708Sstevel else {
8951708Sstevel *len = prom_getproplen(nodeid, (caddr_t)name);
8961708Sstevel rv = (*len < 0 ? -1 : 0);
8971708Sstevel }
8981708Sstevel
8991708Sstevel return (rv);
9001708Sstevel }
9011708Sstevel
9021708Sstevel static drmachid_t
drmach_node_dup(drmach_node_t * np)9031708Sstevel drmach_node_dup(drmach_node_t *np)
9041708Sstevel {
9051708Sstevel drmach_node_t *dup;
9061708Sstevel
9071708Sstevel dup = drmach_node_new();
9081708Sstevel dup->here = np->here;
9091708Sstevel dup->get_dnode = np->get_dnode;
9101708Sstevel dup->walk = np->walk;
9111708Sstevel dup->n_getdip = np->n_getdip;
9121708Sstevel dup->n_getproplen = np->n_getproplen;
9131708Sstevel dup->n_getprop = np->n_getprop;
9141708Sstevel dup->get_parent = np->get_parent;
9151708Sstevel
9161708Sstevel return (dup);
9171708Sstevel }
9181708Sstevel
9191708Sstevel /*
9201708Sstevel * drmach_array provides convenient array construction, access,
9211708Sstevel * bounds checking and array destruction logic.
9221708Sstevel */
9231708Sstevel
9241708Sstevel static drmach_array_t *
drmach_array_new(int min_index,int max_index)9251708Sstevel drmach_array_new(int min_index, int max_index)
9261708Sstevel {
9271708Sstevel drmach_array_t *arr;
9281708Sstevel
9291708Sstevel arr = kmem_zalloc(sizeof (drmach_array_t), KM_SLEEP);
9301708Sstevel
9311708Sstevel arr->arr_sz = (max_index - min_index + 1) * sizeof (void *);
9321708Sstevel if (arr->arr_sz > 0) {
9331708Sstevel arr->min_index = min_index;
9341708Sstevel arr->max_index = max_index;
9351708Sstevel
9361708Sstevel arr->arr = kmem_zalloc(arr->arr_sz, KM_SLEEP);
9371708Sstevel return (arr);
9381708Sstevel } else {
9391708Sstevel kmem_free(arr, sizeof (*arr));
9401708Sstevel return (0);
9411708Sstevel }
9421708Sstevel }
9431708Sstevel
9441708Sstevel static int
drmach_array_set(drmach_array_t * arr,int idx,drmachid_t val)9451708Sstevel drmach_array_set(drmach_array_t *arr, int idx, drmachid_t val)
9461708Sstevel {
9471708Sstevel if (idx < arr->min_index || idx > arr->max_index)
9481708Sstevel return (-1);
9491708Sstevel else {
9501708Sstevel arr->arr[idx - arr->min_index] = val;
9511708Sstevel return (0);
9521708Sstevel }
9531708Sstevel /*NOTREACHED*/
9541708Sstevel }
9551708Sstevel
9561708Sstevel static int
drmach_array_get(drmach_array_t * arr,int idx,drmachid_t * val)9571708Sstevel drmach_array_get(drmach_array_t *arr, int idx, drmachid_t *val)
9581708Sstevel {
9591708Sstevel if (idx < arr->min_index || idx > arr->max_index)
9601708Sstevel return (-1);
9611708Sstevel else {
9621708Sstevel *val = arr->arr[idx - arr->min_index];
9631708Sstevel return (0);
9641708Sstevel }
9651708Sstevel /*NOTREACHED*/
9661708Sstevel }
9671708Sstevel
9681708Sstevel static int
drmach_array_first(drmach_array_t * arr,int * idx,drmachid_t * val)9691708Sstevel drmach_array_first(drmach_array_t *arr, int *idx, drmachid_t *val)
9701708Sstevel {
9711708Sstevel int rv;
9721708Sstevel
9731708Sstevel *idx = arr->min_index;
9741708Sstevel while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
9751708Sstevel *idx += 1;
9761708Sstevel
9771708Sstevel return (rv);
9781708Sstevel }
9791708Sstevel
9801708Sstevel static int
drmach_array_next(drmach_array_t * arr,int * idx,drmachid_t * val)9811708Sstevel drmach_array_next(drmach_array_t *arr, int *idx, drmachid_t *val)
9821708Sstevel {
9831708Sstevel int rv;
9841708Sstevel
9851708Sstevel *idx += 1;
9861708Sstevel while ((rv = drmach_array_get(arr, *idx, val)) == 0 && *val == NULL)
9871708Sstevel *idx += 1;
9881708Sstevel
9891708Sstevel return (rv);
9901708Sstevel }
9911708Sstevel
9921708Sstevel static void
drmach_array_dispose(drmach_array_t * arr,void (* disposer)(drmachid_t))9931708Sstevel drmach_array_dispose(drmach_array_t *arr, void (*disposer)(drmachid_t))
9941708Sstevel {
9951708Sstevel drmachid_t val;
9961708Sstevel int idx;
9971708Sstevel int rv;
9981708Sstevel
9991708Sstevel rv = drmach_array_first(arr, &idx, &val);
10001708Sstevel while (rv == 0) {
10011708Sstevel (*disposer)(val);
10021708Sstevel
10031708Sstevel /* clear the array entry */
10041708Sstevel rv = drmach_array_set(arr, idx, NULL);
10051708Sstevel ASSERT(rv == 0);
10061708Sstevel
10071708Sstevel rv = drmach_array_next(arr, &idx, &val);
10081708Sstevel }
10091708Sstevel
10101708Sstevel kmem_free(arr->arr, arr->arr_sz);
10111708Sstevel kmem_free(arr, sizeof (*arr));
10121708Sstevel }
10131708Sstevel
10141708Sstevel
10151708Sstevel static gdcd_t *
drmach_gdcd_new()10161708Sstevel drmach_gdcd_new()
10171708Sstevel {
10181708Sstevel gdcd_t *gdcd;
10191708Sstevel
10201708Sstevel gdcd = kmem_zalloc(sizeof (gdcd_t), KM_SLEEP);
10211708Sstevel
10221708Sstevel /* read the gdcd, bail if magic or ver #s are not what is expected */
10231708Sstevel if (iosram_rd(GDCD_MAGIC, 0, sizeof (gdcd_t), (caddr_t)gdcd)) {
10241708Sstevel bail:
10251708Sstevel kmem_free(gdcd, sizeof (gdcd_t));
10261708Sstevel return (NULL);
10271708Sstevel } else if (gdcd->h.dcd_magic != GDCD_MAGIC) {
10281708Sstevel goto bail;
10291708Sstevel } else if (gdcd->h.dcd_version != DCD_VERSION) {
10301708Sstevel goto bail;
10311708Sstevel }
10321708Sstevel
10331708Sstevel return (gdcd);
10341708Sstevel }
10351708Sstevel
10361708Sstevel static void
drmach_gdcd_dispose(gdcd_t * gdcd)10371708Sstevel drmach_gdcd_dispose(gdcd_t *gdcd)
10381708Sstevel {
10391708Sstevel kmem_free(gdcd, sizeof (gdcd_t));
10401708Sstevel }
10411708Sstevel
10421708Sstevel /*ARGSUSED*/
10431708Sstevel sbd_error_t *
drmach_configure(drmachid_t id,int flags)10441708Sstevel drmach_configure(drmachid_t id, int flags)
10451708Sstevel {
10461708Sstevel drmach_device_t *dp;
10471708Sstevel dev_info_t *rdip;
10481708Sstevel sbd_error_t *err = NULL;
10491708Sstevel
10501708Sstevel /*
10511708Sstevel * On Starcat, there is no CPU driver, so it is
10521708Sstevel * not necessary to configure any CPU nodes.
10531708Sstevel */
10541708Sstevel if (DRMACH_IS_CPU_ID(id)) {
10551708Sstevel return (NULL);
10561708Sstevel }
10571708Sstevel
10581708Sstevel for (; id; ) {
10591708Sstevel dev_info_t *fdip = NULL;
10601708Sstevel
10611708Sstevel if (!DRMACH_IS_DEVICE_ID(id))
10621708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
10631708Sstevel dp = id;
10641708Sstevel
10651708Sstevel rdip = dp->node->n_getdip(dp->node);
10661708Sstevel
10671708Sstevel /*
10681708Sstevel * We held this branch earlier, so at a minimum its
10691708Sstevel * root should still be present in the device tree.
10701708Sstevel */
10711708Sstevel ASSERT(rdip);
10721708Sstevel
10731708Sstevel DRMACH_PR("drmach_configure: configuring DDI branch");
10741708Sstevel
10751708Sstevel ASSERT(e_ddi_branch_held(rdip));
10761708Sstevel if (e_ddi_branch_configure(rdip, &fdip, 0) != 0) {
10771708Sstevel if (err == NULL) {
10781708Sstevel /*
10791708Sstevel * Record first failure but don't stop
10801708Sstevel */
10811708Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
10821708Sstevel dev_info_t *dip = (fdip != NULL) ? fdip : rdip;
10831708Sstevel
10841708Sstevel (void) ddi_pathname(dip, path);
10851708Sstevel err = drerr_new(1, ESTC_DRVFAIL, path);
10861708Sstevel
10871708Sstevel kmem_free(path, MAXPATHLEN);
10881708Sstevel }
10891708Sstevel
10901708Sstevel /*
10911708Sstevel * If non-NULL, fdip is returned held and must be
10921708Sstevel * released.
10931708Sstevel */
10941708Sstevel if (fdip != NULL) {
10951708Sstevel ddi_release_devi(fdip);
10961708Sstevel }
10971708Sstevel }
10981708Sstevel
10991708Sstevel if (DRMACH_IS_MEM_ID(id)) {
11001708Sstevel drmach_mem_t *mp = id;
11011708Sstevel id = mp->next;
11021708Sstevel } else {
11031708Sstevel id = NULL;
11041708Sstevel }
11051708Sstevel }
11061708Sstevel
11071708Sstevel return (err);
11081708Sstevel }
11091708Sstevel
11101708Sstevel static sbd_error_t *
drmach_device_new(drmach_node_t * node,drmach_board_t * bp,int portid,drmachid_t * idp)11111708Sstevel drmach_device_new(drmach_node_t *node,
11121708Sstevel drmach_board_t *bp, int portid, drmachid_t *idp)
11131708Sstevel {
11141708Sstevel int i, rv, device_id, unum;
11151708Sstevel char name[OBP_MAXDRVNAME];
11161708Sstevel drmach_device_t proto;
11171708Sstevel
11181708Sstevel rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
11191708Sstevel if (rv) {
11201708Sstevel sbd_error_t *err;
11211708Sstevel
11221708Sstevel /* every node is expected to have a name */
11231708Sstevel err = drerr_new(1, ESTC_GETPROP,
112411066Srafael.vanoni@sun.com "dip: 0x%p: property %s",
112511066Srafael.vanoni@sun.com node->n_getdip(node), OBP_NAME);
11261708Sstevel
11271708Sstevel return (err);
11281708Sstevel }
11291708Sstevel
11301708Sstevel i = drmach_name2type_idx(name);
11311708Sstevel
11321708Sstevel if (i < 0 || strcmp(name, "cmp") == 0) {
11331708Sstevel /*
11341708Sstevel * Not a node of interest to dr - including "cmp",
11351708Sstevel * but it is in drmach_name2type[], which lets gptwocfg
11361708Sstevel * driver to check if node is OBP created.
11371708Sstevel */
11381708Sstevel *idp = (drmachid_t)0;
11391708Sstevel return (NULL);
11401708Sstevel }
11411708Sstevel
11421708Sstevel /*
11431708Sstevel * Derive a best-guess unit number from the portid value.
11441708Sstevel * Some drmach_*_new constructors (drmach_pci_new, for example)
11451708Sstevel * will overwrite the prototype unum value with one that is more
11461708Sstevel * appropriate for the device.
11471708Sstevel */
11481708Sstevel device_id = portid & 0x1f;
11491708Sstevel if (device_id < 4)
11501708Sstevel unum = device_id;
11511708Sstevel else if (device_id == 8) {
11521708Sstevel unum = 0;
11531708Sstevel } else if (device_id == 9) {
11541708Sstevel unum = 1;
11551708Sstevel } else if (device_id == 0x1c) {
11561708Sstevel unum = 0;
11571708Sstevel } else if (device_id == 0x1d) {
11581708Sstevel unum = 1;
11591708Sstevel } else {
11601708Sstevel return (DRMACH_INTERNAL_ERROR());
11611708Sstevel }
11621708Sstevel
11631708Sstevel bzero(&proto, sizeof (proto));
11641708Sstevel proto.type = drmach_name2type[i].type;
11651708Sstevel proto.bp = bp;
11661708Sstevel proto.node = node;
11671708Sstevel proto.portid = portid;
11681708Sstevel proto.unum = unum;
11691708Sstevel
11701708Sstevel return (drmach_name2type[i].new(&proto, idp));
11711708Sstevel }
11721708Sstevel
11731708Sstevel static void
drmach_device_dispose(drmachid_t id)11741708Sstevel drmach_device_dispose(drmachid_t id)
11751708Sstevel {
11761708Sstevel drmach_device_t *self = id;
11771708Sstevel
11781708Sstevel self->cm.dispose(id);
11791708Sstevel }
11801708Sstevel
11811708Sstevel static drmach_board_t *
drmach_board_new(int bnum)11821708Sstevel drmach_board_new(int bnum)
11831708Sstevel {
11841708Sstevel static sbd_error_t *drmach_board_release(drmachid_t);
11851708Sstevel static sbd_error_t *drmach_board_status(drmachid_t, drmach_status_t *);
11861708Sstevel
11871708Sstevel drmach_board_t *bp;
11881708Sstevel
11891708Sstevel bp = kmem_zalloc(sizeof (drmach_board_t), KM_SLEEP);
11901708Sstevel
11911708Sstevel bp->cm.isa = (void *)drmach_board_new;
11921708Sstevel bp->cm.release = drmach_board_release;
11931708Sstevel bp->cm.status = drmach_board_status;
11941708Sstevel
11951708Sstevel (void) drmach_board_name(bnum, bp->cm.name, sizeof (bp->cm.name));
11961708Sstevel
11971708Sstevel bp->bnum = bnum;
11981708Sstevel bp->devices = NULL;
11991708Sstevel bp->tree = drmach_node_new();
12001708Sstevel
120111311SSurya.Prakki@Sun.COM (void) drmach_array_set(drmach_boards, bnum, bp);
12021708Sstevel return (bp);
12031708Sstevel }
12041708Sstevel
12051708Sstevel static void
drmach_board_dispose(drmachid_t id)12061708Sstevel drmach_board_dispose(drmachid_t id)
12071708Sstevel {
12081708Sstevel drmach_board_t *bp;
12091708Sstevel
12101708Sstevel ASSERT(DRMACH_IS_BOARD_ID(id));
12111708Sstevel bp = id;
12121708Sstevel
12131708Sstevel if (bp->tree)
12141708Sstevel drmach_node_dispose(bp->tree);
12151708Sstevel
12161708Sstevel if (bp->devices)
12171708Sstevel drmach_array_dispose(bp->devices, drmach_device_dispose);
12181708Sstevel
12191708Sstevel kmem_free(bp, sizeof (*bp));
12201708Sstevel }
12211708Sstevel
12221708Sstevel static sbd_error_t *
drmach_board_status(drmachid_t id,drmach_status_t * stat)12231708Sstevel drmach_board_status(drmachid_t id, drmach_status_t *stat)
12241708Sstevel {
12251708Sstevel sbd_error_t *err = NULL;
12261708Sstevel drmach_board_t *bp;
12271708Sstevel caddr_t obufp;
12281708Sstevel dr_showboard_t shb;
12291708Sstevel
12301708Sstevel if (!DRMACH_IS_BOARD_ID(id))
12311708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
12321708Sstevel
12331708Sstevel bp = id;
12341708Sstevel
12351708Sstevel /*
12361708Sstevel * we need to know if the board's connected before
12371708Sstevel * issuing a showboard message. If it's connected, we just
12381708Sstevel * reply with status composed of cached info
12391708Sstevel */
12401708Sstevel
12411708Sstevel if (!bp->connected) {
12421708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
12431708Sstevel err = drmach_mbox_trans(DRMSG_SHOWBOARD, bp->bnum, obufp,
124411066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)&shb,
124511066Srafael.vanoni@sun.com sizeof (dr_showboard_t));
12461708Sstevel
12471708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
12481708Sstevel if (err)
12491708Sstevel return (err);
12501708Sstevel
12511708Sstevel bp->connected = (shb.bd_assigned && shb.bd_active);
125211311SSurya.Prakki@Sun.COM (void) strncpy(bp->type, shb.board_type, sizeof (bp->type));
12531708Sstevel stat->assigned = bp->assigned = shb.bd_assigned;
12541708Sstevel stat->powered = bp->powered = shb.power_on;
12551708Sstevel stat->empty = bp->empty = shb.slot_empty;
12561708Sstevel
12571708Sstevel switch (shb.test_status) {
12581708Sstevel case DR_TEST_STATUS_UNKNOWN:
12591708Sstevel case DR_TEST_STATUS_IPOST:
12601708Sstevel case DR_TEST_STATUS_ABORTED:
12611708Sstevel stat->cond = bp->cond = SBD_COND_UNKNOWN;
12621708Sstevel break;
12631708Sstevel case DR_TEST_STATUS_PASSED:
12641708Sstevel stat->cond = bp->cond = SBD_COND_OK;
12651708Sstevel break;
12661708Sstevel case DR_TEST_STATUS_FAILED:
12671708Sstevel stat->cond = bp->cond = SBD_COND_FAILED;
12681708Sstevel break;
12691708Sstevel default:
12701708Sstevel stat->cond = bp->cond = SBD_COND_UNKNOWN;
12711708Sstevel DRMACH_PR("Unknown test status=0x%x from SC\n",
127211066Srafael.vanoni@sun.com shb.test_status);
12731708Sstevel break;
12741708Sstevel
12751708Sstevel }
12761708Sstevel
127711311SSurya.Prakki@Sun.COM (void) strncpy(stat->type, shb.board_type, sizeof (stat->type));
127811311SSurya.Prakki@Sun.COM (void) snprintf(stat->info, sizeof (stat->info),
127911311SSurya.Prakki@Sun.COM "Test Level=%d", shb.test_level);
12801708Sstevel } else {
12811708Sstevel stat->assigned = bp->assigned;
12821708Sstevel stat->powered = bp->powered;
12831708Sstevel stat->empty = bp->empty;
12841708Sstevel stat->cond = bp->cond;
128511311SSurya.Prakki@Sun.COM (void) strncpy(stat->type, bp->type, sizeof (stat->type));
12861708Sstevel }
12871708Sstevel
12881708Sstevel stat->busy = 0; /* assume not busy */
12891708Sstevel stat->configured = 0; /* assume not configured */
12901708Sstevel if (bp->devices) {
12911708Sstevel int rv;
12921708Sstevel int d_idx;
12931708Sstevel drmachid_t d_id;
12941708Sstevel
12951708Sstevel rv = drmach_array_first(bp->devices, &d_idx, &d_id);
12961708Sstevel while (rv == 0) {
12971708Sstevel drmach_status_t d_stat;
12981708Sstevel
12991708Sstevel err = drmach_i_status(d_id, &d_stat);
13001708Sstevel if (err)
13011708Sstevel break;
13021708Sstevel
13031708Sstevel stat->busy |= d_stat.busy;
13041708Sstevel stat->configured |= d_stat.configured;
13051708Sstevel
13061708Sstevel rv = drmach_array_next(bp->devices, &d_idx, &d_id);
13071708Sstevel }
13081708Sstevel }
13091708Sstevel
13101708Sstevel return (err);
13111708Sstevel }
13121708Sstevel
13131708Sstevel typedef struct drmach_msglist {
13141708Sstevel kcondvar_t s_cv; /* condvar for sending msg */
13151708Sstevel kmutex_t s_lock; /* mutex for sending */
13161708Sstevel kcondvar_t g_cv; /* condvar for getting reply */
13171708Sstevel kmutex_t g_lock; /* mutex for getting reply */
13181708Sstevel struct drmach_msglist *prev; /* link to previous entry */
13191708Sstevel struct drmach_msglist *next; /* link to next entry */
13201708Sstevel struct drmach_msglist *link; /* link to related entry */
13211708Sstevel caddr_t o_buf; /* address of output buffer */
13221708Sstevel caddr_t i_buf; /* address of input buffer */
13231708Sstevel uint32_t o_buflen; /* output buffer length */
13241708Sstevel uint32_t i_buflen; /* input buffer length */
13251708Sstevel uint32_t msgid; /* message identifier */
13261708Sstevel int o_nretry; /* number of sending retries */
13271708Sstevel int f_error; /* mailbox framework error */
13281708Sstevel uint8_t e_code; /* error code returned by SC */
13291708Sstevel uint8_t p_flag :1, /* successfully putmsg */
13301708Sstevel m_reply :1, /* msg reply received */
13311708Sstevel unused :6;
13321708Sstevel } drmach_msglist_t;
13331708Sstevel
13341708Sstevel kmutex_t drmach_g_mbox_mutex; /* mutex for mailbox globals */
13351708Sstevel kmutex_t drmach_ri_mbox_mutex; /* mutex for mailbox reinit */
13361708Sstevel kmutex_t drmach_msglist_mutex; /* mutex for message list */
13371708Sstevel drmach_msglist_t *drmach_msglist_first; /* first entry in msg list */
13381708Sstevel drmach_msglist_t *drmach_msglist_last; /* last entry in msg list */
13391708Sstevel uint32_t drmach_msgid; /* current message id */
13401708Sstevel kthread_t *drmach_getmsg_thread; /* ptr to getmsg thread */
13411708Sstevel volatile int drmach_getmsg_thread_run; /* run flag for getmsg thr */
13421708Sstevel kmutex_t drmach_sendmsg_mutex; /* mutex for sendmsg cv */
13431708Sstevel kcondvar_t drmach_sendmsg_cv; /* signaled to send new msg */
13441708Sstevel kthread_t *drmach_sendmsg_thread; /* ptr to sendmsg thread */
13451708Sstevel volatile int drmach_sendmsg_thread_run; /* run flag for sendmsg */
13461708Sstevel int drmach_mbox_istate; /* mailbox init state */
13471708Sstevel int drmach_mbox_iflag; /* set if init'd with SC */
13481708Sstevel int drmach_mbox_ipending; /* set if reinit scheduled */
13491708Sstevel
13501708Sstevel /*
13511708Sstevel * Timeout values (in seconds) used when waiting for replies (from the SC) to
13521708Sstevel * requests that we sent. Since we only receive boardevent messages, and they
13531708Sstevel * are events rather than replies, there is no boardevent timeout.
13541708Sstevel */
13551708Sstevel int drmach_to_mbxinit = 60; /* 1 minute */
13561708Sstevel int drmach_to_assign = 60; /* 1 minute */
13571708Sstevel int drmach_to_unassign = 60; /* 1 minute */
13581708Sstevel int drmach_to_claim = 3600; /* 1 hour */
13591708Sstevel int drmach_to_unclaim = 3600; /* 1 hour */
13601708Sstevel int drmach_to_poweron = 480; /* 8 minutes */
13611708Sstevel int drmach_to_poweroff = 480; /* 8 minutes */
13621708Sstevel int drmach_to_testboard = 43200; /* 12 hours */
13631708Sstevel int drmach_to_aborttest = 180; /* 3 minutes */
13641708Sstevel int drmach_to_showboard = 180; /* 3 minutes */
13651708Sstevel int drmach_to_unconfig = 180; /* 3 minutes */
13661708Sstevel
13671708Sstevel /*
13681708Sstevel * Delay (in seconds) used after receiving a non-transient error indication from
13691708Sstevel * an mboxsc_getmsg call in the thread that loops waiting for incoming messages.
13701708Sstevel */
13711708Sstevel int drmach_mbxerr_delay = 15; /* 15 seconds */
13721708Sstevel
13731708Sstevel /*
13741708Sstevel * Timeout values (in milliseconds) for mboxsc_putmsg and mboxsc_getmsg calls.
13751708Sstevel */
13761708Sstevel clock_t drmach_to_putmsg; /* set in drmach_mbox_init */
13771708Sstevel clock_t drmach_to_getmsg = 31000; /* 31 seconds */
13781708Sstevel
13791708Sstevel /*
13801708Sstevel * Normally, drmach_to_putmsg is set dynamically during initialization in
13811708Sstevel * drmach_mbox_init. This has the potentially undesirable side effect of
13821708Sstevel * clobbering any value that might have been set in /etc/system. To prevent
13831708Sstevel * dynamic setting of drmach_to_putmsg (thereby allowing it to be tuned in
13841708Sstevel * /etc/system), set drmach_use_tuned_putmsg_to to 1.
13851708Sstevel */
13861708Sstevel int drmach_use_tuned_putmsg_to = 0;
13871708Sstevel
13881708Sstevel
13891708Sstevel /* maximum conceivable message size for future mailbox protocol versions */
13901708Sstevel #define DRMACH_MAX_MBOX_MSG_SIZE 4096
13911708Sstevel
13921708Sstevel /*ARGSUSED*/
13931708Sstevel void
drmach_mbox_prmsg(dr_mbox_msg_t * mbp,int dir)13941708Sstevel drmach_mbox_prmsg(dr_mbox_msg_t *mbp, int dir)
13951708Sstevel {
13961708Sstevel int i, j;
13971708Sstevel dr_memregs_t *memregs;
13981708Sstevel dr_proto_hdr_t *php = &mbp->p_hdr;
13991708Sstevel dr_msg_t *mp = &mbp->msgdata;
14001708Sstevel
14011708Sstevel #ifdef DEBUG
14021708Sstevel switch (php->command) {
14031708Sstevel case DRMSG_BOARDEVENT:
14041708Sstevel if (dir) {
14051708Sstevel DRMACH_PR("ERROR!! outgoing BOARDEVENT\n");
14061708Sstevel } else {
14071708Sstevel DRMACH_PR("BOARDEVENT received:\n");
14081708Sstevel DRMACH_PR("init=%d ins=%d rem=%d asgn=%d\n",
140911066Srafael.vanoni@sun.com mp->dm_be.initialized,
141011066Srafael.vanoni@sun.com mp->dm_be.board_insertion,
141111066Srafael.vanoni@sun.com mp->dm_be.board_removal,
141211066Srafael.vanoni@sun.com mp->dm_be.slot_assign);
14131708Sstevel DRMACH_PR("unasgn=%d avail=%d unavail=%d\n",
141411066Srafael.vanoni@sun.com mp->dm_be.slot_unassign,
141511066Srafael.vanoni@sun.com mp->dm_be.slot_avail,
141611066Srafael.vanoni@sun.com mp->dm_be.slot_unavail);
14171708Sstevel }
14181708Sstevel break;
14191708Sstevel case DRMSG_MBOX_INIT:
14201708Sstevel if (dir) {
14211708Sstevel DRMACH_PR("MBOX_INIT Request:\n");
14221708Sstevel } else {
14231708Sstevel DRMACH_PR("MBOX_INIT Reply:\n");
14241708Sstevel }
14251708Sstevel break;
14261708Sstevel case DRMSG_ASSIGN:
14271708Sstevel if (dir) {
14281708Sstevel DRMACH_PR("ASSIGN Request:\n");
14291708Sstevel } else {
14301708Sstevel DRMACH_PR("ASSIGN Reply:\n");
14311708Sstevel }
14321708Sstevel break;
14331708Sstevel case DRMSG_UNASSIGN:
14341708Sstevel if (dir) {
14351708Sstevel DRMACH_PR("UNASSIGN Request:\n");
14361708Sstevel } else {
14371708Sstevel DRMACH_PR("UNASSIGN Reply:\n");
14381708Sstevel }
14391708Sstevel break;
14401708Sstevel case DRMSG_CLAIM:
14411708Sstevel if (!dir) {
14421708Sstevel DRMACH_PR("CLAIM Reply:\n");
14431708Sstevel break;
14441708Sstevel }
14451708Sstevel
14461708Sstevel DRMACH_PR("CLAIM Request:\n");
14471708Sstevel for (i = 0; i < 18; ++i) {
14481708Sstevel DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
144911066Srafael.vanoni@sun.com mp->dm_cr.mem_slice[i].valid,
145011066Srafael.vanoni@sun.com mp->dm_cr.mem_slice[i].slice);
14511708Sstevel memregs = &(mp->dm_cr.mem_regs[i]);
14521708Sstevel for (j = 0; j < S0_LPORT_COUNT; j++) {
14531708Sstevel DRMACH_PR(" MC %2d: "
145411066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx, "
145511066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx\n", j,
145611066Srafael.vanoni@sun.com 0, DRMACH_MCREG_TO_U64(
145711066Srafael.vanoni@sun.com memregs->madr[j][0]),
145811066Srafael.vanoni@sun.com 1, DRMACH_MCREG_TO_U64(
145911066Srafael.vanoni@sun.com memregs->madr[j][1]));
14601708Sstevel DRMACH_PR(" : "
146111066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx, "
146211066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx\n",
146311066Srafael.vanoni@sun.com 2, DRMACH_MCREG_TO_U64(
146411066Srafael.vanoni@sun.com memregs->madr[j][2]),
146511066Srafael.vanoni@sun.com 3, DRMACH_MCREG_TO_U64(
146611066Srafael.vanoni@sun.com memregs->madr[j][3]));
14671708Sstevel }
14681708Sstevel }
14691708Sstevel break;
14701708Sstevel case DRMSG_UNCLAIM:
14711708Sstevel if (!dir) {
14721708Sstevel DRMACH_PR("UNCLAIM Reply:\n");
14731708Sstevel break;
14741708Sstevel }
14751708Sstevel
14761708Sstevel DRMACH_PR("UNCLAIM Request:\n");
14771708Sstevel for (i = 0; i < 18; ++i) {
14781708Sstevel DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
147911066Srafael.vanoni@sun.com mp->dm_ur.mem_slice[i].valid,
148011066Srafael.vanoni@sun.com mp->dm_ur.mem_slice[i].slice);
14811708Sstevel memregs = &(mp->dm_ur.mem_regs[i]);
14821708Sstevel for (j = 0; j < S0_LPORT_COUNT; j++) {
14831708Sstevel DRMACH_PR(" MC %2d: "
148411066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx, "
148511066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx\n", j,
148611066Srafael.vanoni@sun.com 0, DRMACH_MCREG_TO_U64(
148711066Srafael.vanoni@sun.com memregs->madr[j][0]),
148811066Srafael.vanoni@sun.com 1, DRMACH_MCREG_TO_U64(
148911066Srafael.vanoni@sun.com memregs->madr[j][1]));
14901708Sstevel DRMACH_PR(" : "
149111066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx, "
149211066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx\n",
149311066Srafael.vanoni@sun.com 2, DRMACH_MCREG_TO_U64(
149411066Srafael.vanoni@sun.com memregs->madr[j][2]),
149511066Srafael.vanoni@sun.com 3, DRMACH_MCREG_TO_U64(
149611066Srafael.vanoni@sun.com memregs->madr[j][3]));
14971708Sstevel }
14981708Sstevel }
14991708Sstevel DRMACH_PR(" mem_clear=%d\n", mp->dm_ur.mem_clear);
15001708Sstevel break;
15011708Sstevel case DRMSG_UNCONFIG:
15021708Sstevel if (!dir) {
15031708Sstevel DRMACH_PR("UNCONFIG Reply:\n");
15041708Sstevel break;
15051708Sstevel }
15061708Sstevel
15071708Sstevel DRMACH_PR("UNCONFIG Request:\n");
15081708Sstevel for (i = 0; i < 18; ++i) {
15091708Sstevel DRMACH_PR("exp%d: val=%d slice=0x%x\n", i,
151011066Srafael.vanoni@sun.com mp->dm_uc.mem_slice[i].valid,
151111066Srafael.vanoni@sun.com mp->dm_uc.mem_slice[i].slice);
15121708Sstevel memregs = &(mp->dm_uc.mem_regs[i]);
15131708Sstevel for (j = 0; j < S0_LPORT_COUNT; j++) {
15141708Sstevel DRMACH_PR(" MC %2d: "
151511066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx, "
151611066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx\n", j,
151711066Srafael.vanoni@sun.com 0, DRMACH_MCREG_TO_U64(
151811066Srafael.vanoni@sun.com memregs->madr[j][0]),
151911066Srafael.vanoni@sun.com 1, DRMACH_MCREG_TO_U64(
152011066Srafael.vanoni@sun.com memregs->madr[j][1]));
15211708Sstevel DRMACH_PR(" : "
152211066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx, "
152311066Srafael.vanoni@sun.com "MADR[%d] = 0x%lx\n",
152411066Srafael.vanoni@sun.com 2, DRMACH_MCREG_TO_U64(
152511066Srafael.vanoni@sun.com memregs->madr[j][2]),
152611066Srafael.vanoni@sun.com 3, DRMACH_MCREG_TO_U64(
152711066Srafael.vanoni@sun.com memregs->madr[j][3]));
15281708Sstevel }
15291708Sstevel }
15301708Sstevel break;
15311708Sstevel case DRMSG_POWERON:
15321708Sstevel if (dir) {
15331708Sstevel DRMACH_PR("POWERON Request:\n");
15341708Sstevel } else {
15351708Sstevel DRMACH_PR("POWERON Reply:\n");
15361708Sstevel }
15371708Sstevel break;
15381708Sstevel case DRMSG_POWEROFF:
15391708Sstevel if (dir) {
15401708Sstevel DRMACH_PR("POWEROFF Request:\n");
15411708Sstevel } else {
15421708Sstevel DRMACH_PR("POWEROFF Reply:\n");
15431708Sstevel }
15441708Sstevel break;
15451708Sstevel case DRMSG_TESTBOARD:
15461708Sstevel if (dir) {
15471708Sstevel DRMACH_PR("TESTBOARD Request:\n");
15481708Sstevel DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
154911066Srafael.vanoni@sun.com mp->dm_tb.memaddrhi,
155011066Srafael.vanoni@sun.com mp->dm_tb.memaddrlo);
15511708Sstevel DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
155211066Srafael.vanoni@sun.com mp->dm_tb.memlen, mp->dm_tb.cpu_portid);
15531708Sstevel DRMACH_PR("\tforce=0x%x imm=0x%x\n",
155411066Srafael.vanoni@sun.com mp->dm_tb.force, mp->dm_tb.immediate);
15551708Sstevel } else {
15561708Sstevel DRMACH_PR("TESTBOARD Reply:\n");
15571708Sstevel DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
155811066Srafael.vanoni@sun.com mp->dm_tr.memaddrhi,
155911066Srafael.vanoni@sun.com mp->dm_tr.memaddrlo);
15601708Sstevel DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
156111066Srafael.vanoni@sun.com mp->dm_tr.memlen, mp->dm_tr.cpu_portid);
15621708Sstevel DRMACH_PR("\trecovered=0x%x test status=0x%x\n",
156311066Srafael.vanoni@sun.com mp->dm_tr.cpu_recovered,
156411066Srafael.vanoni@sun.com mp->dm_tr.test_status);
15651708Sstevel
15661708Sstevel }
15671708Sstevel break;
15681708Sstevel case DRMSG_ABORT_TEST:
15691708Sstevel if (dir) {
15701708Sstevel DRMACH_PR("ABORT_TEST Request:\n");
15711708Sstevel } else {
15721708Sstevel DRMACH_PR("ABORT_TEST Reply:\n");
15731708Sstevel }
15741708Sstevel
15751708Sstevel DRMACH_PR("\tmemaddrhi=0x%x memaddrlo=0x%x ",
157611066Srafael.vanoni@sun.com mp->dm_ta.memaddrhi,
157711066Srafael.vanoni@sun.com mp->dm_ta.memaddrlo);
15781708Sstevel DRMACH_PR("memlen=0x%x cpu_portid=0x%x\n",
157911066Srafael.vanoni@sun.com mp->dm_ta.memlen, mp->dm_ta.cpu_portid);
15801708Sstevel break;
15811708Sstevel case DRMSG_SHOWBOARD:
15821708Sstevel if (dir) {
15831708Sstevel DRMACH_PR("SHOWBOARD Request:\n");
15841708Sstevel } else {
15851708Sstevel DRMACH_PR("SHOWBOARD Reply:\n");
15861708Sstevel
15871708Sstevel DRMACH_PR(": empty=%d power=%d assigned=%d",
158811066Srafael.vanoni@sun.com mp->dm_sb.slot_empty,
158911066Srafael.vanoni@sun.com mp->dm_sb.power_on,
159011066Srafael.vanoni@sun.com mp->dm_sb.bd_assigned);
15911708Sstevel DRMACH_PR(": active=%d t_status=%d t_level=%d ",
159211066Srafael.vanoni@sun.com mp->dm_sb.bd_active,
159311066Srafael.vanoni@sun.com mp->dm_sb.test_status,
159411066Srafael.vanoni@sun.com mp->dm_sb.test_level);
15951708Sstevel DRMACH_PR(": type=%s ", mp->dm_sb.board_type);
15961708Sstevel }
15971708Sstevel break;
15981708Sstevel default:
15991708Sstevel DRMACH_PR("Unknown message type\n");
16001708Sstevel break;
16011708Sstevel }
16021708Sstevel
16031708Sstevel DRMACH_PR("dr hdr:\n\tid=0x%x vers=0x%x cmd=0x%x exp=0x%x slot=0x%x\n",
160411066Srafael.vanoni@sun.com php->message_id, php->drproto_version, php->command,
160511066Srafael.vanoni@sun.com php->expbrd, php->slot);
16061708Sstevel #endif
16071708Sstevel DRMACH_PR("\treply_status=0x%x error_code=0x%x\n", php->reply_status,
160811066Srafael.vanoni@sun.com php->error_code);
16091708Sstevel }
16101708Sstevel
16111708Sstevel /*
16121708Sstevel * Callback function passed to taskq_dispatch when a mailbox reinitialization
16131708Sstevel * handshake needs to be scheduled. The handshake can't be performed by the
16141708Sstevel * thread that determines it is needed, in most cases, so this function is
16151708Sstevel * dispatched on the system-wide taskq pool of threads. Failure is reported but
16161708Sstevel * otherwise ignored, since any situation that requires a mailbox initialization
16171708Sstevel * handshake will continue to request the handshake until it succeeds.
16181708Sstevel */
16191708Sstevel static void
drmach_mbox_reinit(void * unused)16201708Sstevel drmach_mbox_reinit(void *unused)
16211708Sstevel {
16221708Sstevel _NOTE(ARGUNUSED(unused))
16231708Sstevel
16241708Sstevel caddr_t obufp = NULL;
16251708Sstevel sbd_error_t *serr = NULL;
16261708Sstevel
16271708Sstevel DRMACH_PR("scheduled mailbox reinit running\n");
16281708Sstevel
16291708Sstevel mutex_enter(&drmach_ri_mbox_mutex);
16301708Sstevel mutex_enter(&drmach_g_mbox_mutex);
16311708Sstevel if (drmach_mbox_iflag == 0) {
16321708Sstevel /* need to initialize the mailbox */
16331708Sstevel mutex_exit(&drmach_g_mbox_mutex);
16341708Sstevel
16351708Sstevel cmn_err(CE_NOTE, "!reinitializing DR mailbox");
16361708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
16371708Sstevel serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
163811066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
16391708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
16401708Sstevel
16411708Sstevel if (serr) {
16421708Sstevel cmn_err(CE_WARN,
164311066Srafael.vanoni@sun.com "mbox_init: MBOX_INIT failed ecode=0x%x",
164411066Srafael.vanoni@sun.com serr->e_code);
16451708Sstevel sbd_err_clear(&serr);
16461708Sstevel }
16471708Sstevel mutex_enter(&drmach_g_mbox_mutex);
16481708Sstevel if (!serr) {
16491708Sstevel drmach_mbox_iflag = 1;
16501708Sstevel }
16511708Sstevel }
16521708Sstevel drmach_mbox_ipending = 0;
16531708Sstevel mutex_exit(&drmach_g_mbox_mutex);
16541708Sstevel mutex_exit(&drmach_ri_mbox_mutex);
16551708Sstevel }
16561708Sstevel
16571708Sstevel /*
16581708Sstevel * To ensure sufficient compatibility with future versions of the DR mailbox
16591708Sstevel * protocol, we use a buffer that is large enough to receive the largest message
16601708Sstevel * that could possibly be sent to us. However, since that ends up being fairly
16611708Sstevel * large, allocating it on the stack is a bad idea. Fortunately, this function
16621708Sstevel * does not need to be MT-safe since it is only invoked by the mailbox
16631708Sstevel * framework, which will never invoke it multiple times concurrently. Since
16641708Sstevel * that is the case, we can use a static buffer.
16651708Sstevel */
16661708Sstevel void
drmach_mbox_event(void)16671708Sstevel drmach_mbox_event(void)
16681708Sstevel {
16691708Sstevel static uint8_t buf[DRMACH_MAX_MBOX_MSG_SIZE];
16701708Sstevel dr_mbox_msg_t *msg = (dr_mbox_msg_t *)buf;
16711708Sstevel int err;
16721708Sstevel uint32_t type = MBOXSC_MSG_EVENT;
16731708Sstevel uint32_t command = DRMSG_BOARDEVENT;
16741708Sstevel uint64_t transid = 0;
16751708Sstevel uint32_t length = DRMACH_MAX_MBOX_MSG_SIZE;
16761708Sstevel char *hint = "";
16771708Sstevel int logsys = 0;
16781708Sstevel
16791708Sstevel do {
168011066Srafael.vanoni@sun.com err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
168111066Srafael.vanoni@sun.com &length, (void *)msg, 0);
16821708Sstevel } while (err == EAGAIN);
16831708Sstevel
16841708Sstevel /* don't try to interpret anything with the wrong version number */
16851708Sstevel if ((err == 0) && (msg->p_hdr.drproto_version != DRMBX_VERSION)) {
16861708Sstevel cmn_err(CE_WARN, "mailbox version mismatch 0x%x vs 0x%x",
168711066Srafael.vanoni@sun.com msg->p_hdr.drproto_version, DRMBX_VERSION);
16881708Sstevel mutex_enter(&drmach_g_mbox_mutex);
16891708Sstevel drmach_mbox_iflag = 0;
16901708Sstevel /* schedule a reinit handshake if one isn't pending */
16911708Sstevel if (!drmach_mbox_ipending) {
16921708Sstevel if (taskq_dispatch(system_taskq, drmach_mbox_reinit,
169311066Srafael.vanoni@sun.com NULL, TQ_NOSLEEP) != NULL) {
16941708Sstevel drmach_mbox_ipending = 1;
16951708Sstevel } else {
16961708Sstevel cmn_err(CE_WARN,
169711066Srafael.vanoni@sun.com "failed to schedule mailbox reinit");
16981708Sstevel }
16991708Sstevel }
17001708Sstevel mutex_exit(&drmach_g_mbox_mutex);
17011708Sstevel return;
17021708Sstevel }
17031708Sstevel
17041708Sstevel if ((err != 0) || (msg->p_hdr.reply_status != DRMSG_REPLY_OK)) {
17051708Sstevel cmn_err(CE_WARN,
170611066Srafael.vanoni@sun.com "Unsolicited mboxsc_getmsg failed: err=0x%x code=0x%x",
170711066Srafael.vanoni@sun.com err, msg->p_hdr.error_code);
17081708Sstevel } else {
17091708Sstevel dr_boardevent_t *be;
17101708Sstevel be = (dr_boardevent_t *)&msg->msgdata;
17111708Sstevel
17121708Sstevel /* check for initialization event */
17131708Sstevel if (be->initialized) {
17141708Sstevel mutex_enter(&drmach_g_mbox_mutex);
17151708Sstevel drmach_mbox_iflag = 0;
17161708Sstevel /* schedule a reinit handshake if one isn't pending */
17171708Sstevel if (!drmach_mbox_ipending) {
17181708Sstevel if (taskq_dispatch(system_taskq,
171911066Srafael.vanoni@sun.com drmach_mbox_reinit, NULL, TQ_NOSLEEP)
172011066Srafael.vanoni@sun.com != NULL) {
17211708Sstevel drmach_mbox_ipending = 1;
17221708Sstevel } else {
172311066Srafael.vanoni@sun.com cmn_err(CE_WARN, "failed to schedule "
172411066Srafael.vanoni@sun.com "mailbox reinit");
17251708Sstevel }
17261708Sstevel }
17271708Sstevel mutex_exit(&drmach_g_mbox_mutex);
17281708Sstevel cmn_err(CE_NOTE, "!Mailbox Init event received");
17291708Sstevel }
17301708Sstevel
17311708Sstevel /* anything else will be a log_sysevent call */
17321708Sstevel
17331708Sstevel if (be->board_insertion) {
17341708Sstevel DRMACH_PR("Board Insertion event received");
17351708Sstevel hint = DR_HINT_INSERT;
17361708Sstevel logsys++;
17371708Sstevel }
17381708Sstevel if (be->board_removal) {
17391708Sstevel DRMACH_PR("Board Removal event received");
17401708Sstevel hint = DR_HINT_REMOVE;
17411708Sstevel logsys++;
17421708Sstevel }
17431708Sstevel if (be->slot_assign) {
17441708Sstevel DRMACH_PR("Slot Assign event received");
17451708Sstevel logsys++;
17461708Sstevel }
17471708Sstevel if (be->slot_unassign) {
17481708Sstevel DRMACH_PR("Slot Unassign event received");
17491708Sstevel logsys++;
17501708Sstevel }
17511708Sstevel if (be->slot_avail) {
17521708Sstevel DRMACH_PR("Slot Available event received");
17531708Sstevel logsys++;
17541708Sstevel }
17551708Sstevel if (be->slot_unavail) {
17561708Sstevel DRMACH_PR("Slot Unavailable event received");
17571708Sstevel logsys++;
17581708Sstevel }
17591708Sstevel if (be->power_on) {
17601708Sstevel DRMACH_PR("Power ON event received");
17611708Sstevel logsys++;
17621708Sstevel }
17631708Sstevel if (be->power_off) {
17641708Sstevel DRMACH_PR("Power OFF event received");
17651708Sstevel logsys++;
17661708Sstevel }
17671708Sstevel
17681708Sstevel if (logsys)
176911311SSurya.Prakki@Sun.COM (void) drmach_log_sysevent(
177011066Srafael.vanoni@sun.com DRMACH_EXPSLOT2BNUM(msg->p_hdr.expbrd,
177111066Srafael.vanoni@sun.com msg->p_hdr.slot), hint, SE_NOSLEEP, 1);
17721708Sstevel }
17731708Sstevel }
17741708Sstevel
17751708Sstevel static uint32_t
drmach_get_msgid()17761708Sstevel drmach_get_msgid()
17771708Sstevel {
17781708Sstevel uint32_t rv;
17791708Sstevel mutex_enter(&drmach_msglist_mutex);
17801708Sstevel if (!(++drmach_msgid))
17811708Sstevel ++drmach_msgid;
17821708Sstevel rv = drmach_msgid;
17831708Sstevel mutex_exit(&drmach_msglist_mutex);
17841708Sstevel return (rv);
17851708Sstevel }
17861708Sstevel
17871708Sstevel /*
17881708Sstevel * unlink an entry from the message transaction list
17891708Sstevel *
17901708Sstevel * caller must hold drmach_msglist_mutex
17911708Sstevel */
17921708Sstevel void
drmach_msglist_unlink(drmach_msglist_t * entry)17931708Sstevel drmach_msglist_unlink(drmach_msglist_t *entry)
17941708Sstevel {
17951708Sstevel ASSERT(mutex_owned(&drmach_msglist_mutex));
17961708Sstevel if (entry->prev) {
17971708Sstevel entry->prev->next = entry->next;
17981708Sstevel if (entry->next)
17991708Sstevel entry->next->prev = entry->prev;
18001708Sstevel } else {
18011708Sstevel drmach_msglist_first = entry->next;
18021708Sstevel if (entry->next)
18031708Sstevel entry->next->prev = NULL;
18041708Sstevel }
18051708Sstevel if (entry == drmach_msglist_last) {
18061708Sstevel drmach_msglist_last = entry->prev;
18071708Sstevel }
18081708Sstevel }
18091708Sstevel
18101708Sstevel void
drmach_msglist_link(drmach_msglist_t * entry)18111708Sstevel drmach_msglist_link(drmach_msglist_t *entry)
18121708Sstevel {
18131708Sstevel mutex_enter(&drmach_msglist_mutex);
18141708Sstevel if (drmach_msglist_last) {
18151708Sstevel entry->prev = drmach_msglist_last;
18161708Sstevel drmach_msglist_last->next = entry;
18171708Sstevel drmach_msglist_last = entry;
18181708Sstevel } else {
18191708Sstevel drmach_msglist_last = drmach_msglist_first = entry;
18201708Sstevel }
18211708Sstevel mutex_exit(&drmach_msglist_mutex);
18221708Sstevel }
18231708Sstevel
18241708Sstevel void
drmach_mbox_getmsg()18251708Sstevel drmach_mbox_getmsg()
18261708Sstevel {
18271708Sstevel int err;
18281708Sstevel register int msgid;
18291708Sstevel static uint8_t buf[DRMACH_MAX_MBOX_MSG_SIZE];
18301708Sstevel dr_mbox_msg_t *msg = (dr_mbox_msg_t *)buf;
18311708Sstevel dr_proto_hdr_t *php;
18321708Sstevel drmach_msglist_t *found, *entry;
18331708Sstevel uint32_t type = MBOXSC_MSG_REPLY;
18341708Sstevel uint32_t command;
18351708Sstevel uint64_t transid;
18361708Sstevel uint32_t length;
18371708Sstevel
18381708Sstevel php = &msg->p_hdr;
18391708Sstevel
18401708Sstevel while (drmach_getmsg_thread_run != 0) {
18411708Sstevel /* get a reply message */
18421708Sstevel command = 0;
18431708Sstevel transid = 0;
18441708Sstevel length = DRMACH_MAX_MBOX_MSG_SIZE;
184511066Srafael.vanoni@sun.com err = mboxsc_getmsg(KEY_SCDR, &type, &command, &transid,
184611066Srafael.vanoni@sun.com &length, (void *)msg, drmach_to_getmsg);
18471708Sstevel
18481708Sstevel if (err) {
18491708Sstevel /*
18501708Sstevel * If mboxsc_getmsg returns ETIMEDOUT or EAGAIN, then
18511708Sstevel * the "error" is really just a normal, transient
18521708Sstevel * condition and we can retry the operation right away.
18531708Sstevel * Any other error suggests a more serious problem,
18541708Sstevel * ranging from a message being too big for our buffer
18551708Sstevel * (EMSGSIZE) to total failure of the mailbox layer.
18561708Sstevel * This second class of errors is much less "transient",
18571708Sstevel * so rather than retrying over and over (and getting
18581708Sstevel * the same error over and over) as fast as we can,
18591708Sstevel * we'll sleep for a while before retrying.
18601708Sstevel */
18611708Sstevel if ((err != ETIMEDOUT) && (err != EAGAIN)) {
18621708Sstevel cmn_err(CE_WARN,
186311066Srafael.vanoni@sun.com "mboxsc_getmsg failed, err=0x%x", err);
18641708Sstevel delay(drmach_mbxerr_delay * hz);
18651708Sstevel }
18661708Sstevel continue;
18671708Sstevel }
18681708Sstevel
18691708Sstevel drmach_mbox_prmsg(msg, 0);
18701708Sstevel
18711708Sstevel if (php->drproto_version != DRMBX_VERSION) {
18721708Sstevel cmn_err(CE_WARN,
187311066Srafael.vanoni@sun.com "mailbox version mismatch 0x%x vs 0x%x",
187411066Srafael.vanoni@sun.com php->drproto_version, DRMBX_VERSION);
18751708Sstevel
18761708Sstevel mutex_enter(&drmach_g_mbox_mutex);
18771708Sstevel drmach_mbox_iflag = 0;
18781708Sstevel /* schedule a reinit handshake if one isn't pending */
18791708Sstevel if (!drmach_mbox_ipending) {
18801708Sstevel if (taskq_dispatch(system_taskq,
188111066Srafael.vanoni@sun.com drmach_mbox_reinit, NULL, TQ_NOSLEEP)
188211066Srafael.vanoni@sun.com != NULL) {
18831708Sstevel drmach_mbox_ipending = 1;
18841708Sstevel } else {
188511066Srafael.vanoni@sun.com cmn_err(CE_WARN, "failed to schedule "
188611066Srafael.vanoni@sun.com "mailbox reinit");
18871708Sstevel }
18881708Sstevel }
18891708Sstevel mutex_exit(&drmach_g_mbox_mutex);
18901708Sstevel
18911708Sstevel continue;
18921708Sstevel }
18931708Sstevel
18941708Sstevel msgid = php->message_id;
18951708Sstevel found = NULL;
18961708Sstevel mutex_enter(&drmach_msglist_mutex);
18971708Sstevel entry = drmach_msglist_first;
18981708Sstevel while (entry != NULL) {
18991708Sstevel if (entry->msgid == msgid) {
19001708Sstevel found = entry;
19011708Sstevel drmach_msglist_unlink(entry);
19021708Sstevel entry = NULL;
19031708Sstevel } else
19041708Sstevel entry = entry->next;
19051708Sstevel }
19061708Sstevel
19071708Sstevel if (found) {
19081708Sstevel mutex_enter(&found->g_lock);
19091708Sstevel
19101708Sstevel found->e_code = php->error_code;
19111708Sstevel if (found->i_buflen > 0)
19121708Sstevel bcopy((caddr_t)&msg->msgdata, found->i_buf,
191311066Srafael.vanoni@sun.com found->i_buflen);
19141708Sstevel found->m_reply = 1;
19151708Sstevel
19161708Sstevel cv_signal(&found->g_cv);
19171708Sstevel mutex_exit(&found->g_lock);
19181708Sstevel } else {
19191708Sstevel cmn_err(CE_WARN, "!mbox_getmsg: no match for id 0x%x",
19201708Sstevel msgid);
19211708Sstevel cmn_err(CE_WARN, "! cmd = 0x%x, exb = %d, slot = %d",
19221708Sstevel php->command, php->expbrd, php->slot);
19231708Sstevel }
19241708Sstevel
19251708Sstevel mutex_exit(&drmach_msglist_mutex);
19261708Sstevel }
19271708Sstevel cmn_err(CE_WARN, "mbox_getmsg: exiting");
19281708Sstevel mutex_enter(&drmach_msglist_mutex);
19291708Sstevel entry = drmach_msglist_first;
19301708Sstevel while (entry != NULL) {
19311708Sstevel if (entry->p_flag == 1) {
19321708Sstevel entry->f_error = -1;
19331708Sstevel mutex_enter(&entry->g_lock);
19341708Sstevel cv_signal(&entry->g_cv);
19351708Sstevel mutex_exit(&entry->g_lock);
19361708Sstevel drmach_msglist_unlink(entry);
19371708Sstevel }
19381708Sstevel entry = entry->next;
19391708Sstevel }
19401708Sstevel mutex_exit(&drmach_msglist_mutex);
19411708Sstevel drmach_getmsg_thread_run = -1;
19421708Sstevel thread_exit();
19431708Sstevel }
19441708Sstevel
19451708Sstevel void
drmach_mbox_sendmsg()19461708Sstevel drmach_mbox_sendmsg()
19471708Sstevel {
19481708Sstevel int err, retry;
19491708Sstevel drmach_msglist_t *entry;
19501708Sstevel dr_mbox_msg_t *mp;
19511708Sstevel dr_proto_hdr_t *php;
19521708Sstevel
19531708Sstevel while (drmach_sendmsg_thread_run != 0) {
19541708Sstevel /*
19551708Sstevel * Search through the list to find entries awaiting
19561708Sstevel * transmission to the SC
19571708Sstevel */
19581708Sstevel mutex_enter(&drmach_msglist_mutex);
19591708Sstevel entry = drmach_msglist_first;
19601708Sstevel retry = 0;
19611708Sstevel while (entry != NULL) {
19621708Sstevel if (entry->p_flag == 1) {
19631708Sstevel entry = entry->next;
19641708Sstevel continue;
19651708Sstevel }
19661708Sstevel
19671708Sstevel mutex_exit(&drmach_msglist_mutex);
19681708Sstevel
19691708Sstevel if (!retry)
19701708Sstevel mutex_enter(&entry->s_lock);
19711708Sstevel mp = (dr_mbox_msg_t *)entry->o_buf;
19721708Sstevel php = &mp->p_hdr;
19731708Sstevel
19741708Sstevel drmach_mbox_prmsg(mp, 1);
19751708Sstevel
19761708Sstevel err = mboxsc_putmsg(KEY_DRSC, MBOXSC_MSG_REQUEST,
197711066Srafael.vanoni@sun.com php->command, NULL, entry->o_buflen, (void *)mp,
197811066Srafael.vanoni@sun.com drmach_to_putmsg);
19791708Sstevel
19801708Sstevel if (err) {
19811708Sstevel switch (err) {
19821708Sstevel
19831708Sstevel case EAGAIN:
19841708Sstevel case EBUSY:
19851708Sstevel ++retry;
19861708Sstevel mutex_enter(&drmach_msglist_mutex);
19871708Sstevel continue;
19881708Sstevel
19891708Sstevel case ETIMEDOUT:
19901708Sstevel if (--entry->o_nretry <= 0) {
19911708Sstevel mutex_enter(
199211066Srafael.vanoni@sun.com &drmach_msglist_mutex);
19931708Sstevel drmach_msglist_unlink(entry);
19941708Sstevel mutex_exit(
199511066Srafael.vanoni@sun.com &drmach_msglist_mutex);
19961708Sstevel entry->f_error = err;
19971708Sstevel entry->p_flag = 1;
19981708Sstevel cv_signal(&entry->s_cv);
19991708Sstevel } else {
20001708Sstevel ++retry;
20011708Sstevel mutex_enter(
200211066Srafael.vanoni@sun.com &drmach_msglist_mutex);
20031708Sstevel continue;
20041708Sstevel }
20051708Sstevel break;
20061708Sstevel default:
20071708Sstevel mutex_enter(&drmach_msglist_mutex);
20081708Sstevel drmach_msglist_unlink(entry);
20091708Sstevel mutex_exit(&drmach_msglist_mutex);
20101708Sstevel entry->f_error = err;
20111708Sstevel entry->p_flag = 1;
20121708Sstevel cv_signal(&entry->s_cv);
20131708Sstevel break;
20141708Sstevel }
20151708Sstevel } else {
20161708Sstevel entry->p_flag = 1;
20171708Sstevel cv_signal(&entry->s_cv);
20181708Sstevel }
20191708Sstevel
20201708Sstevel mutex_exit(&entry->s_lock);
20211708Sstevel retry = 0;
20221708Sstevel mutex_enter(&drmach_msglist_mutex);
20231708Sstevel entry = drmach_msglist_first;
20241708Sstevel }
20251708Sstevel mutex_exit(&drmach_msglist_mutex);
20261708Sstevel
20271708Sstevel mutex_enter(&drmach_sendmsg_mutex);
202811066Srafael.vanoni@sun.com (void) cv_reltimedwait(&drmach_sendmsg_cv,
202911066Srafael.vanoni@sun.com &drmach_sendmsg_mutex, (5 * hz), TR_CLOCK_TICK);
20301708Sstevel mutex_exit(&drmach_sendmsg_mutex);
20311708Sstevel }
20321708Sstevel cmn_err(CE_WARN, "mbox_sendmsg: exiting");
20331708Sstevel mutex_enter(&drmach_msglist_mutex);
20341708Sstevel entry = drmach_msglist_first;
20351708Sstevel while (entry != NULL) {
20361708Sstevel if (entry->p_flag == 0) {
20371708Sstevel entry->f_error = -1;
20381708Sstevel mutex_enter(&entry->s_lock);
20391708Sstevel cv_signal(&entry->s_cv);
20401708Sstevel mutex_exit(&entry->s_lock);
20411708Sstevel drmach_msglist_unlink(entry);
20421708Sstevel }
20431708Sstevel entry = entry->next;
20441708Sstevel }
20451708Sstevel mutex_exit(&drmach_msglist_mutex);
20461708Sstevel cv_destroy(&drmach_sendmsg_cv);
20471708Sstevel mutex_destroy(&drmach_sendmsg_mutex);
20481708Sstevel
20491708Sstevel drmach_sendmsg_thread_run = -1;
20501708Sstevel thread_exit();
20511708Sstevel }
20521708Sstevel
20531708Sstevel void
drmach_msglist_destroy(drmach_msglist_t * listp)20541708Sstevel drmach_msglist_destroy(drmach_msglist_t *listp)
20551708Sstevel {
20561708Sstevel if (listp != NULL) {
20571708Sstevel drmach_msglist_t *entry;
20581708Sstevel
20591708Sstevel mutex_enter(&drmach_msglist_mutex);
20601708Sstevel entry = drmach_msglist_first;
20611708Sstevel while (entry) {
20621708Sstevel if (listp == entry) {
20631708Sstevel drmach_msglist_unlink(listp);
20641708Sstevel entry = NULL;
20651708Sstevel } else
20661708Sstevel entry = entry->next;
20671708Sstevel }
20681708Sstevel
20691708Sstevel mutex_destroy(&listp->s_lock);
20701708Sstevel cv_destroy(&listp->s_cv);
20711708Sstevel mutex_destroy(&listp->g_lock);
20721708Sstevel cv_destroy(&listp->g_cv);
20731708Sstevel kmem_free(listp, sizeof (drmach_msglist_t));
20741708Sstevel
20751708Sstevel mutex_exit(&drmach_msglist_mutex);
20761708Sstevel }
20771708Sstevel }
20781708Sstevel
20791708Sstevel static drmach_msglist_t *
drmach_msglist_new(caddr_t ibufp,uint32_t ilen,dr_proto_hdr_t * hdrp,uint32_t olen,int nrtry)20801708Sstevel drmach_msglist_new(caddr_t ibufp, uint32_t ilen, dr_proto_hdr_t *hdrp,
20811708Sstevel uint32_t olen, int nrtry)
20821708Sstevel {
20831708Sstevel drmach_msglist_t *listp;
20841708Sstevel
20851708Sstevel listp = kmem_zalloc(sizeof (drmach_msglist_t), KM_SLEEP);
20861708Sstevel mutex_init(&listp->s_lock, NULL, MUTEX_DRIVER, NULL);
20871708Sstevel cv_init(&listp->s_cv, NULL, CV_DRIVER, NULL);
20881708Sstevel mutex_init(&listp->g_lock, NULL, MUTEX_DRIVER, NULL);
20891708Sstevel cv_init(&listp->g_cv, NULL, CV_DRIVER, NULL);
20901708Sstevel listp->o_buf = (caddr_t)hdrp;
20911708Sstevel listp->o_buflen = olen;
20921708Sstevel listp->i_buf = ibufp;
20931708Sstevel listp->i_buflen = ilen;
20941708Sstevel listp->o_nretry = nrtry;
20951708Sstevel listp->msgid = hdrp->message_id;
20961708Sstevel
20971708Sstevel return (listp);
20981708Sstevel }
20991708Sstevel
21001708Sstevel static drmach_msglist_t *
drmach_mbox_req_rply(dr_proto_hdr_t * hdrp,uint32_t olen,caddr_t ibufp,uint32_t ilen,int timeout,int nrtry,int nosig,drmach_msglist_t * link)21011708Sstevel drmach_mbox_req_rply(dr_proto_hdr_t *hdrp, uint32_t olen, caddr_t ibufp,
21021708Sstevel uint32_t ilen, int timeout, int nrtry, int nosig,
21031708Sstevel drmach_msglist_t *link)
21041708Sstevel {
21051708Sstevel int crv;
21061708Sstevel drmach_msglist_t *listp;
21071708Sstevel clock_t to_val;
21081708Sstevel dr_proto_hdr_t *php;
21091708Sstevel
21101708Sstevel /* setup transaction list entry */
21111708Sstevel listp = drmach_msglist_new(ibufp, ilen, hdrp, olen, nrtry);
21121708Sstevel
21131708Sstevel /* send mailbox message, await reply */
21141708Sstevel mutex_enter(&listp->s_lock);
21151708Sstevel mutex_enter(&listp->g_lock);
21161708Sstevel
21171708Sstevel listp->link = link;
21181708Sstevel drmach_msglist_link(listp);
21191708Sstevel
21201708Sstevel mutex_enter(&drmach_sendmsg_mutex);
21211708Sstevel cv_signal(&drmach_sendmsg_cv);
21221708Sstevel mutex_exit(&drmach_sendmsg_mutex);
21231708Sstevel
21241708Sstevel while (listp->p_flag == 0) {
21251708Sstevel cv_wait(&listp->s_cv, &listp->s_lock);
21261708Sstevel }
21271708Sstevel
212811066Srafael.vanoni@sun.com to_val = ddi_get_lbolt() + (timeout * hz);
21291708Sstevel
21301708Sstevel if (listp->f_error) {
21311708Sstevel listp->p_flag = 0;
213211066Srafael.vanoni@sun.com cmn_err(CE_WARN, "!mboxsc_putmsg failed: 0x%x", listp->f_error);
21331708Sstevel php = (dr_proto_hdr_t *)listp->o_buf;
21341708Sstevel cmn_err(CE_WARN, "! cmd = 0x%x, exb = %d, slot = %d",
21351708Sstevel php->command, php->expbrd, php->slot);
21361708Sstevel } else {
21371708Sstevel while (listp->m_reply == 0 && listp->f_error == 0) {
21381708Sstevel if (nosig)
21391708Sstevel crv = cv_timedwait(&listp->g_cv, &listp->g_lock,
214011066Srafael.vanoni@sun.com to_val);
21411708Sstevel else
21421708Sstevel crv = cv_timedwait_sig(&listp->g_cv,
214311066Srafael.vanoni@sun.com &listp->g_lock, to_val);
21441708Sstevel switch (crv) {
21451708Sstevel case -1: /* timed out */
21461708Sstevel cmn_err(CE_WARN,
21471708Sstevel "!msgid=0x%x reply timed out",
21481708Sstevel hdrp->message_id);
21491708Sstevel php = (dr_proto_hdr_t *)listp->o_buf;
21501708Sstevel cmn_err(CE_WARN, "! cmd = 0x%x, "
21511708Sstevel "exb = %d, slot = %d", php->command,
21521708Sstevel php->expbrd, php->slot);
21531708Sstevel listp->f_error = ETIMEDOUT;
21541708Sstevel break;
21551708Sstevel case 0: /* signal received */
21561708Sstevel cmn_err(CE_WARN,
21571708Sstevel "operation interrupted by signal");
21581708Sstevel listp->f_error = EINTR;
21591708Sstevel break;
21601708Sstevel default:
21611708Sstevel break;
21621708Sstevel }
21631708Sstevel }
21641708Sstevel
21651708Sstevel /*
21661708Sstevel * If link is set for this entry, check to see if
21671708Sstevel * the linked entry has been replied to. If not,
21681708Sstevel * wait for the response.
21691708Sstevel * Currently, this is only used for ABORT_TEST functionality,
21701708Sstevel * wherein a check is made for the TESTBOARD reply when
21711708Sstevel * the ABORT_TEST reply is received.
21721708Sstevel */
21731708Sstevel
21741708Sstevel if (link) {
21751708Sstevel mutex_enter(&link->g_lock);
21761708Sstevel /*
21771708Sstevel * If the reply to the linked entry hasn't been
21781708Sstevel * received, clear the existing link->f_error,
21791708Sstevel * and await the reply.
21801708Sstevel */
21811708Sstevel if (link->m_reply == 0) {
21821708Sstevel link->f_error = 0;
21831708Sstevel }
21841708Sstevel to_val = ddi_get_lbolt() + (timeout * hz);
21851708Sstevel while (link->m_reply == 0 && link->f_error == 0) {
21861708Sstevel crv = cv_timedwait(&link->g_cv, &link->g_lock,
218711066Srafael.vanoni@sun.com to_val);
21881708Sstevel switch (crv) {
21891708Sstevel case -1: /* timed out */
21901708Sstevel cmn_err(CE_NOTE,
21911708Sstevel "!link msgid=0x%x reply timed out",
21921708Sstevel link->msgid);
21931708Sstevel link->f_error = ETIMEDOUT;
21941708Sstevel break;
21951708Sstevel default:
21961708Sstevel break;
21971708Sstevel }
21981708Sstevel }
21991708Sstevel mutex_exit(&link->g_lock);
22001708Sstevel }
22011708Sstevel }
22021708Sstevel mutex_exit(&listp->g_lock);
22031708Sstevel mutex_exit(&listp->s_lock);
22041708Sstevel return (listp);
22051708Sstevel }
22061708Sstevel
22071708Sstevel static sbd_error_t *
drmach_mbx2sbderr(drmach_msglist_t * mlp)22081708Sstevel drmach_mbx2sbderr(drmach_msglist_t *mlp)
22091708Sstevel {
22101708Sstevel char a_pnt[MAXNAMELEN];
22111708Sstevel dr_proto_hdr_t *php;
22121708Sstevel int bnum;
22131708Sstevel
22141708Sstevel if (mlp->f_error) {
22151708Sstevel /*
22161708Sstevel * If framework failure is due to signal, return "no error"
22171708Sstevel * error.
22181708Sstevel */
22191708Sstevel if (mlp->f_error == EINTR)
22201708Sstevel return (drerr_new(0, ESTC_NONE, NULL));
22211708Sstevel
22221708Sstevel mutex_enter(&drmach_g_mbox_mutex);
22231708Sstevel drmach_mbox_iflag = 0;
22241708Sstevel mutex_exit(&drmach_g_mbox_mutex);
22251708Sstevel if (!mlp->p_flag)
22261708Sstevel return (drerr_new(1, ESTC_MBXRQST, NULL));
22271708Sstevel else
22281708Sstevel return (drerr_new(1, ESTC_MBXRPLY, NULL));
22291708Sstevel }
22301708Sstevel php = (dr_proto_hdr_t *)mlp->o_buf;
22311708Sstevel bnum = 2 * php->expbrd + php->slot;
22321708Sstevel a_pnt[0] = '\0';
22331708Sstevel (void) drmach_board_name(bnum, a_pnt, MAXNAMELEN);
22341708Sstevel
22351708Sstevel switch (mlp->e_code) {
22361708Sstevel case 0:
22371708Sstevel return (NULL);
22381708Sstevel case DRERR_NOACL:
22391708Sstevel return (drerr_new(0, ESTC_NOACL, "%s", a_pnt));
22401708Sstevel case DRERR_NOT_ASSIGNED:
22411708Sstevel return (drerr_new(0, ESTC_NOT_ASSIGNED, "%s", a_pnt));
22421708Sstevel case DRERR_NOT_ACTIVE:
22431708Sstevel return (drerr_new(0, ESTC_NOT_ACTIVE, "%s", a_pnt));
22441708Sstevel case DRERR_EMPTY_SLOT:
22451708Sstevel return (drerr_new(0, ESTC_EMPTY_SLOT, "%s", a_pnt));
22461708Sstevel case DRERR_POWER_OFF:
22471708Sstevel return (drerr_new(0, ESTC_POWER_OFF, "%s", a_pnt));
22481708Sstevel case DRERR_TEST_IN_PROGRESS:
224911066Srafael.vanoni@sun.com return (drerr_new(0, ESTC_TEST_IN_PROGRESS, "%s",
225011066Srafael.vanoni@sun.com a_pnt));
22511708Sstevel case DRERR_TESTING_BUSY:
22521708Sstevel return (drerr_new(0, ESTC_TESTING_BUSY, "%s", a_pnt));
22531708Sstevel case DRERR_TEST_REQUIRED:
22541708Sstevel return (drerr_new(0, ESTC_TEST_REQUIRED, "%s", a_pnt));
22551708Sstevel case DRERR_UNAVAILABLE:
22561708Sstevel return (drerr_new(0, ESTC_UNAVAILABLE, "%s", a_pnt));
22571708Sstevel case DRERR_RECOVERABLE:
225811066Srafael.vanoni@sun.com return (drerr_new(0, ESTC_SMS_ERR_RECOVERABLE, "%s",
225911066Srafael.vanoni@sun.com a_pnt));
22601708Sstevel case DRERR_UNRECOVERABLE:
226111066Srafael.vanoni@sun.com return (drerr_new(1, ESTC_SMS_ERR_UNRECOVERABLE, "%s",
226211066Srafael.vanoni@sun.com a_pnt));
22631708Sstevel default:
22641708Sstevel return (drerr_new(1, ESTC_MBOX_UNKNOWN, NULL));
22651708Sstevel }
22661708Sstevel }
22671708Sstevel
22681708Sstevel static sbd_error_t *
drmach_mbox_trans(uint8_t msgtype,int bnum,caddr_t obufp,int olen,caddr_t ibufp,int ilen)22691708Sstevel drmach_mbox_trans(uint8_t msgtype, int bnum, caddr_t obufp, int olen,
22701708Sstevel caddr_t ibufp, int ilen)
22711708Sstevel {
22721708Sstevel int timeout = 0;
22731708Sstevel int ntries = 0;
22741708Sstevel int nosignals = 0;
22751708Sstevel dr_proto_hdr_t *hdrp;
22761708Sstevel drmach_msglist_t *mlp;
22771708Sstevel sbd_error_t *err = NULL;
22781708Sstevel
22791708Sstevel if (msgtype != DRMSG_MBOX_INIT) {
22801708Sstevel mutex_enter(&drmach_ri_mbox_mutex);
22811708Sstevel mutex_enter(&drmach_g_mbox_mutex);
22821708Sstevel if (drmach_mbox_iflag == 0) {
22831708Sstevel /* need to initialize the mailbox */
22841708Sstevel dr_proto_hdr_t imsg;
22851708Sstevel
22861708Sstevel mutex_exit(&drmach_g_mbox_mutex);
22871708Sstevel
22881708Sstevel imsg.command = DRMSG_MBOX_INIT;
22891708Sstevel
22901708Sstevel imsg.message_id = drmach_get_msgid();
22911708Sstevel imsg.drproto_version = DRMBX_VERSION;
22921708Sstevel imsg.expbrd = 0;
22931708Sstevel imsg.slot = 0;
22941708Sstevel
229511066Srafael.vanoni@sun.com cmn_err(CE_WARN, "!reinitializing DR mailbox");
22961708Sstevel mlp = drmach_mbox_req_rply(&imsg, sizeof (imsg), 0, 0,
229711066Srafael.vanoni@sun.com 10, 5, 0, NULL);
22981708Sstevel err = drmach_mbx2sbderr(mlp);
22991708Sstevel /*
23001708Sstevel * If framework failure incoming is encountered on
23011708Sstevel * the MBOX_INIT [timeout on SMS reply], the error
23021708Sstevel * type must be changed before returning to caller.
23031708Sstevel * This is to prevent drmach_board_connect() and
23041708Sstevel * drmach_board_disconnect() from marking boards
23051708Sstevel * UNUSABLE based on MBOX_INIT failures.
23061708Sstevel */
23071708Sstevel if ((err != NULL) && (err->e_code == ESTC_MBXRPLY)) {
23081708Sstevel cmn_err(CE_WARN,
23091708Sstevel "!Changed mbox incoming to outgoing"
23101708Sstevel " failure on reinit");
23111708Sstevel sbd_err_clear(&err);
23121708Sstevel err = drerr_new(0, ESTC_MBXRQST, NULL);
23131708Sstevel }
23141708Sstevel drmach_msglist_destroy(mlp);
23151708Sstevel if (err) {
23161708Sstevel mutex_exit(&drmach_ri_mbox_mutex);
23171708Sstevel return (err);
23181708Sstevel }
23191708Sstevel mutex_enter(&drmach_g_mbox_mutex);
23201708Sstevel drmach_mbox_iflag = 1;
23211708Sstevel }
23221708Sstevel mutex_exit(&drmach_g_mbox_mutex);
23231708Sstevel mutex_exit(&drmach_ri_mbox_mutex);
23241708Sstevel }
23251708Sstevel
23261708Sstevel hdrp = (dr_proto_hdr_t *)obufp;
23271708Sstevel
23281708Sstevel /* setup outgoing mailbox header */
23291708Sstevel hdrp->command = msgtype;
23301708Sstevel hdrp->message_id = drmach_get_msgid();
23311708Sstevel hdrp->drproto_version = DRMBX_VERSION;
23321708Sstevel hdrp->expbrd = DRMACH_BNUM2EXP(bnum);
23331708Sstevel hdrp->slot = DRMACH_BNUM2SLOT(bnum);
23341708Sstevel
23351708Sstevel switch (msgtype) {
23361708Sstevel
23371708Sstevel case DRMSG_MBOX_INIT:
23381708Sstevel timeout = drmach_to_mbxinit;
23391708Sstevel ntries = 1;
23401708Sstevel nosignals = 0;
23411708Sstevel break;
23421708Sstevel
23431708Sstevel case DRMSG_ASSIGN:
23441708Sstevel timeout = drmach_to_assign;
23451708Sstevel ntries = 1;
23461708Sstevel nosignals = 0;
23471708Sstevel break;
23481708Sstevel
23491708Sstevel case DRMSG_UNASSIGN:
23501708Sstevel timeout = drmach_to_unassign;
23511708Sstevel ntries = 1;
23521708Sstevel nosignals = 0;
23531708Sstevel break;
23541708Sstevel
23551708Sstevel case DRMSG_POWERON:
23561708Sstevel timeout = drmach_to_poweron;
23571708Sstevel ntries = 1;
23581708Sstevel nosignals = 0;
23591708Sstevel break;
23601708Sstevel
23611708Sstevel case DRMSG_POWEROFF:
23621708Sstevel timeout = drmach_to_poweroff;
23631708Sstevel ntries = 1;
23641708Sstevel nosignals = 0;
23651708Sstevel break;
23661708Sstevel
23671708Sstevel case DRMSG_SHOWBOARD:
23681708Sstevel timeout = drmach_to_showboard;
23691708Sstevel ntries = 1;
23701708Sstevel nosignals = 0;
23711708Sstevel break;
23721708Sstevel
23731708Sstevel case DRMSG_CLAIM:
23741708Sstevel timeout = drmach_to_claim;
23751708Sstevel ntries = 1;
23761708Sstevel nosignals = 1;
23771708Sstevel break;
23781708Sstevel
23791708Sstevel case DRMSG_UNCLAIM:
23801708Sstevel timeout = drmach_to_unclaim;
23811708Sstevel ntries = 1;
23821708Sstevel nosignals = 1;
23831708Sstevel break;
23841708Sstevel
23851708Sstevel case DRMSG_UNCONFIG:
23861708Sstevel timeout = drmach_to_unconfig;
23871708Sstevel ntries = 1;
23881708Sstevel nosignals = 0;
23891708Sstevel break;
23901708Sstevel
23911708Sstevel case DRMSG_TESTBOARD:
23921708Sstevel timeout = drmach_to_testboard;
23931708Sstevel ntries = 1;
23941708Sstevel nosignals = 0;
23951708Sstevel break;
23961708Sstevel
23971708Sstevel default:
239811066Srafael.vanoni@sun.com cmn_err(CE_WARN, "Unknown outgoing message type 0x%x",
239911066Srafael.vanoni@sun.com msgtype);
24001708Sstevel err = DRMACH_INTERNAL_ERROR();
24011708Sstevel break;
24021708Sstevel }
24031708Sstevel
24041708Sstevel if (err == NULL) {
240511066Srafael.vanoni@sun.com mlp = drmach_mbox_req_rply(hdrp, olen, ibufp, ilen, timeout,
240611066Srafael.vanoni@sun.com ntries, nosignals, NULL);
24071708Sstevel err = drmach_mbx2sbderr(mlp);
24081708Sstevel
24091708Sstevel /*
24101708Sstevel * For DRMSG_TESTBOARD attempts which have timed out, or
24111708Sstevel * been aborted due to a signal received after mboxsc_putmsg()
24121708Sstevel * has succeeded in sending the message, a DRMSG_ABORT_TEST
24131708Sstevel * must be sent.
24141708Sstevel */
24151708Sstevel if ((msgtype == DRMSG_TESTBOARD) && (err != NULL) &&
24161708Sstevel ((mlp->f_error == EINTR) || ((mlp->f_error == ETIMEDOUT) &&
24171708Sstevel (mlp->p_flag != 0)))) {
24181708Sstevel drmach_msglist_t *abmlp;
24191708Sstevel dr_abort_test_t abibuf;
24201708Sstevel
24211708Sstevel hdrp->command = DRMSG_ABORT_TEST;
24221708Sstevel hdrp->message_id = drmach_get_msgid();
24231708Sstevel abmlp = drmach_mbox_req_rply(hdrp,
24241708Sstevel sizeof (dr_abort_test_t), (caddr_t)&abibuf,
24251708Sstevel sizeof (abibuf), drmach_to_aborttest, 5, 1, mlp);
24261708Sstevel cmn_err(CE_WARN, "test aborted");
24271708Sstevel drmach_msglist_destroy(abmlp);
24281708Sstevel }
24291708Sstevel
24301708Sstevel drmach_msglist_destroy(mlp);
24311708Sstevel }
24321708Sstevel
24331708Sstevel return (err);
24341708Sstevel }
24351708Sstevel
24361708Sstevel static int
drmach_mbox_init()24371708Sstevel drmach_mbox_init()
24381708Sstevel {
24391708Sstevel int err;
24401708Sstevel caddr_t obufp;
24411708Sstevel sbd_error_t *serr = NULL;
24421708Sstevel mboxsc_timeout_range_t mbxtoz;
24431708Sstevel
24441708Sstevel drmach_mbox_istate = 0;
24451708Sstevel /* register the outgoing mailbox */
24461708Sstevel if ((err = mboxsc_init(KEY_DRSC, MBOXSC_MBOX_OUT,
244711066Srafael.vanoni@sun.com NULL)) != 0) {
24481708Sstevel cmn_err(CE_WARN, "DR - SC mboxsc_init failed: 0x%x", err);
24491708Sstevel return (-1);
24501708Sstevel }
24511708Sstevel drmach_mbox_istate = 1;
24521708Sstevel
24531708Sstevel /* setup the mboxsc_putmsg timeout value */
24541708Sstevel if (drmach_use_tuned_putmsg_to) {
24551708Sstevel cmn_err(CE_NOTE, "!using tuned drmach_to_putmsg = 0x%lx\n",
24561708Sstevel drmach_to_putmsg);
24571708Sstevel } else {
24581708Sstevel if ((err = mboxsc_ctrl(KEY_DRSC,
24591708Sstevel MBOXSC_CMD_PUTMSG_TIMEOUT_RANGE, &mbxtoz)) != 0) {
24601708Sstevel cmn_err(CE_WARN, "mboxsc_ctrl failed: 0x%x", err);
24611708Sstevel drmach_to_putmsg = 60000;
24621708Sstevel } else {
24631708Sstevel drmach_to_putmsg = mboxsc_putmsg_def_timeout() * 6;
24641708Sstevel DRMACH_PR("putmsg range is 0x%lx - 0x%lx value"
24651708Sstevel " is 0x%lx\n", mbxtoz.min_timeout,
24661708Sstevel mbxtoz.max_timeout, drmach_to_putmsg);
24671708Sstevel }
24681708Sstevel }
24691708Sstevel
24701708Sstevel /* register the incoming mailbox */
24711708Sstevel if ((err = mboxsc_init(KEY_SCDR, MBOXSC_MBOX_IN,
247211066Srafael.vanoni@sun.com drmach_mbox_event)) != 0) {
24731708Sstevel cmn_err(CE_WARN, "SC - DR mboxsc_init failed: 0x%x", err);
24741708Sstevel return (-1);
24751708Sstevel }
24761708Sstevel drmach_mbox_istate = 2;
24771708Sstevel
24781708Sstevel /* initialize mutex for mailbox globals */
24791708Sstevel mutex_init(&drmach_g_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
24801708Sstevel
24811708Sstevel /* initialize mutex for mailbox re-init */
24821708Sstevel mutex_init(&drmach_ri_mbox_mutex, NULL, MUTEX_DRIVER, NULL);
24831708Sstevel
24841708Sstevel /* initialize mailbox message list elements */
24851708Sstevel drmach_msglist_first = drmach_msglist_last = NULL;
24861708Sstevel mutex_init(&drmach_msglist_mutex, NULL, MUTEX_DRIVER, NULL);
24871708Sstevel
24881708Sstevel mutex_init(&drmach_sendmsg_mutex, NULL, MUTEX_DRIVER, NULL);
24891708Sstevel cv_init(&drmach_sendmsg_cv, NULL, CV_DRIVER, NULL);
24901708Sstevel
24911708Sstevel drmach_mbox_istate = 3;
24921708Sstevel
24931708Sstevel /* start mailbox sendmsg thread */
24941708Sstevel drmach_sendmsg_thread_run = 1;
24951708Sstevel if (drmach_sendmsg_thread == NULL)
24961708Sstevel drmach_sendmsg_thread = thread_create(NULL, 0,
24971708Sstevel (void (*)())drmach_mbox_sendmsg, NULL, 0, &p0,
24981708Sstevel TS_RUN, minclsyspri);
24991708Sstevel
25001708Sstevel /* start mailbox getmsg thread */
25011708Sstevel drmach_getmsg_thread_run = 1;
25021708Sstevel if (drmach_getmsg_thread == NULL)
25031708Sstevel drmach_getmsg_thread = thread_create(NULL, 0,
25041708Sstevel (void (*)())drmach_mbox_getmsg, NULL, 0, &p0,
25051708Sstevel TS_RUN, minclsyspri);
25061708Sstevel
25071708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
25081708Sstevel serr = drmach_mbox_trans(DRMSG_MBOX_INIT, 0, obufp,
250911066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
25101708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
25111708Sstevel if (serr) {
25121708Sstevel cmn_err(CE_WARN, "mbox_init: MBOX_INIT failed ecode=0x%x",
251311066Srafael.vanoni@sun.com serr->e_code);
25141708Sstevel sbd_err_clear(&serr);
25151708Sstevel return (-1);
25161708Sstevel }
25171708Sstevel mutex_enter(&drmach_g_mbox_mutex);
25181708Sstevel drmach_mbox_iflag = 1;
25191708Sstevel drmach_mbox_ipending = 0;
25201708Sstevel mutex_exit(&drmach_g_mbox_mutex);
25211708Sstevel
25221708Sstevel return (0);
25231708Sstevel }
25241708Sstevel
25251708Sstevel static int
drmach_mbox_fini()25261708Sstevel drmach_mbox_fini()
25271708Sstevel {
25281708Sstevel int err, rv = 0;
25291708Sstevel
25301708Sstevel if (drmach_mbox_istate > 2) {
25311708Sstevel drmach_getmsg_thread_run = 0;
25321708Sstevel drmach_sendmsg_thread_run = 0;
25331708Sstevel cmn_err(CE_WARN,
253411066Srafael.vanoni@sun.com "drmach_mbox_fini: waiting for mbox threads...");
25351708Sstevel while ((drmach_getmsg_thread_run == 0) ||
253611066Srafael.vanoni@sun.com (drmach_sendmsg_thread_run == 0)) {
25371708Sstevel continue;
25381708Sstevel }
253911066Srafael.vanoni@sun.com cmn_err(CE_WARN, "drmach_mbox_fini: mbox threads done.");
25401708Sstevel mutex_destroy(&drmach_msglist_mutex);
25411708Sstevel
25421708Sstevel }
25431708Sstevel if (drmach_mbox_istate) {
25441708Sstevel /* de-register the outgoing mailbox */
25451708Sstevel if ((err = mboxsc_fini(KEY_DRSC)) != 0) {
25461708Sstevel cmn_err(CE_WARN, "DR - SC mboxsc_fini failed: 0x%x",
254711066Srafael.vanoni@sun.com err);
25481708Sstevel rv = -1;
25491708Sstevel }
25501708Sstevel }
25511708Sstevel if (drmach_mbox_istate > 1) {
25521708Sstevel /* de-register the incoming mailbox */
25531708Sstevel if ((err = mboxsc_fini(KEY_SCDR)) != 0) {
25541708Sstevel cmn_err(CE_WARN, "SC - DR mboxsc_fini failed: 0x%x",
255511066Srafael.vanoni@sun.com err);
25561708Sstevel rv = -1;
25571708Sstevel }
25581708Sstevel }
25591708Sstevel mutex_destroy(&drmach_g_mbox_mutex);
25601708Sstevel mutex_destroy(&drmach_ri_mbox_mutex);
25611708Sstevel return (rv);
25621708Sstevel }
25631708Sstevel
25641708Sstevel static int
drmach_portid2bnum(int portid)25651708Sstevel drmach_portid2bnum(int portid)
25661708Sstevel {
25671708Sstevel int slot;
25681708Sstevel
25691708Sstevel switch (portid & 0x1f) {
25701708Sstevel case 0: case 1: case 2: case 3: /* cpu/wci devices */
25711708Sstevel case 0x1e: /* slot 0 axq registers */
25721708Sstevel slot = 0;
25731708Sstevel break;
25741708Sstevel
25751708Sstevel case 8: case 9: /* cpu devices */
25761708Sstevel case 0x1c: case 0x1d: /* schizo/wci devices */
25771708Sstevel case 0x1f: /* slot 1 axq registers */
25781708Sstevel slot = 1;
25791708Sstevel break;
25801708Sstevel
25811708Sstevel default:
25821708Sstevel ASSERT(0); /* catch in debug kernels */
25831708Sstevel }
25841708Sstevel
25851708Sstevel return (((portid >> 4) & 0x7e) | slot);
25861708Sstevel }
25871708Sstevel
25881708Sstevel extern int axq_suspend_iopause;
25891708Sstevel
25901708Sstevel static int
hold_rele_branch(dev_info_t * rdip,void * arg)25911708Sstevel hold_rele_branch(dev_info_t *rdip, void *arg)
25921708Sstevel {
25931708Sstevel int i;
25941708Sstevel int *holdp = (int *)arg;
25951708Sstevel char *name = ddi_node_name(rdip);
25961708Sstevel
25971708Sstevel /*
25981708Sstevel * For Starcat, we must be children of the root devinfo node
25991708Sstevel */
26001708Sstevel ASSERT(ddi_get_parent(rdip) == ddi_root_node());
26011708Sstevel
26021708Sstevel i = drmach_name2type_idx(name);
26031708Sstevel
26041708Sstevel /*
26051708Sstevel * Only children of the root devinfo node need to be
26061708Sstevel * held/released since they are the only valid targets
26071708Sstevel * of tree operations. This corresponds to the node types
26081708Sstevel * listed in the drmach_name2type array.
26091708Sstevel */
26101708Sstevel if (i < 0) {
26111708Sstevel /* Not of interest to us */
26121708Sstevel return (DDI_WALK_PRUNECHILD);
26131708Sstevel }
26141708Sstevel
26151708Sstevel if (*holdp) {
26161708Sstevel ASSERT(!e_ddi_branch_held(rdip));
26171708Sstevel e_ddi_branch_hold(rdip);
26181708Sstevel } else {
26191708Sstevel ASSERT(e_ddi_branch_held(rdip));
26201708Sstevel e_ddi_branch_rele(rdip);
26211708Sstevel }
26221708Sstevel
26231708Sstevel return (DDI_WALK_PRUNECHILD);
26241708Sstevel }
26251708Sstevel
26261708Sstevel static int
drmach_init(void)26271708Sstevel drmach_init(void)
26281708Sstevel {
26291708Sstevel pnode_t nodeid;
26301708Sstevel gdcd_t *gdcd;
26311708Sstevel int bnum;
26321708Sstevel dev_info_t *rdip;
26331708Sstevel int hold, circ;
26341708Sstevel
26351708Sstevel mutex_enter(&drmach_i_lock);
26361708Sstevel if (drmach_initialized) {
26371708Sstevel mutex_exit(&drmach_i_lock);
26381708Sstevel return (0);
26391708Sstevel }
26401708Sstevel
26411708Sstevel gdcd = drmach_gdcd_new();
26421708Sstevel if (gdcd == NULL) {
26431708Sstevel mutex_exit(&drmach_i_lock);
26441708Sstevel cmn_err(CE_WARN, "drmach_init: failed to access GDCD\n");
26451708Sstevel return (-1);
26461708Sstevel }
26471708Sstevel
26481708Sstevel drmach_boards = drmach_array_new(0, MAX_BOARDS - 1);
26491708Sstevel
26501708Sstevel nodeid = prom_childnode(prom_rootnode());
26511708Sstevel do {
26521708Sstevel int len;
26531708Sstevel int portid;
26541708Sstevel drmachid_t id;
26551708Sstevel
26561708Sstevel len = prom_getproplen(nodeid, "portid");
26571708Sstevel if (len != sizeof (portid))
26581708Sstevel continue;
26591708Sstevel
26601708Sstevel portid = -1;
26611708Sstevel (void) prom_getprop(nodeid, "portid", (caddr_t)&portid);
26621708Sstevel if (portid == -1)
26631708Sstevel continue;
26641708Sstevel
26651708Sstevel bnum = drmach_portid2bnum(portid);
26661708Sstevel
26671708Sstevel if (drmach_array_get(drmach_boards, bnum, &id) == -1) {
26681708Sstevel /* portid translated to an invalid board number */
26691708Sstevel cmn_err(CE_WARN, "OBP node 0x%x has"
267011066Srafael.vanoni@sun.com " invalid property value, %s=%u",
267111066Srafael.vanoni@sun.com nodeid, "portid", portid);
26721708Sstevel
26731708Sstevel /* clean up */
26741708Sstevel drmach_array_dispose(drmach_boards,
26751708Sstevel drmach_board_dispose);
26761708Sstevel drmach_gdcd_dispose(gdcd);
26771708Sstevel mutex_exit(&drmach_i_lock);
26781708Sstevel return (-1);
26791708Sstevel } else if (id == NULL) {
26801708Sstevel drmach_board_t *bp;
26811708Sstevel l1_slot_stat_t *dcd;
26821708Sstevel int exp, slot;
26831708Sstevel
26841708Sstevel bp = drmach_board_new(bnum);
26851708Sstevel bp->assigned = !drmach_initialized;
26861708Sstevel bp->powered = !drmach_initialized;
26871708Sstevel
26881708Sstevel exp = DRMACH_BNUM2EXP(bnum);
26891708Sstevel slot = DRMACH_BNUM2SLOT(bnum);
26901708Sstevel dcd = &gdcd->dcd_slot[exp][slot];
26911708Sstevel bp->stardrb_offset =
26921708Sstevel dcd->l1ss_cpu_drblock_xwd_offset << 3;
26931708Sstevel DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
26941708Sstevel bp->stardrb_offset);
26951708Sstevel
26961708Sstevel if (gdcd->dcd_slot[exp][slot].l1ss_flags &
26971708Sstevel L1SSFLG_THIS_L1_NULL_PROC_LPA) {
26981708Sstevel bp->flags |= DRMACH_NULL_PROC_LPA;
26991708Sstevel DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
27001708Sstevel }
27011708Sstevel }
27021708Sstevel } while ((nodeid = prom_nextnode(nodeid)) != OBP_NONODE);
27031708Sstevel
27041708Sstevel drmach_cpu_sram_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
27051708Sstevel
27061708Sstevel if (gdcd->dcd_testcage_log2_mbytes_size != DCD_DR_TESTCAGE_DISABLED) {
27071708Sstevel ASSERT(gdcd->dcd_testcage_log2_mbytes_size ==
270811066Srafael.vanoni@sun.com gdcd->dcd_testcage_log2_mbytes_align);
27091708Sstevel drmach_iocage_paddr =
271011066Srafael.vanoni@sun.com (uint64_t)gdcd->dcd_testcage_mbyte_PA << 20;
27111708Sstevel drmach_iocage_size =
271211066Srafael.vanoni@sun.com 1 << (gdcd->dcd_testcage_log2_mbytes_size + 20);
27131708Sstevel
27141708Sstevel drmach_iocage_vaddr = (caddr_t)vmem_alloc(heap_arena,
271511066Srafael.vanoni@sun.com drmach_iocage_size, VM_SLEEP);
27161708Sstevel hat_devload(kas.a_hat, drmach_iocage_vaddr, drmach_iocage_size,
271711066Srafael.vanoni@sun.com mmu_btop(drmach_iocage_paddr),
271811066Srafael.vanoni@sun.com PROT_READ | PROT_WRITE,
271911066Srafael.vanoni@sun.com HAT_LOAD_LOCK | HAT_LOAD_NOCONSIST);
27201708Sstevel
27211708Sstevel DRMACH_PR("gdcd size=0x%x align=0x%x PA=0x%x\n",
272211066Srafael.vanoni@sun.com gdcd->dcd_testcage_log2_mbytes_size,
272311066Srafael.vanoni@sun.com gdcd->dcd_testcage_log2_mbytes_align,
272411066Srafael.vanoni@sun.com gdcd->dcd_testcage_mbyte_PA);
27251708Sstevel DRMACH_PR("drmach size=0x%x PA=0x%lx VA=0x%p\n",
272611066Srafael.vanoni@sun.com drmach_iocage_size, drmach_iocage_paddr,
272711311SSurya.Prakki@Sun.COM (void *)drmach_iocage_vaddr);
27281708Sstevel }
27291708Sstevel
27301708Sstevel if (drmach_iocage_size == 0) {
27311708Sstevel drmach_array_dispose(drmach_boards, drmach_board_dispose);
27321708Sstevel drmach_boards = NULL;
27331708Sstevel vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
27341708Sstevel drmach_gdcd_dispose(gdcd);
27351708Sstevel mutex_exit(&drmach_i_lock);
27361708Sstevel cmn_err(CE_WARN, "drmach_init: iocage not available\n");
27371708Sstevel return (-1);
27381708Sstevel }
27391708Sstevel
27401708Sstevel drmach_gdcd_dispose(gdcd);
27411708Sstevel
27421708Sstevel mutex_init(&drmach_iocage_lock, NULL, MUTEX_DRIVER, NULL);
27431708Sstevel cv_init(&drmach_iocage_cv, NULL, CV_DRIVER, NULL);
27441708Sstevel mutex_init(&drmach_xt_mb_lock, NULL, MUTEX_DRIVER, NULL);
27451708Sstevel mutex_init(&drmach_bus_sync_lock, NULL, MUTEX_DRIVER, NULL);
27461708Sstevel mutex_init(&drmach_slice_table_lock, NULL, MUTEX_DRIVER, NULL);
27471708Sstevel
27481708Sstevel mutex_enter(&cpu_lock);
27491708Sstevel mutex_enter(&drmach_iocage_lock);
27501708Sstevel ASSERT(drmach_iocage_is_busy == 0);
27511708Sstevel drmach_iocage_is_busy = 1;
27521708Sstevel drmach_iocage_mem_scrub(drmach_iocage_size);
27531708Sstevel drmach_iocage_is_busy = 0;
27541708Sstevel cv_signal(&drmach_iocage_cv);
27551708Sstevel mutex_exit(&drmach_iocage_lock);
27561708Sstevel mutex_exit(&cpu_lock);
27571708Sstevel
27581708Sstevel
27591708Sstevel if (drmach_mbox_init() == -1) {
27601708Sstevel cmn_err(CE_WARN, "DR - SC mailbox initialization Failed");
27611708Sstevel }
27621708Sstevel
27631708Sstevel /*
27641708Sstevel * Walk immediate children of devinfo root node and hold
27651708Sstevel * all devinfo branches of interest.
27661708Sstevel */
27671708Sstevel hold = 1;
27681708Sstevel rdip = ddi_root_node();
27691708Sstevel
27701708Sstevel ndi_devi_enter(rdip, &circ);
27711708Sstevel ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
27721708Sstevel ndi_devi_exit(rdip, circ);
27731708Sstevel
27741708Sstevel drmach_initialized = 1;
27751708Sstevel
27761708Sstevel /*
27771708Sstevel * To avoid a circular patch dependency between DR and AXQ, the AXQ
27781708Sstevel * rev introducing the axq_iopause_*_all interfaces should not regress
27791708Sstevel * when installed without the DR rev using those interfaces. The default
27801708Sstevel * is for iopause to be enabled/disabled during axq suspend/resume. By
27811708Sstevel * setting the following axq flag to zero, axq will not enable iopause
27821708Sstevel * during suspend/resume, instead DR will call the axq_iopause_*_all
27831708Sstevel * interfaces during drmach_copy_rename.
27841708Sstevel */
27851708Sstevel axq_suspend_iopause = 0;
27861708Sstevel
27871708Sstevel mutex_exit(&drmach_i_lock);
27881708Sstevel
27891708Sstevel return (0);
27901708Sstevel }
27911708Sstevel
27921708Sstevel static void
drmach_fini(void)27931708Sstevel drmach_fini(void)
27941708Sstevel {
27951708Sstevel dev_info_t *rdip;
27961708Sstevel int hold, circ;
27971708Sstevel
27981708Sstevel if (drmach_initialized) {
27991708Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER);
28001708Sstevel drmach_array_dispose(drmach_boards, drmach_board_dispose);
28011708Sstevel drmach_boards = NULL;
28021708Sstevel rw_exit(&drmach_boards_rwlock);
28031708Sstevel
28041708Sstevel mutex_destroy(&drmach_slice_table_lock);
28051708Sstevel mutex_destroy(&drmach_xt_mb_lock);
28061708Sstevel mutex_destroy(&drmach_bus_sync_lock);
28071708Sstevel cv_destroy(&drmach_iocage_cv);
28081708Sstevel mutex_destroy(&drmach_iocage_lock);
28091708Sstevel
28101708Sstevel vmem_free(heap_arena, drmach_cpu_sram_va, PAGESIZE);
28111708Sstevel
28121708Sstevel /*
28131708Sstevel * Walk immediate children of the root devinfo node
28141708Sstevel * releasing holds acquired on branches in drmach_init()
28151708Sstevel */
28161708Sstevel hold = 0;
28171708Sstevel rdip = ddi_root_node();
28181708Sstevel
28191708Sstevel ndi_devi_enter(rdip, &circ);
28201708Sstevel ddi_walk_devs(ddi_get_child(rdip), hold_rele_branch, &hold);
28211708Sstevel ndi_devi_exit(rdip, circ);
28221708Sstevel
28231708Sstevel drmach_initialized = 0;
28241708Sstevel }
28251708Sstevel
282611311SSurya.Prakki@Sun.COM (void) drmach_mbox_fini();
28271708Sstevel if (drmach_xt_mb != NULL) {
28281708Sstevel vmem_free(static_alloc_arena, (void *)drmach_xt_mb,
28291708Sstevel drmach_xt_mb_size);
28301708Sstevel }
28311708Sstevel rw_destroy(&drmach_boards_rwlock);
28321708Sstevel mutex_destroy(&drmach_i_lock);
28331708Sstevel }
28341708Sstevel
28351708Sstevel static void
drmach_mem_read_madr(drmach_mem_t * mp,int bank,uint64_t * madr)28361708Sstevel drmach_mem_read_madr(drmach_mem_t *mp, int bank, uint64_t *madr)
28371708Sstevel {
28381708Sstevel kpreempt_disable();
28391708Sstevel
28401708Sstevel /* get register address, read madr value */
28411708Sstevel if (STARCAT_CPUID_TO_PORTID(CPU->cpu_id) == mp->dev.portid) {
28421708Sstevel *madr = lddmcdecode(DRMACH_MC_ASI_ADDR(mp, bank));
28431708Sstevel } else {
28441708Sstevel *madr = lddphysio(DRMACH_MC_ADDR(mp, bank));
28451708Sstevel }
28461708Sstevel
28471708Sstevel kpreempt_enable();
28481708Sstevel }
28491708Sstevel
28501708Sstevel
28511708Sstevel static uint64_t *
drmach_prep_mc_rename(uint64_t * p,int local,drmach_mem_t * mp,uint64_t current_basepa,uint64_t new_basepa)28521708Sstevel drmach_prep_mc_rename(uint64_t *p, int local,
28531708Sstevel drmach_mem_t *mp, uint64_t current_basepa, uint64_t new_basepa)
28541708Sstevel {
28551708Sstevel int bank;
28561708Sstevel
28571708Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
28581708Sstevel uint64_t madr, bank_offset;
28591708Sstevel
28601708Sstevel /* fetch mc's bank madr register value */
28611708Sstevel drmach_mem_read_madr(mp, bank, &madr);
28621708Sstevel if (madr & DRMACH_MC_VALID_MASK) {
28631708Sstevel uint64_t bankpa;
28641708Sstevel
28651708Sstevel bank_offset = (DRMACH_MC_UM_TO_PA(madr) |
28661708Sstevel DRMACH_MC_LM_TO_PA(madr)) - current_basepa;
28671708Sstevel bankpa = new_basepa + bank_offset;
28681708Sstevel
28691708Sstevel /* encode new base pa into madr */
28701708Sstevel madr &= ~DRMACH_MC_UM_MASK;
28711708Sstevel madr |= DRMACH_MC_PA_TO_UM(bankpa);
28721708Sstevel madr &= ~DRMACH_MC_LM_MASK;
28731708Sstevel madr |= DRMACH_MC_PA_TO_LM(bankpa);
28741708Sstevel
28751708Sstevel if (local)
28761708Sstevel *p++ = DRMACH_MC_ASI_ADDR(mp, bank);
28771708Sstevel else
28781708Sstevel *p++ = DRMACH_MC_ADDR(mp, bank);
28791708Sstevel
28801708Sstevel *p++ = madr;
28811708Sstevel }
28821708Sstevel }
28831708Sstevel
28841708Sstevel return (p);
28851708Sstevel }
28861708Sstevel
28871708Sstevel static uint64_t *
drmach_prep_schizo_script(uint64_t * p,drmach_mem_t * mp,uint64_t new_basepa)28881708Sstevel drmach_prep_schizo_script(uint64_t *p, drmach_mem_t *mp, uint64_t new_basepa)
28891708Sstevel {
28901708Sstevel drmach_board_t *bp;
28911708Sstevel int rv;
28921708Sstevel int idx;
28931708Sstevel drmachid_t id;
28941708Sstevel uint64_t last_scsr_pa = 0;
28951708Sstevel
28961708Sstevel /* memory is always in slot 0 */
28971708Sstevel ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
28981708Sstevel
28991708Sstevel /* look up slot 1 board on same expander */
29001708Sstevel idx = DRMACH_EXPSLOT2BNUM(DRMACH_BNUM2EXP(mp->dev.bp->bnum), 1);
29011708Sstevel rv = drmach_array_get(drmach_boards, idx, &id);
29021708Sstevel bp = id; /* bp will be NULL if board not found */
29031708Sstevel
29041708Sstevel /* look up should never be out of bounds */
29051708Sstevel ASSERT(rv == 0);
29061708Sstevel
29071708Sstevel /* nothing to do when board is not found or has no devices */
29081708Sstevel if (rv == -1 || bp == NULL || bp->devices == NULL)
29091708Sstevel return (p);
29101708Sstevel
29111708Sstevel rv = drmach_array_first(bp->devices, &idx, &id);
29121708Sstevel while (rv == 0) {
29131708Sstevel if (DRMACH_IS_IO_ID(id)) {
29141708Sstevel drmach_io_t *io = id;
29151708Sstevel
29161708Sstevel /*
29171708Sstevel * Skip all non-Schizo IO devices (only IO nodes
29181708Sstevel * that are Schizo devices have non-zero scsr_pa).
29191708Sstevel * Filter out "other" leaf to avoid writing to the
29201708Sstevel * same Schizo Control/Status Register twice.
29211708Sstevel */
29221708Sstevel if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
29231708Sstevel uint64_t scsr;
29241708Sstevel
29251708Sstevel scsr = lddphysio(io->scsr_pa);
29261708Sstevel scsr &= ~(DRMACH_LPA_BASE_MASK |
292711066Srafael.vanoni@sun.com DRMACH_LPA_BND_MASK);
29281708Sstevel scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
29291708Sstevel scsr |= DRMACH_PA_TO_LPA_BND(
293011066Srafael.vanoni@sun.com new_basepa + DRMACH_MEM_SLICE_SIZE);
29311708Sstevel
29321708Sstevel *p++ = io->scsr_pa;
29331708Sstevel *p++ = scsr;
29341708Sstevel
29351708Sstevel last_scsr_pa = io->scsr_pa;
29361708Sstevel }
29371708Sstevel }
29381708Sstevel rv = drmach_array_next(bp->devices, &idx, &id);
29391708Sstevel }
29401708Sstevel
29411708Sstevel return (p);
29421708Sstevel }
29431708Sstevel
29441708Sstevel /*
29451708Sstevel * For Panther MCs, append the MC idle reg address and drmach_mem_t pointer.
29461708Sstevel * The latter is returned when drmach_rename fails to idle a Panther MC and
29471708Sstevel * is used to identify the MC for error reporting.
29481708Sstevel */
29491708Sstevel static uint64_t *
drmach_prep_pn_mc_idle(uint64_t * p,drmach_mem_t * mp,int local)29501708Sstevel drmach_prep_pn_mc_idle(uint64_t *p, drmach_mem_t *mp, int local)
29511708Sstevel {
29521708Sstevel /* only slot 0 has memory */
29531708Sstevel ASSERT(DRMACH_BNUM2SLOT(mp->dev.bp->bnum) == 0);
29541708Sstevel ASSERT(IS_PANTHER(mp->dev.bp->cpu_impl));
29551708Sstevel
29561708Sstevel for (mp = mp->dev.bp->mem; mp != NULL; mp = mp->next) {
29571708Sstevel ASSERT(DRMACH_IS_MEM_ID(mp));
29581708Sstevel
29591708Sstevel if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
29601708Sstevel if (local) {
29611708Sstevel *p++ = ASI_EMU_ACT_STATUS_VA; /* local ASI */
29621708Sstevel *p++ = (uintptr_t)mp;
29631708Sstevel }
29641708Sstevel } else if (!local) {
29651708Sstevel *p++ = DRMACH_EMU_ACT_STATUS_ADDR(mp); /* PIO */
29661708Sstevel *p++ = (uintptr_t)mp;
29671708Sstevel }
29681708Sstevel }
29691708Sstevel
29701708Sstevel return (p);
29711708Sstevel }
29721708Sstevel
29731708Sstevel static sbd_error_t *
drmach_prep_rename_script(drmach_mem_t * s_mp,drmach_mem_t * t_mp,uint64_t t_slice_offset,caddr_t buf,int buflen)29741708Sstevel drmach_prep_rename_script(drmach_mem_t *s_mp, drmach_mem_t *t_mp,
29751708Sstevel uint64_t t_slice_offset, caddr_t buf, int buflen)
29761708Sstevel {
29771708Sstevel _NOTE(ARGUNUSED(buflen))
29781708Sstevel
29791708Sstevel uint64_t *p = (uint64_t *)buf, *q;
29801708Sstevel sbd_error_t *err;
29811708Sstevel int rv;
29821708Sstevel drmach_mem_t *mp, *skip_mp;
29831708Sstevel uint64_t s_basepa, t_basepa;
29841708Sstevel uint64_t s_new_basepa, t_new_basepa;
29851708Sstevel
29861708Sstevel /* verify supplied buffer space is adequate */
29871708Sstevel ASSERT(buflen >=
298811066Srafael.vanoni@sun.com /* addr for all possible MC banks */
298911066Srafael.vanoni@sun.com (sizeof (uint64_t) * 4 * 4 * 18) +
299011066Srafael.vanoni@sun.com /* list section terminator */
299111066Srafael.vanoni@sun.com (sizeof (uint64_t) * 1) +
299211066Srafael.vanoni@sun.com /* addr/id tuple for local Panther MC idle reg */
299311066Srafael.vanoni@sun.com (sizeof (uint64_t) * 2) +
299411066Srafael.vanoni@sun.com /* list section terminator */
299511066Srafael.vanoni@sun.com (sizeof (uint64_t) * 1) +
299611066Srafael.vanoni@sun.com /* addr/id tuple for 2 boards with 4 Panther MC idle regs */
299711066Srafael.vanoni@sun.com (sizeof (uint64_t) * 2 * 2 * 4) +
299811066Srafael.vanoni@sun.com /* list section terminator */
299911066Srafael.vanoni@sun.com (sizeof (uint64_t) * 1) +
300011066Srafael.vanoni@sun.com /* addr/val tuple for 1 proc with 4 MC banks */
300111066Srafael.vanoni@sun.com (sizeof (uint64_t) * 2 * 4) +
300211066Srafael.vanoni@sun.com /* list section terminator */
300311066Srafael.vanoni@sun.com (sizeof (uint64_t) * 1) +
300411066Srafael.vanoni@sun.com /* addr/val tuple for 2 boards w/ 2 schizos each */
300511066Srafael.vanoni@sun.com (sizeof (uint64_t) * 2 * 2 * 2) +
300611066Srafael.vanoni@sun.com /* addr/val tuple for 2 boards w/ 16 MC banks each */
300711066Srafael.vanoni@sun.com (sizeof (uint64_t) * 2 * 2 * 16) +
300811066Srafael.vanoni@sun.com /* list section terminator */
300911066Srafael.vanoni@sun.com (sizeof (uint64_t) * 1) +
301011066Srafael.vanoni@sun.com /* addr/val tuple for 18 AXQs w/ two slots each */
301111066Srafael.vanoni@sun.com (sizeof (uint64_t) * 2 * 2 * 18) +
301211066Srafael.vanoni@sun.com /* list section terminator */
301311066Srafael.vanoni@sun.com (sizeof (uint64_t) * 1) +
301411066Srafael.vanoni@sun.com /* list terminator */
301511066Srafael.vanoni@sun.com (sizeof (uint64_t) * 1));
30161708Sstevel
30171708Sstevel /* copy bank list to rename script */
30181708Sstevel mutex_enter(&drmach_bus_sync_lock);
30191708Sstevel for (q = drmach_bus_sync_list; *q; q++, p++)
30201708Sstevel *p = *q;
30211708Sstevel mutex_exit(&drmach_bus_sync_lock);
30221708Sstevel
30231708Sstevel /* list section terminator */
30241708Sstevel *p++ = 0;
30251708Sstevel
30261708Sstevel /*
30271708Sstevel * Write idle script for MC on this processor. A script will be
30281708Sstevel * produced only if this is a Panther processor on the source or
30291708Sstevel * target board.
30301708Sstevel */
30311708Sstevel if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
30321708Sstevel p = drmach_prep_pn_mc_idle(p, s_mp, 1);
30331708Sstevel
30341708Sstevel if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
30351708Sstevel p = drmach_prep_pn_mc_idle(p, t_mp, 1);
30361708Sstevel
30371708Sstevel /* list section terminator */
30381708Sstevel *p++ = 0;
30391708Sstevel
30401708Sstevel /*
30411708Sstevel * Write idle script for all other MCs on source and target
30421708Sstevel * Panther boards.
30431708Sstevel */
30441708Sstevel if (IS_PANTHER(s_mp->dev.bp->cpu_impl))
30451708Sstevel p = drmach_prep_pn_mc_idle(p, s_mp, 0);
30461708Sstevel
30471708Sstevel if (IS_PANTHER(t_mp->dev.bp->cpu_impl))
30481708Sstevel p = drmach_prep_pn_mc_idle(p, t_mp, 0);
30491708Sstevel
30501708Sstevel /* list section terminator */
30511708Sstevel *p++ = 0;
30521708Sstevel
30531708Sstevel /*
30541708Sstevel * Step 1: Write source base address to target MC
30551708Sstevel * with present bit off.
30561708Sstevel * Step 2: Now rewrite target reg with present bit on.
30571708Sstevel */
30581708Sstevel err = drmach_mem_get_base_physaddr(s_mp, &s_basepa);
30591708Sstevel ASSERT(err == NULL);
30601708Sstevel err = drmach_mem_get_base_physaddr(t_mp, &t_basepa);
30611708Sstevel ASSERT(err == NULL);
30621708Sstevel
30631708Sstevel /* exchange base pa. include slice offset in new target base pa */
30641708Sstevel s_new_basepa = t_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1);
30651708Sstevel t_new_basepa = (s_basepa & ~ (DRMACH_MEM_SLICE_SIZE - 1)) +
306611066Srafael.vanoni@sun.com t_slice_offset;
30671708Sstevel
30681708Sstevel DRMACH_PR("s_new_basepa 0x%lx\n", s_new_basepa);
30691708Sstevel DRMACH_PR("t_new_basepa 0x%lx\n", t_new_basepa);
30701708Sstevel
30711708Sstevel DRMACH_PR("preparing MC MADR rename script (master is CPU%d):\n",
307211066Srafael.vanoni@sun.com CPU->cpu_id);
30731708Sstevel
30741708Sstevel /*
30751708Sstevel * Write rename script for MC on this processor. A script will
30761708Sstevel * be produced only if this processor is on the source or target
30771708Sstevel * board.
30781708Sstevel */
30791708Sstevel
30801708Sstevel skip_mp = NULL;
30811708Sstevel mp = s_mp->dev.bp->mem;
30821708Sstevel while (mp != NULL && skip_mp == NULL) {
30831708Sstevel if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
30841708Sstevel skip_mp = mp;
30851708Sstevel p = drmach_prep_mc_rename(p, 1, mp, s_basepa,
30861708Sstevel s_new_basepa);
30871708Sstevel }
30881708Sstevel
30891708Sstevel mp = mp->next;
30901708Sstevel }
30911708Sstevel
30921708Sstevel mp = t_mp->dev.bp->mem;
30931708Sstevel while (mp != NULL && skip_mp == NULL) {
30941708Sstevel if (mp->dev.portid == STARCAT_CPUID_TO_PORTID(CPU->cpu_id)) {
30951708Sstevel skip_mp = mp;
30961708Sstevel p = drmach_prep_mc_rename(p, 1, mp, t_basepa,
30971708Sstevel t_new_basepa);
30981708Sstevel }
30991708Sstevel
31001708Sstevel mp = mp->next;
31011708Sstevel }
31021708Sstevel
31031708Sstevel /* list section terminator */
31041708Sstevel *p++ = 0;
31051708Sstevel
31061708Sstevel /*
31071708Sstevel * Write rename script for all other MCs on source and target
31081708Sstevel * boards.
31091708Sstevel */
31101708Sstevel
31111708Sstevel for (mp = s_mp->dev.bp->mem; mp; mp = mp->next) {
31121708Sstevel if (mp == skip_mp)
31131708Sstevel continue;
31141708Sstevel p = drmach_prep_mc_rename(p, 0, mp, s_basepa, s_new_basepa);
31151708Sstevel }
31161708Sstevel
31171708Sstevel for (mp = t_mp->dev.bp->mem; mp; mp = mp->next) {
31181708Sstevel if (mp == skip_mp)
31191708Sstevel continue;
31201708Sstevel p = drmach_prep_mc_rename(p, 0, mp, t_basepa, t_new_basepa);
31211708Sstevel }
31221708Sstevel
31231708Sstevel /* Write rename script for Schizo LPA_BASE/LPA_BND */
31241708Sstevel p = drmach_prep_schizo_script(p, s_mp, s_new_basepa);
31251708Sstevel p = drmach_prep_schizo_script(p, t_mp, t_new_basepa);
31261708Sstevel
31271708Sstevel /* list section terminator */
31281708Sstevel *p++ = 0;
31291708Sstevel
31301708Sstevel DRMACH_PR("preparing AXQ CASM rename script (EXP%d <> EXP%d):\n",
313111066Srafael.vanoni@sun.com DRMACH_BNUM2EXP(s_mp->dev.bp->bnum),
313211066Srafael.vanoni@sun.com DRMACH_BNUM2EXP(t_mp->dev.bp->bnum));
31331708Sstevel
31341708Sstevel rv = axq_do_casm_rename_script(&p,
313511066Srafael.vanoni@sun.com DRMACH_PA_TO_SLICE(s_new_basepa),
313611066Srafael.vanoni@sun.com DRMACH_PA_TO_SLICE(t_new_basepa));
31371708Sstevel if (rv == DDI_FAILURE)
31381708Sstevel return (DRMACH_INTERNAL_ERROR());
31391708Sstevel
31401708Sstevel /* list section & final terminator */
31411708Sstevel *p++ = 0;
31421708Sstevel *p++ = 0;
31431708Sstevel
31441708Sstevel #ifdef DEBUG
31451708Sstevel {
31461708Sstevel uint64_t *q = (uint64_t *)buf;
31471708Sstevel
31481708Sstevel /* paranoia */
31491708Sstevel ASSERT((caddr_t)p <= buf + buflen);
31501708Sstevel
31511708Sstevel DRMACH_PR("MC bank base pa list:\n");
31521708Sstevel while (*q) {
31531708Sstevel uint64_t a = *q++;
31541708Sstevel
31551708Sstevel DRMACH_PR("0x%lx\n", a);
31561708Sstevel }
31571708Sstevel
31581708Sstevel /* skip terminator */
31591708Sstevel q += 1;
31601708Sstevel
31611708Sstevel DRMACH_PR("local Panther MC idle reg (via ASI 0x4a):\n");
31621708Sstevel while (*q) {
31631708Sstevel DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
31641708Sstevel q += 2;
31651708Sstevel }
31661708Sstevel
31671708Sstevel /* skip terminator */
31681708Sstevel q += 1;
31691708Sstevel
31701708Sstevel DRMACH_PR("non-local Panther MC idle reg (via ASI 0x15):\n");
31711708Sstevel while (*q) {
31721708Sstevel DRMACH_PR("addr=0x%lx, mp=0x%lx\n", *q, *(q + 1));
31731708Sstevel q += 2;
31741708Sstevel }
31751708Sstevel
31761708Sstevel /* skip terminator */
31771708Sstevel q += 1;
31781708Sstevel
31791708Sstevel DRMACH_PR("MC reprogramming script (via ASI 0x72):\n");
31801708Sstevel while (*q) {
31811708Sstevel uint64_t r = *q++; /* register address */
31821708Sstevel uint64_t v = *q++; /* new register value */
31831708Sstevel
31841708Sstevel DRMACH_PR("0x%lx = 0x%lx, basepa 0x%lx\n",
318511311SSurya.Prakki@Sun.COM r, v, (long)(DRMACH_MC_UM_TO_PA(v)|
318611311SSurya.Prakki@Sun.COM DRMACH_MC_LM_TO_PA(v)));
31871708Sstevel }
31881708Sstevel
31891708Sstevel /* skip terminator */
31901708Sstevel q += 1;
31911708Sstevel
31921708Sstevel DRMACH_PR("MC/SCHIZO reprogramming script:\n");
31931708Sstevel while (*q) {
31941708Sstevel DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
31951708Sstevel q += 2;
31961708Sstevel }
31971708Sstevel
31981708Sstevel /* skip terminator */
31991708Sstevel q += 1;
32001708Sstevel
32011708Sstevel DRMACH_PR("AXQ reprogramming script:\n");
32021708Sstevel while (*q) {
32031708Sstevel DRMACH_PR("0x%lx = 0x%lx\n", *q, *(q + 1));
32041708Sstevel q += 2;
32051708Sstevel }
32061708Sstevel
32071708Sstevel /* verify final terminator is present */
32081708Sstevel ASSERT(*(q + 1) == 0);
32091708Sstevel
321011311SSurya.Prakki@Sun.COM DRMACH_PR("copy-rename script 0x%p, len %d\n",
321111311SSurya.Prakki@Sun.COM (void *)buf, (int)((intptr_t)p - (intptr_t)buf));
32121708Sstevel
32131708Sstevel if (drmach_debug)
32141708Sstevel DELAY(10000000);
32151708Sstevel }
32161708Sstevel #endif
32171708Sstevel
32181708Sstevel return (NULL);
32191708Sstevel }
32201708Sstevel
32211708Sstevel static void
drmach_prep_xt_mb_for_slice_update(drmach_board_t * bp,uchar_t slice)32221708Sstevel drmach_prep_xt_mb_for_slice_update(drmach_board_t *bp, uchar_t slice)
32231708Sstevel {
32241708Sstevel int rv;
32251708Sstevel
32261708Sstevel ASSERT(MUTEX_HELD(&drmach_xt_mb_lock));
32271708Sstevel
32281708Sstevel if (bp->devices) {
32291708Sstevel int d_idx;
32301708Sstevel drmachid_t d_id;
32311708Sstevel
32321708Sstevel rv = drmach_array_first(bp->devices, &d_idx, &d_id);
32331708Sstevel while (rv == 0) {
32341708Sstevel if (DRMACH_IS_CPU_ID(d_id)) {
32351708Sstevel drmach_cpu_t *cp = d_id;
32361708Sstevel processorid_t cpuid = cp->cpuid;
32371708Sstevel
32381708Sstevel mutex_enter(&cpu_lock);
32391708Sstevel if (cpu[cpuid] && cpu[cpuid]->cpu_flags)
32401708Sstevel drmach_xt_mb[cpuid] = 0x80 | slice;
32411708Sstevel mutex_exit(&cpu_lock);
32421708Sstevel }
32431708Sstevel rv = drmach_array_next(bp->devices, &d_idx, &d_id);
32441708Sstevel }
32451708Sstevel }
32461708Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
32471708Sstevel drmach_board_t *s1bp = NULL;
32481708Sstevel
32491708Sstevel rv = drmach_array_get(drmach_boards, bp->bnum + 1,
32501708Sstevel (void *) &s1bp);
32511708Sstevel if (rv == 0 && s1bp != NULL) {
32521708Sstevel ASSERT(DRMACH_IS_BOARD_ID(s1bp));
32531708Sstevel ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
32541708Sstevel drmach_prep_xt_mb_for_slice_update(s1bp, slice);
32551708Sstevel }
32561708Sstevel }
32571708Sstevel }
32581708Sstevel
32591708Sstevel sbd_error_t *
drmach_copy_rename_init(drmachid_t t_id,uint64_t t_slice_offset,drmachid_t s_id,struct memlist * c_ml,drmachid_t * cr_id)32601708Sstevel drmach_copy_rename_init(drmachid_t t_id, uint64_t t_slice_offset,
32611708Sstevel drmachid_t s_id, struct memlist *c_ml, drmachid_t *cr_id)
32621708Sstevel {
32631708Sstevel extern void drmach_rename(uint64_t *, uint_t *, uint64_t *);
32641708Sstevel extern void drmach_rename_end(void);
32651708Sstevel
32661708Sstevel drmach_mem_t *s_mp, *t_mp;
32671708Sstevel struct memlist *x_ml;
32681708Sstevel uint64_t off_mask, s_copybasepa, t_copybasepa, t_basepa;
32691708Sstevel int len;
32701708Sstevel caddr_t bp, wp;
32711708Sstevel uint_t *p, *q;
32721708Sstevel sbd_error_t *err;
32731708Sstevel tte_t *tte;
32741708Sstevel drmach_copy_rename_t *cr;
32751708Sstevel
32761708Sstevel if (!DRMACH_IS_MEM_ID(s_id))
32771708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
32781708Sstevel if (!DRMACH_IS_MEM_ID(t_id))
32791708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
32801708Sstevel s_mp = s_id;
32811708Sstevel t_mp = t_id;
32821708Sstevel
32831708Sstevel /* get starting physical address of target memory */
32841708Sstevel err = drmach_mem_get_base_physaddr(t_id, &t_basepa);
32851708Sstevel if (err)
32861708Sstevel return (err);
32871708Sstevel
32881708Sstevel /* calculate slice offset mask from slice size */
32891708Sstevel off_mask = DRMACH_MEM_SLICE_SIZE - 1;
32901708Sstevel
32911708Sstevel /* calculate source and target base pa */
3292*11474SJonathan.Adams@Sun.COM s_copybasepa = c_ml->ml_address;
3293*11474SJonathan.Adams@Sun.COM t_copybasepa =
3294*11474SJonathan.Adams@Sun.COM t_basepa + ((c_ml->ml_address & off_mask) - t_slice_offset);
32951708Sstevel
32961708Sstevel /* paranoia */
3297*11474SJonathan.Adams@Sun.COM ASSERT((c_ml->ml_address & off_mask) >= t_slice_offset);
32981708Sstevel
32991708Sstevel /* adjust copy memlist addresses to be relative to copy base pa */
33001708Sstevel x_ml = c_ml;
33011708Sstevel while (x_ml != NULL) {
3302*11474SJonathan.Adams@Sun.COM x_ml->ml_address -= s_copybasepa;
3303*11474SJonathan.Adams@Sun.COM x_ml = x_ml->ml_next;
33041708Sstevel }
33051708Sstevel
33061708Sstevel #ifdef DEBUG
33071708Sstevel {
33081708Sstevel uint64_t s_basepa, s_size, t_size;
33091708Sstevel
33101708Sstevel x_ml = c_ml;
3311*11474SJonathan.Adams@Sun.COM while (x_ml->ml_next != NULL)
3312*11474SJonathan.Adams@Sun.COM x_ml = x_ml->ml_next;
33131708Sstevel
33141708Sstevel DRMACH_PR("source copy span: base pa 0x%lx, end pa 0x%lx\n",
331511066Srafael.vanoni@sun.com s_copybasepa,
3316*11474SJonathan.Adams@Sun.COM s_copybasepa + x_ml->ml_address + x_ml->ml_size);
33171708Sstevel
33181708Sstevel DRMACH_PR("target copy span: base pa 0x%lx, end pa 0x%lx\n",
331911066Srafael.vanoni@sun.com t_copybasepa,
3320*11474SJonathan.Adams@Sun.COM t_copybasepa + x_ml->ml_address + x_ml->ml_size);
33211708Sstevel
33221708Sstevel DRMACH_PR("copy memlist (relative to copy base pa):\n");
33231708Sstevel DRMACH_MEMLIST_DUMP(c_ml);
33241708Sstevel
33251708Sstevel err = drmach_mem_get_base_physaddr(s_id, &s_basepa);
33261708Sstevel ASSERT(err == NULL);
33271708Sstevel
33281708Sstevel err = drmach_mem_get_size(s_id, &s_size);
33291708Sstevel ASSERT(err == NULL);
33301708Sstevel
33311708Sstevel err = drmach_mem_get_size(t_id, &t_size);
33321708Sstevel ASSERT(err == NULL);
33331708Sstevel
33341708Sstevel DRMACH_PR("current source base pa 0x%lx, size 0x%lx\n",
333511066Srafael.vanoni@sun.com s_basepa, s_size);
33361708Sstevel DRMACH_PR("current target base pa 0x%lx, size 0x%lx\n",
333711066Srafael.vanoni@sun.com t_basepa, t_size);
33381708Sstevel }
33391708Sstevel #endif /* DEBUG */
33401708Sstevel
33411708Sstevel /* Map in appropriate cpu sram page */
33421708Sstevel tte = &drmach_cpu_sram_tte[CPU->cpu_id];
33431708Sstevel ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) &&
33441708Sstevel TTE_IS_PRIVILEGED(tte) && TTE_IS_LOCKED(tte));
33452241Shuah sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
33462241Shuah sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
33471708Sstevel
33481708Sstevel bp = wp = drmach_cpu_sram_va;
33491708Sstevel
33501708Sstevel /* Make sure the rename routine will fit */
33511708Sstevel len = (ptrdiff_t)drmach_rename_end - (ptrdiff_t)drmach_rename;
33521708Sstevel ASSERT(wp + len < bp + PAGESIZE);
33531708Sstevel
33541708Sstevel /* copy text. standard bcopy not designed to work in nc space */
33551708Sstevel p = (uint_t *)wp;
33561708Sstevel q = (uint_t *)drmach_rename;
33571708Sstevel while (q < (uint_t *)drmach_rename_end)
33581708Sstevel *p++ = *q++;
33591708Sstevel
33601708Sstevel /* zero remainder. standard bzero not designed to work in nc space */
33611708Sstevel while (p < (uint_t *)(bp + PAGESIZE))
33621708Sstevel *p++ = 0;
33631708Sstevel
336411311SSurya.Prakki@Sun.COM DRMACH_PR("drmach_rename function 0x%p, len %d\n", (void *)wp, len);
33651708Sstevel wp += (len + 15) & ~15;
33661708Sstevel
336711066Srafael.vanoni@sun.com err = drmach_prep_rename_script(s_mp, t_mp, t_slice_offset, wp,
336811066Srafael.vanoni@sun.com PAGESIZE - (wp - bp));
33691708Sstevel if (err) {
33701708Sstevel cleanup:
33711708Sstevel xt_one(CPU->cpu_id, vtag_flushpage_tl1,
337211066Srafael.vanoni@sun.com (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
33731708Sstevel return (err);
33741708Sstevel }
33751708Sstevel
33761708Sstevel /* disable and flush CDC */
33771708Sstevel if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
33781708Sstevel axq_cdc_enable_all(); /* paranoia */
33791708Sstevel err = DRMACH_INTERNAL_ERROR();
33801708Sstevel goto cleanup;
33811708Sstevel }
33821708Sstevel
33831708Sstevel /* mark both memory units busy */
33841708Sstevel t_mp->dev.busy++;
33851708Sstevel s_mp->dev.busy++;
33861708Sstevel
33871708Sstevel cr = vmem_alloc(static_alloc_arena, sizeof (drmach_copy_rename_t),
33881708Sstevel VM_SLEEP);
33891708Sstevel cr->isa = (void *)drmach_copy_rename_init;
33901708Sstevel cr->data = wp;
33911708Sstevel cr->c_ml = c_ml;
33921708Sstevel cr->s_mp = s_mp;
33931708Sstevel cr->t_mp = t_mp;
33941708Sstevel cr->s_copybasepa = s_copybasepa;
33951708Sstevel cr->t_copybasepa = t_copybasepa;
33961708Sstevel cr->ecode = DRMACH_CR_OK;
33971708Sstevel
33981708Sstevel mutex_enter(&drmach_slice_table_lock);
33991708Sstevel
34001708Sstevel mutex_enter(&drmach_xt_mb_lock);
34011708Sstevel bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
34021708Sstevel
34031708Sstevel if (DRMACH_L1_SET_LPA(s_mp->dev.bp) && drmach_reprogram_lpa) {
34041708Sstevel drmach_prep_xt_mb_for_slice_update(s_mp->dev.bp,
340511066Srafael.vanoni@sun.com DRMACH_PA_TO_SLICE(t_copybasepa));
34061708Sstevel }
34071708Sstevel if (DRMACH_L1_SET_LPA(t_mp->dev.bp) && drmach_reprogram_lpa) {
34081708Sstevel drmach_prep_xt_mb_for_slice_update(t_mp->dev.bp,
340911066Srafael.vanoni@sun.com DRMACH_PA_TO_SLICE(s_copybasepa));
34101708Sstevel }
34111708Sstevel
34121708Sstevel *cr_id = cr;
34131708Sstevel return (NULL);
34141708Sstevel }
34151708Sstevel
34161708Sstevel int drmach_rename_count;
34171708Sstevel int drmach_rename_ntries;
34181708Sstevel
34191708Sstevel sbd_error_t *
drmach_copy_rename_fini(drmachid_t id)34201708Sstevel drmach_copy_rename_fini(drmachid_t id)
34211708Sstevel {
34221708Sstevel drmach_copy_rename_t *cr = id;
34231708Sstevel sbd_error_t *err = NULL;
34241708Sstevel dr_mbox_msg_t *obufp;
34251708Sstevel
34261708Sstevel ASSERT(cr->isa == (void *)drmach_copy_rename_init);
34271708Sstevel
34281708Sstevel axq_cdc_enable_all();
34291708Sstevel
34301708Sstevel xt_one(CPU->cpu_id, vtag_flushpage_tl1,
343111066Srafael.vanoni@sun.com (uint64_t)drmach_cpu_sram_va, (uint64_t)ksfmmup);
34321708Sstevel
34331708Sstevel switch (cr->ecode) {
34341708Sstevel case DRMACH_CR_OK:
34351708Sstevel break;
34361708Sstevel case DRMACH_CR_MC_IDLE_ERR: {
34371708Sstevel dev_info_t *dip = NULL;
34381708Sstevel drmach_mem_t *mp = (drmach_mem_t *)cr->earg;
34391708Sstevel char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
34401708Sstevel
34411708Sstevel ASSERT(DRMACH_IS_MEM_ID(mp));
34421708Sstevel
34431708Sstevel err = drmach_get_dip(mp, &dip);
34441708Sstevel
34451708Sstevel ASSERT(err == NULL);
34461708Sstevel ASSERT(dip != NULL);
34471708Sstevel
34481708Sstevel err = drerr_new(0, ESBD_MEMFAIL, NULL);
34491708Sstevel (void) ddi_pathname(dip, path);
34501708Sstevel cmn_err(CE_WARN, "failed to idle memory controller %s on %s: "
34511708Sstevel "copy-rename aborted", path, mp->dev.bp->cm.name);
34521708Sstevel kmem_free(path, MAXPATHLEN);
34531708Sstevel break;
34541708Sstevel }
34551708Sstevel case DRMACH_CR_IOPAUSE_ERR:
34561708Sstevel ASSERT((uintptr_t)cr->earg >= 0 &&
34571708Sstevel (uintptr_t)cr->earg < AXQ_MAX_EXP);
34581708Sstevel
34591708Sstevel err = drerr_new(0, ESBD_SUSPEND, "EX%d", (uintptr_t)cr->earg);
34601708Sstevel cmn_err(CE_WARN, "failed to idle EX%ld AXQ slot1 activity prior"
34611708Sstevel " to copy-rename", (uintptr_t)cr->earg);
34621708Sstevel break;
34631708Sstevel case DRMACH_CR_ONTRAP_ERR:
34641708Sstevel err = drerr_new(0, ESBD_MEMFAIL, NULL);
34651708Sstevel cmn_err(CE_WARN, "copy-rename aborted due to uncorrectable "
34661708Sstevel "memory error");
34671708Sstevel break;
34681708Sstevel default:
34691708Sstevel err = DRMACH_INTERNAL_ERROR();
34701708Sstevel cmn_err(CE_WARN, "unknown copy-rename error code (%d)\n",
34711708Sstevel cr->ecode);
34721708Sstevel break;
34731708Sstevel }
34741708Sstevel
34751708Sstevel #ifdef DEBUG
34761708Sstevel if ((DRMACH_L1_SET_LPA(cr->s_mp->dev.bp) ||
34771708Sstevel DRMACH_L1_SET_LPA(cr->t_mp->dev.bp)) && drmach_reprogram_lpa) {
34781708Sstevel int i;
34791708Sstevel for (i = 0; i < NCPU; i++) {
34801708Sstevel if (drmach_xt_mb[i])
34811708Sstevel DRMACH_PR("cpu%d ignored drmach_xt_mb", i);
34821708Sstevel }
34831708Sstevel }
34841708Sstevel #endif
34851708Sstevel mutex_exit(&drmach_xt_mb_lock);
34861708Sstevel
34871708Sstevel if (cr->c_ml != NULL)
34881708Sstevel memlist_delete(cr->c_ml);
34891708Sstevel
34901708Sstevel cr->t_mp->dev.busy--;
34911708Sstevel cr->s_mp->dev.busy--;
34921708Sstevel
34931708Sstevel if (err) {
34941708Sstevel mutex_exit(&drmach_slice_table_lock);
34951708Sstevel goto done;
34961708Sstevel }
34971708Sstevel
34981708Sstevel /* update casm shadow for target and source board */
34991708Sstevel drmach_slice_table_update(cr->t_mp->dev.bp, 0);
35001708Sstevel drmach_slice_table_update(cr->s_mp->dev.bp, 0);
35011708Sstevel mutex_exit(&drmach_slice_table_lock);
35021708Sstevel
35031708Sstevel mutex_enter(&drmach_bus_sync_lock);
35041708Sstevel drmach_bus_sync_list_update();
35051708Sstevel mutex_exit(&drmach_bus_sync_lock);
35061708Sstevel
35071708Sstevel /*
35081708Sstevel * Make a good-faith effort to notify the SC about the copy-rename, but
35091708Sstevel * don't worry if it fails, since a subsequent claim/unconfig/unclaim
35101708Sstevel * will duplicate the update.
35111708Sstevel */
35121708Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
35131708Sstevel mutex_enter(&drmach_slice_table_lock);
35141708Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_uc.mem_slice);
35151708Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_uc.mem_regs);
35161708Sstevel mutex_exit(&drmach_slice_table_lock);
35171708Sstevel (void) drmach_mbox_trans(DRMSG_UNCONFIG, cr->s_mp->dev.bp->bnum,
351811066Srafael.vanoni@sun.com (caddr_t)obufp, sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
35191708Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t));
35201708Sstevel
35211708Sstevel done:
35221708Sstevel vmem_free(static_alloc_arena, cr, sizeof (drmach_copy_rename_t));
35231708Sstevel
35241708Sstevel DRMACH_PR("waited %d out of %d tries for drmach_rename_wait on %d cpus",
352511066Srafael.vanoni@sun.com drmach_rename_ntries, drmach_cpu_ntries, drmach_rename_count);
35261708Sstevel
35271708Sstevel return (err);
35281708Sstevel }
35291708Sstevel
35301708Sstevel int drmach_slow_copy = 0;
35311708Sstevel
35321708Sstevel void
drmach_copy_rename(drmachid_t id)35331708Sstevel drmach_copy_rename(drmachid_t id)
35341708Sstevel {
35351708Sstevel extern uint_t getpstate(void);
35361708Sstevel extern void setpstate(uint_t);
35371708Sstevel
35381708Sstevel extern xcfunc_t drmach_rename_wait;
35391708Sstevel extern xcfunc_t drmach_rename_done;
35401708Sstevel extern xcfunc_t drmach_rename_abort;
35411708Sstevel
35421708Sstevel drmach_copy_rename_t *cr = id;
35431708Sstevel uint64_t neer;
35441708Sstevel struct memlist *ml;
35451708Sstevel int i, count;
35461708Sstevel int csize, lnsize;
35471708Sstevel uint64_t caddr;
35481708Sstevel cpuset_t cpuset;
35491708Sstevel uint_t pstate;
35501708Sstevel uint32_t exp = 0;
35511708Sstevel on_trap_data_t otd;
35521708Sstevel xcfunc_t *drmach_end_wait_xcall = drmach_rename_done;
35531708Sstevel
35541708Sstevel ASSERT(cr->isa == (void *)drmach_copy_rename_init);
35551708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
35561708Sstevel ASSERT(cr->ecode == DRMACH_CR_OK);
35571708Sstevel
35581708Sstevel /*
35591708Sstevel * Prevent slot1 IO from accessing Safari memory bus.
35601708Sstevel */
35611708Sstevel if (axq_iopause_enable_all(&exp) != DDI_SUCCESS) {
35621708Sstevel ASSERT(exp >= 0 && exp < AXQ_MAX_EXP);
35631708Sstevel cr->ecode = DRMACH_CR_IOPAUSE_ERR;
35641708Sstevel cr->earg = (void *)(uintptr_t)exp;
35651708Sstevel return;
35661708Sstevel }
35671708Sstevel
35681708Sstevel cpuset = cpu_ready_set;
35691708Sstevel CPUSET_DEL(cpuset, CPU->cpu_id);
35701708Sstevel count = ncpus - 1;
35711708Sstevel drmach_rename_count = count; /* for debug */
35721708Sstevel
35731708Sstevel drmach_xt_ready = 0;
35741708Sstevel xt_some(cpuset, drmach_rename_wait, NULL, NULL);
35751708Sstevel
35761708Sstevel for (i = 0; i < drmach_cpu_ntries; i++) {
35771708Sstevel if (drmach_xt_ready == count)
35781708Sstevel break;
35791708Sstevel DELAY(drmach_cpu_delay);
35801708Sstevel }
35811708Sstevel
35821708Sstevel drmach_rename_ntries = i; /* for debug */
35831708Sstevel
35841708Sstevel drmach_xt_ready = 0; /* steal the line back */
35851708Sstevel for (i = 0; i < NCPU; i++) /* steal the line back, preserve data */
35861708Sstevel drmach_xt_mb[i] = drmach_xt_mb[i];
35871708Sstevel
35881708Sstevel caddr = drmach_iocage_paddr;
35891708Sstevel csize = cpunodes[CPU->cpu_id].ecache_size;
35901708Sstevel lnsize = cpunodes[CPU->cpu_id].ecache_linesize;
35911708Sstevel
35921708Sstevel /* disable CE reporting */
35931708Sstevel neer = get_error_enable();
35941708Sstevel set_error_enable(neer & ~EN_REG_CEEN);
35951708Sstevel
35961708Sstevel /* disable interrupts (paranoia) */
35971708Sstevel pstate = getpstate();
35981708Sstevel setpstate(pstate & ~PSTATE_IE);
35991708Sstevel
36001708Sstevel /*
36011708Sstevel * Execute copy-rename under on_trap to protect against a panic due
36021708Sstevel * to an uncorrectable error. Instead, DR will abort the copy-rename
36031708Sstevel * operation and rely on the OS to do the error reporting.
36041708Sstevel *
36051708Sstevel * In general, trap handling on any cpu once the copy begins
36061708Sstevel * can result in an inconsistent memory image on the target.
36071708Sstevel */
36081708Sstevel if (on_trap(&otd, OT_DATA_EC)) {
36091708Sstevel cr->ecode = DRMACH_CR_ONTRAP_ERR;
36101708Sstevel goto copy_rename_end;
36111708Sstevel }
36121708Sstevel
36131708Sstevel /*
36141708Sstevel * DO COPY.
36151708Sstevel */
3616*11474SJonathan.Adams@Sun.COM for (ml = cr->c_ml; ml; ml = ml->ml_next) {
36171708Sstevel uint64_t s_pa, t_pa;
36181708Sstevel uint64_t nbytes;
36191708Sstevel
3620*11474SJonathan.Adams@Sun.COM s_pa = cr->s_copybasepa + ml->ml_address;
3621*11474SJonathan.Adams@Sun.COM t_pa = cr->t_copybasepa + ml->ml_address;
3622*11474SJonathan.Adams@Sun.COM nbytes = ml->ml_size;
36231708Sstevel
36241708Sstevel while (nbytes != 0ull) {
36251708Sstevel /* copy 32 bytes at src_pa to dst_pa */
36261708Sstevel bcopy32_il(s_pa, t_pa);
36271708Sstevel
36281708Sstevel /* increment by 32 bytes */
36291708Sstevel s_pa += (4 * sizeof (uint64_t));
36301708Sstevel t_pa += (4 * sizeof (uint64_t));
36311708Sstevel
36321708Sstevel /* decrement by 32 bytes */
36331708Sstevel nbytes -= (4 * sizeof (uint64_t));
36341708Sstevel
36351708Sstevel if (drmach_slow_copy) { /* for debug */
36361708Sstevel uint64_t i = 13 * 50;
36377799SRichard.Bean@Sun.COM while (i--)
36387799SRichard.Bean@Sun.COM ;
36391708Sstevel }
36401708Sstevel }
36411708Sstevel }
36421708Sstevel
36431708Sstevel /*
36441708Sstevel * XXX CHEETAH SUPPORT
36451708Sstevel * For cheetah, we need to grab the iocage lock since iocage
36461708Sstevel * memory is used for e$ flush.
36471708Sstevel *
36481708Sstevel * NOTE: This code block is dangerous at this point in the
36491708Sstevel * copy-rename operation. It modifies memory after the copy
36501708Sstevel * has taken place which means that any persistent state will
36511708Sstevel * be abandoned after the rename operation. The code is also
36521708Sstevel * performing thread synchronization at a time when all but
36531708Sstevel * one processors are paused. This is a potential deadlock
36541708Sstevel * situation.
36551708Sstevel *
36561708Sstevel * This code block must be moved to drmach_copy_rename_init.
36571708Sstevel */
36581708Sstevel if (drmach_is_cheetah) {
36591708Sstevel mutex_enter(&drmach_iocage_lock);
36601708Sstevel while (drmach_iocage_is_busy)
36611708Sstevel cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
36621708Sstevel drmach_iocage_is_busy = 1;
36631708Sstevel drmach_iocage_mem_scrub(ecache_size * 2);
36641708Sstevel mutex_exit(&drmach_iocage_lock);
36651708Sstevel }
36661708Sstevel
36671708Sstevel /*
36681708Sstevel * bcopy32_il is implemented as a series of ldxa/stxa via
36691708Sstevel * ASI_MEM instructions. Following the copy loop, the E$
36701708Sstevel * of the master (this) processor will have lines in state
36711708Sstevel * O that correspond to lines of home memory in state gI.
36721708Sstevel * An E$ flush is necessary to commit these lines before
36731708Sstevel * proceeding with the rename operation.
36741708Sstevel *
36751708Sstevel * Flushing the E$ will automatically flush the W$, but
36761708Sstevel * the D$ and I$ must be flushed separately and explicitly.
36771708Sstevel */
36781708Sstevel flush_ecache_il(caddr, csize, lnsize); /* inline version */
36791708Sstevel
36801708Sstevel /*
36811708Sstevel * Each line of home memory is now in state gM, except in
36821708Sstevel * the case of a cheetah processor when the E$ flush area
36831708Sstevel * is included within the copied region. In such a case,
36841708Sstevel * the lines of home memory for the upper half of the
36851708Sstevel * flush area are in state gS.
36861708Sstevel *
36871708Sstevel * Each line of target memory is in state gM.
36881708Sstevel *
36891708Sstevel * Each line of this processor's E$ is in state I, except
36901708Sstevel * those of a cheetah processor. All lines of a cheetah
36911708Sstevel * processor's E$ are in state S and correspond to the lines
36921708Sstevel * in upper half of the E$ flush area.
36931708Sstevel *
36941708Sstevel * It is vital at this point that none of the lines in the
36951708Sstevel * home or target memories are in state gI and that none
36961708Sstevel * of the lines in this processor's E$ are in state O or Os.
36971708Sstevel * A single instance of such a condition will cause loss of
36981708Sstevel * coherency following the rename operation.
36991708Sstevel */
37001708Sstevel
37011708Sstevel /*
37021708Sstevel * Rename
37031708Sstevel */
37041708Sstevel (*(void(*)())drmach_cpu_sram_va)(cr->data, &cr->ecode, &cr->earg);
37051708Sstevel
37061708Sstevel /*
37071708Sstevel * Rename operation complete. The physical address space
37081708Sstevel * of the home and target memories have been swapped, the
37091708Sstevel * routing data in the respective CASM entries have been
37101708Sstevel * swapped, and LPA settings in the processor and schizo
37111708Sstevel * devices have been reprogrammed accordingly.
37121708Sstevel *
37131708Sstevel * In the case of a cheetah processor, the E$ remains
37141708Sstevel * populated with lines in state S that correspond to the
37151708Sstevel * lines in the former home memory. Now that the physical
37161708Sstevel * addresses have been swapped, these E$ lines correspond
37171708Sstevel * to lines in the new home memory which are in state gM.
37181708Sstevel * This combination is invalid. An additional E$ flush is
37191708Sstevel * necessary to restore coherency. The E$ flush will cause
37201708Sstevel * the lines of the new home memory for the flush region
37211708Sstevel * to transition from state gM to gS. The former home memory
37221708Sstevel * remains unmodified. This additional E$ flush has no effect
37231708Sstevel * on a cheetah+ processor.
37241708Sstevel */
37251708Sstevel flush_ecache_il(caddr, csize, lnsize); /* inline version */
37261708Sstevel
37271708Sstevel /*
37281708Sstevel * The D$ and I$ must be flushed to ensure that coherency is
37291708Sstevel * maintained. Any line in a cache that is in the valid
37301708Sstevel * state has its corresponding line of the new home memory
37311708Sstevel * in the gM state. This is an invalid condition. When the
37321708Sstevel * flushes are complete the cache line states will be
37331708Sstevel * resynchronized with those in the new home memory.
37341708Sstevel */
37351708Sstevel flush_icache_il(); /* inline version */
37361708Sstevel flush_dcache_il(); /* inline version */
37371708Sstevel flush_pcache_il(); /* inline version */
37381708Sstevel
37391708Sstevel copy_rename_end:
37401708Sstevel
37411708Sstevel no_trap();
37421708Sstevel
37431708Sstevel /* enable interrupts */
37441708Sstevel setpstate(pstate);
37451708Sstevel
37461708Sstevel /* enable CE reporting */
37471708Sstevel set_error_enable(neer);
37481708Sstevel
37491708Sstevel if (cr->ecode != DRMACH_CR_OK)
37501708Sstevel drmach_end_wait_xcall = drmach_rename_abort;
37511708Sstevel
37521708Sstevel /*
37531708Sstevel * XXX CHEETAH SUPPORT
37541708Sstevel */
37551708Sstevel if (drmach_is_cheetah) {
37561708Sstevel mutex_enter(&drmach_iocage_lock);
37571708Sstevel drmach_iocage_mem_scrub(ecache_size * 2);
37581708Sstevel drmach_iocage_is_busy = 0;
37591708Sstevel cv_signal(&drmach_iocage_cv);
37601708Sstevel mutex_exit(&drmach_iocage_lock);
37611708Sstevel }
37621708Sstevel
37631708Sstevel axq_iopause_disable_all();
37641708Sstevel
37651708Sstevel xt_some(cpuset, drmach_end_wait_xcall, NULL, NULL);
37661708Sstevel }
37671708Sstevel
37681708Sstevel static void drmach_io_dispose(drmachid_t);
37691708Sstevel static sbd_error_t *drmach_io_release(drmachid_t);
37701708Sstevel static sbd_error_t *drmach_io_status(drmachid_t, drmach_status_t *);
37711708Sstevel
37721708Sstevel static sbd_error_t *
drmach_pci_new(drmach_device_t * proto,drmachid_t * idp)37731708Sstevel drmach_pci_new(drmach_device_t *proto, drmachid_t *idp)
37741708Sstevel {
37751708Sstevel drmach_node_t *node = proto->node;
37761708Sstevel sbd_error_t *err;
37771708Sstevel drmach_reg_t regs[3];
37781708Sstevel int rv;
37791708Sstevel int len = 0;
37801708Sstevel
37811708Sstevel rv = node->n_getproplen(node, "reg", &len);
37821708Sstevel if (rv != 0 || len != sizeof (regs)) {
37831708Sstevel sbd_error_t *err;
37841708Sstevel
37851708Sstevel /* pci nodes are expected to have regs */
37861708Sstevel err = drerr_new(1, ESTC_GETPROP,
378711066Srafael.vanoni@sun.com "Device Node 0x%x: property %s",
378811066Srafael.vanoni@sun.com (uint_t)node->get_dnode(node), "reg");
37891708Sstevel return (err);
37901708Sstevel }
37911708Sstevel
37921708Sstevel rv = node->n_getprop(node, "reg", (void *)regs, sizeof (regs));
37931708Sstevel if (rv) {
37941708Sstevel sbd_error_t *err;
37951708Sstevel
37961708Sstevel err = drerr_new(1, ESTC_GETPROP,
379711066Srafael.vanoni@sun.com "Device Node 0x%x: property %s",
379811066Srafael.vanoni@sun.com (uint_t)node->get_dnode(node), "reg");
37991708Sstevel
38001708Sstevel return (err);
38011708Sstevel }
38021708Sstevel
38031708Sstevel /*
38041708Sstevel * Fix up unit number so that Leaf A has a lower unit number
38051708Sstevel * than Leaf B.
38061708Sstevel */
38071708Sstevel if ((proto->portid % 2) != 0) {
38081708Sstevel if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
38091708Sstevel proto->unum = 0;
38101708Sstevel else
38111708Sstevel proto->unum = 1;
38121708Sstevel } else {
38131708Sstevel if ((regs[0].reg_addr_lo & 0x700000) == 0x700000)
38141708Sstevel proto->unum = 2;
38151708Sstevel else
38161708Sstevel proto->unum = 3;
38171708Sstevel }
38181708Sstevel
38191708Sstevel err = drmach_io_new(proto, idp);
38201708Sstevel if (err == NULL) {
38211708Sstevel drmach_io_t *self = *idp;
38221708Sstevel
38231708Sstevel /* reassemble 64-bit base address */
38241708Sstevel self->scsr_pa = (uint64_t)regs[1].reg_addr_hi << 32;
38251708Sstevel self->scsr_pa |= (uint64_t)regs[1].reg_addr_lo;
38261708Sstevel }
38271708Sstevel
38281708Sstevel return (err);
38291708Sstevel }
38301708Sstevel
38311708Sstevel static sbd_error_t *
drmach_io_new(drmach_device_t * proto,drmachid_t * idp)38321708Sstevel drmach_io_new(drmach_device_t *proto, drmachid_t *idp)
38331708Sstevel {
38341708Sstevel drmach_io_t *ip;
38351708Sstevel
38361708Sstevel ip = kmem_zalloc(sizeof (drmach_io_t), KM_SLEEP);
38371708Sstevel bcopy(proto, &ip->dev, sizeof (ip->dev));
38381708Sstevel ip->dev.node = drmach_node_dup(proto->node);
38391708Sstevel ip->dev.cm.isa = (void *)drmach_io_new;
38401708Sstevel ip->dev.cm.dispose = drmach_io_dispose;
38411708Sstevel ip->dev.cm.release = drmach_io_release;
38421708Sstevel ip->dev.cm.status = drmach_io_status;
38431708Sstevel
384411311SSurya.Prakki@Sun.COM (void) snprintf(ip->dev.cm.name, sizeof (ip->dev.cm.name), "%s%d",
384511066Srafael.vanoni@sun.com ip->dev.type, ip->dev.unum);
38461708Sstevel
38471708Sstevel *idp = (drmachid_t)ip;
38481708Sstevel return (NULL);
38491708Sstevel }
38501708Sstevel
38511708Sstevel static void
drmach_io_dispose(drmachid_t id)38521708Sstevel drmach_io_dispose(drmachid_t id)
38531708Sstevel {
38541708Sstevel drmach_io_t *self;
38551708Sstevel
38561708Sstevel ASSERT(DRMACH_IS_IO_ID(id));
38571708Sstevel
38581708Sstevel self = id;
38591708Sstevel if (self->dev.node)
38601708Sstevel drmach_node_dispose(self->dev.node);
38611708Sstevel
38621708Sstevel kmem_free(self, sizeof (*self));
38631708Sstevel }
38641708Sstevel
38651708Sstevel /*ARGSUSED*/
38661708Sstevel sbd_error_t *
drmach_pre_op(int cmd,drmachid_t id,drmach_opts_t * opts)38671708Sstevel drmach_pre_op(int cmd, drmachid_t id, drmach_opts_t *opts)
38681708Sstevel {
38691708Sstevel drmach_board_t *bp = (drmach_board_t *)id;
38701708Sstevel sbd_error_t *err = NULL;
38711708Sstevel
38721708Sstevel if (id && DRMACH_IS_BOARD_ID(id)) {
38731708Sstevel switch (cmd) {
38741708Sstevel case SBD_CMD_TEST:
38751708Sstevel case SBD_CMD_STATUS:
38761708Sstevel case SBD_CMD_GETNCM:
38771708Sstevel break;
38781708Sstevel case SBD_CMD_CONNECT:
38791708Sstevel if (bp->connected)
38801708Sstevel err = drerr_new(0, ESBD_STATE, NULL);
38811708Sstevel
38821708Sstevel if (bp->cond == SBD_COND_UNUSABLE)
38831708Sstevel err = drerr_new(0,
388411066Srafael.vanoni@sun.com ESBD_FATAL_STATE, NULL);
38851708Sstevel break;
38861708Sstevel case SBD_CMD_DISCONNECT:
38871708Sstevel if (!bp->connected)
38881708Sstevel err = drerr_new(0, ESBD_STATE, NULL);
38891708Sstevel
38901708Sstevel if (bp->cond == SBD_COND_UNUSABLE)
38911708Sstevel err = drerr_new(0,
389211066Srafael.vanoni@sun.com ESBD_FATAL_STATE, NULL);
38931708Sstevel break;
38941708Sstevel default:
38951708Sstevel if (bp->cond == SBD_COND_UNUSABLE)
38961708Sstevel err = drerr_new(0,
389711066Srafael.vanoni@sun.com ESBD_FATAL_STATE, NULL);
38981708Sstevel break;
38991708Sstevel
39001708Sstevel }
39011708Sstevel }
39021708Sstevel
39031708Sstevel return (err);
39041708Sstevel }
39051708Sstevel
39061708Sstevel /*ARGSUSED*/
39071708Sstevel sbd_error_t *
drmach_post_op(int cmd,drmachid_t id,drmach_opts_t * opts)39081708Sstevel drmach_post_op(int cmd, drmachid_t id, drmach_opts_t *opts)
39091708Sstevel {
39101708Sstevel return (NULL);
39111708Sstevel }
39121708Sstevel
39131708Sstevel sbd_error_t *
drmach_board_assign(int bnum,drmachid_t * id)39141708Sstevel drmach_board_assign(int bnum, drmachid_t *id)
39151708Sstevel {
39161708Sstevel sbd_error_t *err = NULL;
39171708Sstevel caddr_t obufp;
39181708Sstevel
39191708Sstevel if (!drmach_initialized && drmach_init() == -1) {
39201708Sstevel err = DRMACH_INTERNAL_ERROR();
39211708Sstevel }
39221708Sstevel
39231708Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER);
39241708Sstevel
39251708Sstevel if (!err) {
39261708Sstevel if (drmach_array_get(drmach_boards, bnum, id) == -1) {
39271708Sstevel err = drerr_new(0, ESTC_BNUM, "%d", bnum);
39281708Sstevel } else {
39291708Sstevel drmach_board_t *bp;
39301708Sstevel
39311708Sstevel if (*id)
39321708Sstevel rw_downgrade(&drmach_boards_rwlock);
39331708Sstevel
39341708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
39351708Sstevel err = drmach_mbox_trans(DRMSG_ASSIGN, bnum, obufp,
393611066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
39371708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
39381708Sstevel
39391708Sstevel if (!err) {
39401708Sstevel bp = *id;
39411708Sstevel if (!*id)
39421708Sstevel bp = *id =
39431708Sstevel (drmachid_t)drmach_board_new(bnum);
39441708Sstevel bp->assigned = 1;
39451708Sstevel }
39461708Sstevel }
39471708Sstevel }
39481708Sstevel rw_exit(&drmach_boards_rwlock);
39491708Sstevel return (err);
39501708Sstevel }
39511708Sstevel
39521708Sstevel static uint_t
drmach_board_non_panther_cpus(gdcd_t * gdcd,uint_t exp,uint_t slot)39531708Sstevel drmach_board_non_panther_cpus(gdcd_t *gdcd, uint_t exp, uint_t slot)
39541708Sstevel {
39551708Sstevel uint_t port, port_start, port_end;
39561708Sstevel uint_t non_panther_cpus = 0;
39571708Sstevel uint_t impl;
39581708Sstevel
39591708Sstevel ASSERT(gdcd != NULL);
39601708Sstevel
39611708Sstevel /*
39621708Sstevel * Determine PRD port indices based on slot location.
39631708Sstevel */
39641708Sstevel switch (slot) {
39651708Sstevel case 0:
39661708Sstevel port_start = 0;
39671708Sstevel port_end = 3;
39681708Sstevel break;
39691708Sstevel case 1:
39701708Sstevel port_start = 4;
39711708Sstevel port_end = 5;
39721708Sstevel break;
39731708Sstevel default:
39741708Sstevel ASSERT(0);
39751708Sstevel /* check all */
39761708Sstevel port_start = 0;
39771708Sstevel port_end = 5;
39781708Sstevel break;
39791708Sstevel }
39801708Sstevel
39811708Sstevel for (port = port_start; port <= port_end; port++) {
39821708Sstevel if (gdcd->dcd_prd[exp][port].prd_ptype == SAFPTYPE_CPU &&
39831708Sstevel RSV_GOOD(gdcd->dcd_prd[exp][port].prd_prsv)) {
39841708Sstevel /*
39851708Sstevel * This Safari port passed POST and represents a
39861708Sstevel * cpu, so check the implementation.
39871708Sstevel */
39881708Sstevel impl = (gdcd->dcd_prd[exp][port].prd_ver_reg >> 32)
39891708Sstevel & 0xffff;
39901708Sstevel
39911708Sstevel switch (impl) {
39921708Sstevel case CHEETAH_IMPL:
39931708Sstevel case CHEETAH_PLUS_IMPL:
39941708Sstevel case JAGUAR_IMPL:
39951708Sstevel non_panther_cpus++;
39961708Sstevel break;
39971708Sstevel case PANTHER_IMPL:
39981708Sstevel break;
39991708Sstevel default:
40001708Sstevel ASSERT(0);
40011708Sstevel non_panther_cpus++;
40021708Sstevel break;
40031708Sstevel }
40041708Sstevel }
40051708Sstevel }
40061708Sstevel
40071708Sstevel DRMACH_PR("drmach_board_non_panther_cpus: exp=%d, slot=%d, "
40081708Sstevel "non_panther_cpus=%d", exp, slot, non_panther_cpus);
40091708Sstevel
40101708Sstevel return (non_panther_cpus);
40111708Sstevel }
40121708Sstevel
40131708Sstevel sbd_error_t *
drmach_board_connect(drmachid_t id,drmach_opts_t * opts)40141708Sstevel drmach_board_connect(drmachid_t id, drmach_opts_t *opts)
40151708Sstevel {
40161708Sstevel _NOTE(ARGUNUSED(opts))
40171708Sstevel
40181708Sstevel drmach_board_t *bp = (drmach_board_t *)id;
40191708Sstevel sbd_error_t *err;
40201708Sstevel dr_mbox_msg_t *obufp;
40211708Sstevel gdcd_t *gdcd = NULL;
40221708Sstevel uint_t exp, slot;
40231708Sstevel sc_gptwocfg_cookie_t scc;
40241708Sstevel int panther_pages_enabled;
40251708Sstevel
40261708Sstevel if (!DRMACH_IS_BOARD_ID(id))
40271708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
40281708Sstevel
40291708Sstevel /*
40301708Sstevel * Build the casm info portion of the CLAIM message.
40311708Sstevel */
40321708Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
40331708Sstevel mutex_enter(&drmach_slice_table_lock);
40341708Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_cr.mem_slice);
40351708Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_cr.mem_regs);
40361708Sstevel mutex_exit(&drmach_slice_table_lock);
40371708Sstevel err = drmach_mbox_trans(DRMSG_CLAIM, bp->bnum, (caddr_t)obufp,
403811066Srafael.vanoni@sun.com sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
40391708Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t));
40401708Sstevel
40411708Sstevel if (err) {
40421708Sstevel /*
40431708Sstevel * if mailbox timeout or unrecoverable error from SC,
40441708Sstevel * board cannot be touched. Mark the status as
40451708Sstevel * unusable.
40461708Sstevel */
40471708Sstevel if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
404811066Srafael.vanoni@sun.com (err->e_code == ESTC_MBXRPLY))
404911066Srafael.vanoni@sun.com bp->cond = SBD_COND_UNUSABLE;
40501708Sstevel return (err);
40511708Sstevel }
40521708Sstevel
40531708Sstevel gdcd = drmach_gdcd_new();
40541708Sstevel if (gdcd == NULL) {
40551708Sstevel cmn_err(CE_WARN, "failed to read GDCD info for %s\n",
40561708Sstevel bp->cm.name);
40571708Sstevel return (DRMACH_INTERNAL_ERROR());
40581708Sstevel }
40591708Sstevel
40601708Sstevel /*
40611708Sstevel * Read CPU SRAM DR buffer offset from GDCD.
40621708Sstevel */
40631708Sstevel exp = DRMACH_BNUM2EXP(bp->bnum);
40641708Sstevel slot = DRMACH_BNUM2SLOT(bp->bnum);
40651708Sstevel bp->stardrb_offset =
40661708Sstevel gdcd->dcd_slot[exp][slot].l1ss_cpu_drblock_xwd_offset << 3;
40671708Sstevel DRMACH_PR("%s: stardrb_offset=0x%lx\n", bp->cm.name,
40681708Sstevel bp->stardrb_offset);
40691708Sstevel
40701708Sstevel /*
40711708Sstevel * Read board LPA setting from GDCD.
40721708Sstevel */
40731708Sstevel bp->flags &= ~DRMACH_NULL_PROC_LPA;
40741708Sstevel if (gdcd->dcd_slot[exp][slot].l1ss_flags &
40751708Sstevel L1SSFLG_THIS_L1_NULL_PROC_LPA) {
40761708Sstevel bp->flags |= DRMACH_NULL_PROC_LPA;
40771708Sstevel DRMACH_PR("%s: NULL proc LPA\n", bp->cm.name);
40781708Sstevel }
40791708Sstevel
40801708Sstevel /*
40811708Sstevel * XXX Until the Solaris large pages support heterogeneous cpu
40821708Sstevel * domains, DR needs to prevent the addition of non-Panther cpus
40831708Sstevel * to an all-Panther domain with large pages enabled.
40841708Sstevel */
40851708Sstevel panther_pages_enabled = (page_num_pagesizes() > DEFAULT_MMU_PAGE_SIZES);
40861708Sstevel if (drmach_board_non_panther_cpus(gdcd, exp, slot) > 0 &&
40871708Sstevel panther_pages_enabled && drmach_large_page_restriction) {
40881708Sstevel cmn_err(CE_WARN, "Domain shutdown is required to add a non-"
40891708Sstevel "UltraSPARC-IV+ board into an all UltraSPARC-IV+ domain");
40901708Sstevel err = drerr_new(0, ESTC_SUPPORT, NULL);
40911708Sstevel }
40921708Sstevel
40931708Sstevel if (err == NULL) {
40941708Sstevel /* do saf configurator stuff */
40951708Sstevel DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
40961708Sstevel scc = sc_probe_board(bp->bnum);
40971708Sstevel if (scc == NULL)
40981708Sstevel err = drerr_new(0, ESTC_PROBE, bp->cm.name);
40991708Sstevel }
41001708Sstevel
41011708Sstevel if (err) {
41021708Sstevel /* flush CDC srams */
41031708Sstevel if (axq_cdc_flush_all() != DDI_SUCCESS) {
41041708Sstevel goto out;
41051708Sstevel }
41061708Sstevel
41071708Sstevel /*
41081708Sstevel * Build the casm info portion of the UNCLAIM message.
41091708Sstevel */
41101708Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
41111708Sstevel mutex_enter(&drmach_slice_table_lock);
41121708Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
41131708Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
41141708Sstevel mutex_exit(&drmach_slice_table_lock);
41151708Sstevel (void) drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum,
411611066Srafael.vanoni@sun.com (caddr_t)obufp, sizeof (dr_mbox_msg_t),
411711066Srafael.vanoni@sun.com (caddr_t)NULL, 0);
41181708Sstevel
41191708Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t));
41201708Sstevel
41211708Sstevel /*
41221708Sstevel * we clear the connected flag just in case it would have
41231708Sstevel * been set by a concurrent drmach_board_status() thread
41241708Sstevel * before the UNCLAIM completed.
41251708Sstevel */
41261708Sstevel bp->connected = 0;
41271708Sstevel goto out;
41281708Sstevel }
41291708Sstevel
41301708Sstevel /*
41311708Sstevel * Now that the board has been successfully attached, obtain
41321708Sstevel * platform-specific DIMM serial id information for the board.
41331708Sstevel */
41341708Sstevel if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
41351708Sstevel plat_ecc_capability_sc_get(PLAT_ECC_DIMM_SID_MESSAGE)) {
41361708Sstevel (void) plat_request_mem_sids(DRMACH_BNUM2EXP(bp->bnum));
41371708Sstevel }
41381708Sstevel
41391708Sstevel out:
41401708Sstevel if (gdcd != NULL)
41411708Sstevel drmach_gdcd_dispose(gdcd);
41421708Sstevel
41431708Sstevel return (err);
41441708Sstevel }
41451708Sstevel
41461708Sstevel static void
drmach_slice_table_update(drmach_board_t * bp,int invalidate)41471708Sstevel drmach_slice_table_update(drmach_board_t *bp, int invalidate)
41481708Sstevel {
41491708Sstevel static char *axq_name = "address-extender-queue";
41501708Sstevel static dev_info_t *axq_dip = NULL;
41511708Sstevel static int axq_exp = -1;
41521708Sstevel static int axq_slot;
41531708Sstevel int e, s, slice;
41541708Sstevel
41551708Sstevel ASSERT(MUTEX_HELD(&drmach_slice_table_lock));
41561708Sstevel
41571708Sstevel e = DRMACH_BNUM2EXP(bp->bnum);
41581708Sstevel if (invalidate) {
41591708Sstevel ASSERT(DRMACH_BNUM2SLOT(bp->bnum) == 0);
41601708Sstevel
41611708Sstevel /* invalidate cached casm value */
41621708Sstevel drmach_slice_table[e] = 0;
41631708Sstevel
41641708Sstevel /* invalidate cached axq info if for same exp */
41651708Sstevel if (e == axq_exp && axq_dip) {
41661708Sstevel ndi_rele_devi(axq_dip);
41671708Sstevel axq_dip = NULL;
41681708Sstevel }
41691708Sstevel }
41701708Sstevel
41711708Sstevel if (axq_dip == NULL || !i_ddi_devi_attached(axq_dip)) {
41721708Sstevel int i, portid;
41731708Sstevel
41741708Sstevel /* search for an attached slot0 axq instance */
41751708Sstevel for (i = 0; i < AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP; i++) {
41761708Sstevel if (axq_dip)
41771708Sstevel ndi_rele_devi(axq_dip);
41781708Sstevel axq_dip = ddi_find_devinfo(axq_name, i, 0);
41791708Sstevel if (axq_dip && DDI_CF2(axq_dip)) {
41801708Sstevel portid = ddi_getprop(DDI_DEV_T_ANY, axq_dip,
41811708Sstevel DDI_PROP_DONTPASS, "portid", -1);
41821708Sstevel if (portid == -1) {
41831708Sstevel DRMACH_PR("cant get portid of axq "
41841708Sstevel "instance %d\n", i);
41851708Sstevel continue;
41861708Sstevel }
41871708Sstevel
41881708Sstevel axq_exp = (portid >> 5) & 0x1f;
41891708Sstevel axq_slot = portid & 1;
41901708Sstevel
41911708Sstevel if (invalidate && axq_exp == e)
41921708Sstevel continue;
41931708Sstevel
41941708Sstevel if (axq_slot == 0)
41951708Sstevel break; /* found */
41961708Sstevel }
41971708Sstevel }
41981708Sstevel
41991708Sstevel if (i == AXQ_MAX_EXP * AXQ_MAX_SLOT_PER_EXP) {
42001708Sstevel if (axq_dip) {
42011708Sstevel ndi_rele_devi(axq_dip);
42021708Sstevel axq_dip = NULL;
42031708Sstevel }
42041708Sstevel DRMACH_PR("drmach_slice_table_update: failed to "
42051708Sstevel "update axq dip\n");
42061708Sstevel return;
42071708Sstevel }
42081708Sstevel
42091708Sstevel }
42101708Sstevel
42111708Sstevel ASSERT(axq_dip);
42121708Sstevel ASSERT(axq_slot == 0);
42131708Sstevel
42141708Sstevel if (invalidate)
42151708Sstevel return;
42161708Sstevel
42171708Sstevel s = DRMACH_BNUM2SLOT(bp->bnum);
421811066Srafael.vanoni@sun.com DRMACH_PR("using AXQ casm %d.%d for slot%d.%d\n", axq_exp, axq_slot,
421911066Srafael.vanoni@sun.com e, s);
42201708Sstevel
42211708Sstevel /* invalidate entry */
42221708Sstevel drmach_slice_table[e] &= ~0x20;
42231708Sstevel
42241708Sstevel /*
42251708Sstevel * find a slice that routes to expander e. If no match
42261708Sstevel * is found, drmach_slice_table[e] will remain invalid.
42271708Sstevel *
42281708Sstevel * The CASM is a routing table indexed by slice number.
42291708Sstevel * Each element in the table contains permission bits,
42301708Sstevel * a destination expander number and a valid bit. The
42311708Sstevel * valid bit must true for the element to be meaningful.
42321708Sstevel *
42331708Sstevel * CASM entry structure
42341708Sstevel * Bits 15..6 ignored
42351708Sstevel * Bit 5 valid
42361708Sstevel * Bits 0..4 expander number
42371708Sstevel *
42381708Sstevel * NOTE: the for loop is really enumerating the range of slices,
42391708Sstevel * which is ALWAYS equal to the range of expanders. Hence,
42401708Sstevel * AXQ_MAX_EXP is okay to use in this loop.
42411708Sstevel */
42421708Sstevel for (slice = 0; slice < AXQ_MAX_EXP; slice++) {
42431708Sstevel uint32_t casm = axq_casm_read(axq_exp, axq_slot, slice);
42441708Sstevel
42451708Sstevel if ((casm & 0x20) && (casm & 0x1f) == e)
42461708Sstevel drmach_slice_table[e] = 0x20 | slice;
42471708Sstevel }
42481708Sstevel }
42491708Sstevel
42501708Sstevel /*
42511708Sstevel * Get base and bound PAs for slot 1 board lpa programming
42521708Sstevel * If a cpu/mem board is present in the same expander, use slice
42531708Sstevel * information corresponding to the CASM. Otherwise, set base and
42541708Sstevel * bound PAs to 0.
42551708Sstevel */
42561708Sstevel static void
drmach_lpa_bb_get(drmach_board_t * s1bp,uint64_t * basep,uint64_t * boundp)42571708Sstevel drmach_lpa_bb_get(drmach_board_t *s1bp, uint64_t *basep, uint64_t *boundp)
42581708Sstevel {
42591708Sstevel drmachid_t s0id;
42601708Sstevel
42611708Sstevel ASSERT(mutex_owned(&drmach_slice_table_lock));
42621708Sstevel ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
42631708Sstevel
42641708Sstevel *basep = *boundp = 0;
42651708Sstevel if (drmach_array_get(drmach_boards, s1bp->bnum - 1, &s0id) == 0 &&
426611066Srafael.vanoni@sun.com s0id != 0) {
42671708Sstevel
42681708Sstevel uint32_t slice;
426911066Srafael.vanoni@sun.com if ((slice = drmach_slice_table[DRMACH_BNUM2EXP(s1bp->bnum)])
427011066Srafael.vanoni@sun.com & 0x20) {
42711708Sstevel *basep = DRMACH_SLICE_TO_PA(slice & DRMACH_SLICE_MASK);
42721708Sstevel *boundp = *basep + DRMACH_MEM_SLICE_SIZE;
42731708Sstevel }
42741708Sstevel }
42751708Sstevel }
42761708Sstevel
42771708Sstevel
42781708Sstevel /*
42791708Sstevel * Reprogram slot 1 lpa's as required.
42801708Sstevel * The purpose of this routine is maintain the LPA settings of the devices
42811708Sstevel * in slot 1. To date we know Schizo and Cheetah are the only devices that
42821708Sstevel * require this attention. The LPA setting must match the slice field in the
42831708Sstevel * CASM element for the local expander. This field is guaranteed to be
42841708Sstevel * programmed in accordance with the cacheable address space on the slot 0
42851708Sstevel * board of the local expander. If no memory is present on the slot 0 board,
42861708Sstevel * there is no cacheable address space and, hence, the CASM slice field will
42871708Sstevel * be zero or its valid bit will be false (or both).
42881708Sstevel */
42891708Sstevel
42901708Sstevel static void
drmach_slot1_lpa_set(drmach_board_t * bp)42911708Sstevel drmach_slot1_lpa_set(drmach_board_t *bp)
42921708Sstevel {
42931708Sstevel drmachid_t id;
42941708Sstevel drmach_board_t *s1bp = NULL;
42951708Sstevel int rv, idx, is_maxcat = 1;
42961708Sstevel uint64_t last_scsr_pa = 0;
42971708Sstevel uint64_t new_basepa, new_boundpa;
42981708Sstevel
42991708Sstevel if (DRMACH_BNUM2SLOT(bp->bnum)) {
43001708Sstevel s1bp = bp;
43011708Sstevel if (s1bp->devices == NULL) {
43021708Sstevel DRMACH_PR("drmach...lpa_set: slot1=%d not present",
430311066Srafael.vanoni@sun.com bp->bnum);
43041708Sstevel return;
43051708Sstevel }
43061708Sstevel } else {
43071708Sstevel rv = drmach_array_get(drmach_boards, bp->bnum + 1, &id);
43081708Sstevel /* nothing to do when board is not found or has no devices */
43091708Sstevel s1bp = id;
43101708Sstevel if (rv == -1 || s1bp == NULL || s1bp->devices == NULL) {
43111708Sstevel DRMACH_PR("drmach...lpa_set: slot1=%d not present",
431211066Srafael.vanoni@sun.com bp->bnum + 1);
43131708Sstevel return;
43141708Sstevel }
43151708Sstevel ASSERT(DRMACH_IS_BOARD_ID(id));
43161708Sstevel }
43171708Sstevel mutex_enter(&drmach_slice_table_lock);
43181708Sstevel drmach_lpa_bb_get(s1bp, &new_basepa, &new_boundpa);
43191708Sstevel DRMACH_PR("drmach_...lpa_set: bnum=%d base=0x%lx bound=0x%lx\n",
432011066Srafael.vanoni@sun.com s1bp->bnum, new_basepa, new_boundpa);
43211708Sstevel
43221708Sstevel rv = drmach_array_first(s1bp->devices, &idx, &id);
43231708Sstevel while (rv == 0) {
43241708Sstevel if (DRMACH_IS_IO_ID(id)) {
43251708Sstevel drmach_io_t *io = id;
43261708Sstevel
43271708Sstevel is_maxcat = 0;
43281708Sstevel
43291708Sstevel /*
43301708Sstevel * Skip all non-Schizo IO devices (only IO nodes
43311708Sstevel * that are Schizo devices have non-zero scsr_pa).
43321708Sstevel * Filter out "other" leaf to avoid writing to the
43331708Sstevel * same Schizo Control/Status Register twice.
43341708Sstevel */
43351708Sstevel if (io->scsr_pa && io->scsr_pa != last_scsr_pa) {
43361708Sstevel uint64_t scsr;
43371708Sstevel
43381708Sstevel scsr = lddphysio(io->scsr_pa);
43391708Sstevel DRMACH_PR("drmach...lpa_set: old scsr=0x%lx\n",
434011066Srafael.vanoni@sun.com scsr);
43411708Sstevel scsr &= ~(DRMACH_LPA_BASE_MASK |
434211066Srafael.vanoni@sun.com DRMACH_LPA_BND_MASK);
43431708Sstevel scsr |= DRMACH_PA_TO_LPA_BASE(new_basepa);
43441708Sstevel scsr |= DRMACH_PA_TO_LPA_BND(new_boundpa);
43451708Sstevel
43461708Sstevel stdphysio(io->scsr_pa, scsr);
43471708Sstevel DRMACH_PR("drmach...lpa_set: new scsr=0x%lx\n",
434811066Srafael.vanoni@sun.com scsr);
43491708Sstevel
43501708Sstevel last_scsr_pa = io->scsr_pa;
43511708Sstevel }
43521708Sstevel }
43531708Sstevel rv = drmach_array_next(s1bp->devices, &idx, &id);
43541708Sstevel }
43551708Sstevel
43561708Sstevel if (is_maxcat && DRMACH_L1_SET_LPA(s1bp) && drmach_reprogram_lpa) {
43571708Sstevel extern xcfunc_t drmach_set_lpa;
43581708Sstevel
43591708Sstevel DRMACH_PR("reprogramming maxcat lpa's");
43601708Sstevel
43611708Sstevel mutex_enter(&cpu_lock);
43621708Sstevel rv = drmach_array_first(s1bp->devices, &idx, &id);
43631708Sstevel while (rv == 0 && id != NULL) {
43641708Sstevel if (DRMACH_IS_CPU_ID(id)) {
43651708Sstevel int ntries;
43661708Sstevel processorid_t cpuid;
43671708Sstevel
43681708Sstevel cpuid = ((drmach_cpu_t *)id)->cpuid;
43691708Sstevel
43701708Sstevel /*
43711708Sstevel * Check for unconfigured or powered-off
43721708Sstevel * MCPUs. If CPU_READY flag is clear, the
43731708Sstevel * MCPU cannot be xcalled.
43741708Sstevel */
43751708Sstevel if ((cpu[cpuid] == NULL) ||
437611066Srafael.vanoni@sun.com (cpu[cpuid]->cpu_flags &
437711066Srafael.vanoni@sun.com CPU_READY) == 0) {
43781708Sstevel
43791708Sstevel rv = drmach_array_next(s1bp->devices,
438011066Srafael.vanoni@sun.com &idx, &id);
43811708Sstevel continue;
43821708Sstevel }
43831708Sstevel
43841708Sstevel /*
43851708Sstevel * XXX CHEETAH SUPPORT
43861708Sstevel * for cheetah, we need to clear iocage
43871708Sstevel * memory since it will be used for e$ flush
43881708Sstevel * in drmach_set_lpa.
43891708Sstevel */
43901708Sstevel if (drmach_is_cheetah) {
43911708Sstevel mutex_enter(&drmach_iocage_lock);
43921708Sstevel while (drmach_iocage_is_busy)
43931708Sstevel cv_wait(&drmach_iocage_cv,
439411066Srafael.vanoni@sun.com &drmach_iocage_lock);
43951708Sstevel drmach_iocage_is_busy = 1;
439611066Srafael.vanoni@sun.com drmach_iocage_mem_scrub(ecache_size *
439711066Srafael.vanoni@sun.com 2);
43981708Sstevel mutex_exit(&drmach_iocage_lock);
43991708Sstevel }
44001708Sstevel
44011708Sstevel /*
44021708Sstevel * drmach_slice_table[*]
44031708Sstevel * bit 5 valid
44041708Sstevel * bit 0:4 slice number
44051708Sstevel *
44061708Sstevel * drmach_xt_mb[*] format for drmach_set_lpa
44071708Sstevel * bit 7 valid
44081708Sstevel * bit 6 set null LPA
44091708Sstevel * (overrides bits 0:4)
44101708Sstevel * bit 0:4 slice number
44111708Sstevel *
44121708Sstevel * drmach_set_lpa derives processor CBASE and
44131708Sstevel * CBND from bits 6 and 0:4 of drmach_xt_mb.
44141708Sstevel * If bit 6 is set, then CBASE = CBND = 0.
44151708Sstevel * Otherwise, CBASE = slice number;
44161708Sstevel * CBND = slice number + 1.
44171708Sstevel * No action is taken if bit 7 is zero.
44181708Sstevel */
44191708Sstevel
44201708Sstevel mutex_enter(&drmach_xt_mb_lock);
44211708Sstevel bzero((void *)drmach_xt_mb,
44221708Sstevel drmach_xt_mb_size);
44231708Sstevel
44241708Sstevel if (new_basepa == 0 && new_boundpa == 0)
44251708Sstevel drmach_xt_mb[cpuid] = 0x80 | 0x40;
44261708Sstevel else
44271708Sstevel drmach_xt_mb[cpuid] = 0x80 |
442811066Srafael.vanoni@sun.com DRMACH_PA_TO_SLICE(new_basepa);
44291708Sstevel
44301708Sstevel drmach_xt_ready = 0;
44311708Sstevel
44321708Sstevel xt_one(cpuid, drmach_set_lpa, NULL, NULL);
44331708Sstevel
44341708Sstevel ntries = drmach_cpu_ntries;
44351708Sstevel while (!drmach_xt_ready && ntries) {
44361708Sstevel DELAY(drmach_cpu_delay);
44371708Sstevel ntries--;
44381708Sstevel }
44391708Sstevel mutex_exit(&drmach_xt_mb_lock);
44401708Sstevel drmach_xt_ready = 0;
44411708Sstevel
44421708Sstevel /*
44431708Sstevel * XXX CHEETAH SUPPORT
44441708Sstevel * for cheetah, we need to clear iocage
44451708Sstevel * memory since it was used for e$ flush
44461708Sstevel * in performed drmach_set_lpa.
44471708Sstevel */
44481708Sstevel if (drmach_is_cheetah) {
44491708Sstevel mutex_enter(&drmach_iocage_lock);
445011066Srafael.vanoni@sun.com drmach_iocage_mem_scrub(ecache_size *
445111066Srafael.vanoni@sun.com 2);
44521708Sstevel drmach_iocage_is_busy = 0;
44531708Sstevel cv_signal(&drmach_iocage_cv);
44541708Sstevel mutex_exit(&drmach_iocage_lock);
44551708Sstevel }
44561708Sstevel }
44571708Sstevel rv = drmach_array_next(s1bp->devices, &idx, &id);
44581708Sstevel }
44591708Sstevel mutex_exit(&cpu_lock);
44601708Sstevel }
44611708Sstevel mutex_exit(&drmach_slice_table_lock);
44621708Sstevel }
44631708Sstevel
44641708Sstevel /*
44651708Sstevel * Return the number of connected Panther boards in the domain.
44661708Sstevel */
44671708Sstevel static int
drmach_panther_boards(void)44681708Sstevel drmach_panther_boards(void)
44691708Sstevel {
44701708Sstevel int rv;
44711708Sstevel int b_idx;
44721708Sstevel drmachid_t b_id;
44731708Sstevel drmach_board_t *bp;
44741708Sstevel int npanther = 0;
44751708Sstevel
44761708Sstevel rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
44771708Sstevel while (rv == 0) {
44781708Sstevel ASSERT(DRMACH_IS_BOARD_ID(b_id));
44791708Sstevel bp = b_id;
44801708Sstevel
44811708Sstevel if (IS_PANTHER(bp->cpu_impl))
44821708Sstevel npanther++;
44831708Sstevel
44841708Sstevel rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
44851708Sstevel }
44861708Sstevel
44871708Sstevel return (npanther);
44881708Sstevel }
44891708Sstevel
44901708Sstevel /*ARGSUSED*/
44911708Sstevel sbd_error_t *
drmach_board_disconnect(drmachid_t id,drmach_opts_t * opts)44921708Sstevel drmach_board_disconnect(drmachid_t id, drmach_opts_t *opts)
44931708Sstevel {
44941708Sstevel drmach_board_t *bp;
44951708Sstevel dr_mbox_msg_t *obufp;
44961708Sstevel sbd_error_t *err = NULL;
44971708Sstevel
44981708Sstevel sc_gptwocfg_cookie_t scc;
44991708Sstevel
45001708Sstevel if (!DRMACH_IS_BOARD_ID(id))
45011708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
45021708Sstevel bp = id;
45031708Sstevel
45041708Sstevel /*
45051708Sstevel * Build the casm info portion of the UNCLAIM message.
45061708Sstevel * This must be done prior to calling for saf configurator
45071708Sstevel * deprobe, to ensure that the associated axq instance
45081708Sstevel * is not detached.
45091708Sstevel */
45101708Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
45111708Sstevel mutex_enter(&drmach_slice_table_lock);
45121708Sstevel drmach_msg_memslice_init(obufp->msgdata.dm_ur.mem_slice);
45131708Sstevel
45141708Sstevel /*
45151708Sstevel * If disconnecting slot 0 board, update the casm slice table
45161708Sstevel * info now, for use by drmach_slot1_lpa_set()
45171708Sstevel */
45181708Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 0)
451911066Srafael.vanoni@sun.com drmach_slice_table_update(bp, 1);
45201708Sstevel
45211708Sstevel drmach_msg_memregs_init(obufp->msgdata.dm_ur.mem_regs);
45221708Sstevel mutex_exit(&drmach_slice_table_lock);
45231708Sstevel
45241708Sstevel /*
45251708Sstevel * Update LPA information for slot1 board
45261708Sstevel */
45271708Sstevel drmach_slot1_lpa_set(bp);
45281708Sstevel
45291708Sstevel /* disable and flush CDC */
45301708Sstevel if (axq_cdc_disable_flush_all() != DDI_SUCCESS) {
45311708Sstevel axq_cdc_enable_all(); /* paranoia */
45321708Sstevel err = DRMACH_INTERNAL_ERROR();
45331708Sstevel }
45341708Sstevel
45351708Sstevel /*
45361708Sstevel * call saf configurator for deprobe
45371708Sstevel * It's done now before sending an UNCLAIM message because
45381708Sstevel * IKP will probe boards it doesn't know about <present at boot>
45391708Sstevel * prior to unprobing them. If this happens after sending the
45401708Sstevel * UNCLAIM, it will cause a dstop for domain transgression error.
45411708Sstevel */
45421708Sstevel
45431708Sstevel if (!err) {
45441708Sstevel scc = sc_unprobe_board(bp->bnum);
45451708Sstevel axq_cdc_enable_all();
45461708Sstevel if (scc != NULL) {
45471708Sstevel err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
45481708Sstevel }
45491708Sstevel }
45501708Sstevel
45511708Sstevel /*
45521708Sstevel * If disconnecting a board from a Panther domain, wait a fixed-
45531708Sstevel * time delay for pending Safari transactions to complete on the
45541708Sstevel * disconnecting board's processors. The bus sync list read used
45551708Sstevel * in drmach_shutdown_asm to synchronize with outstanding Safari
45561708Sstevel * transactions assumes no read-bypass-write mode for all memory
45571708Sstevel * controllers. Since Panther supports read-bypass-write, a
45581708Sstevel * delay is used that is slightly larger than the maximum Safari
45591708Sstevel * timeout value in the Safari/Fireplane Config Reg.
45601708Sstevel */
45611708Sstevel if (drmach_panther_boards() > 0 || drmach_unclaim_delay_all) {
456211066Srafael.vanoni@sun.com clock_t stime = ddi_get_lbolt();
45631708Sstevel
45641708Sstevel delay(drv_usectohz(drmach_unclaim_usec_delay));
45651708Sstevel
456611066Srafael.vanoni@sun.com stime = ddi_get_lbolt() - stime;
45671708Sstevel DRMACH_PR("delayed %ld ticks (%ld secs) before disconnecting "
45681708Sstevel "board %s from domain\n", stime, stime / hz, bp->cm.name);
45691708Sstevel }
45701708Sstevel
45711708Sstevel if (!err) {
45721708Sstevel obufp->msgdata.dm_ur.mem_clear = 0;
45731708Sstevel
45741708Sstevel err = drmach_mbox_trans(DRMSG_UNCLAIM, bp->bnum, (caddr_t)obufp,
457511066Srafael.vanoni@sun.com sizeof (dr_mbox_msg_t), (caddr_t)NULL, 0);
45761708Sstevel
45771708Sstevel if (err) {
45781708Sstevel /*
45791708Sstevel * if mailbox timeout or unrecoverable error from SC,
45801708Sstevel * board cannot be touched. Mark the status as
45811708Sstevel * unusable.
45821708Sstevel */
45831708Sstevel if ((err->e_code == ESTC_SMS_ERR_UNRECOVERABLE) ||
458411066Srafael.vanoni@sun.com (err->e_code == ESTC_MBXRPLY))
458511066Srafael.vanoni@sun.com bp->cond = SBD_COND_UNUSABLE;
45861708Sstevel else {
45871708Sstevel DRMACH_PR("UNCLAIM failed for bnum=%d\n",
458811066Srafael.vanoni@sun.com bp->bnum);
45891708Sstevel DRMACH_PR("calling sc_probe_board: bnum=%d\n",
459011066Srafael.vanoni@sun.com bp->bnum);
45911708Sstevel scc = sc_probe_board(bp->bnum);
45921708Sstevel if (scc == NULL) {
45931708Sstevel cmn_err(CE_WARN,
45941708Sstevel "sc_probe_board failed for bnum=%d",
459511066Srafael.vanoni@sun.com bp->bnum);
45961708Sstevel } else {
45971708Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 0) {
45981708Sstevel mutex_enter(
45991708Sstevel &drmach_slice_table_lock);
46001708Sstevel drmach_slice_table_update(bp,
46011708Sstevel 0);
46021708Sstevel mutex_exit(
46031708Sstevel &drmach_slice_table_lock);
46041708Sstevel }
46051708Sstevel drmach_slot1_lpa_set(bp);
46061708Sstevel }
46071708Sstevel }
46081708Sstevel } else {
46091708Sstevel bp->connected = 0;
46101708Sstevel /*
46111708Sstevel * Now that the board has been successfully detached,
46121708Sstevel * discard platform-specific DIMM serial id information
46131708Sstevel * for the board.
46141708Sstevel */
46151708Sstevel if ((DRMACH_BNUM2SLOT(bp->bnum) == 0) &&
46161708Sstevel plat_ecc_capability_sc_get(
46171708Sstevel PLAT_ECC_DIMM_SID_MESSAGE)) {
46181708Sstevel (void) plat_discard_mem_sids(
46191708Sstevel DRMACH_BNUM2EXP(bp->bnum));
46201708Sstevel }
46211708Sstevel }
46221708Sstevel }
46231708Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t));
46241708Sstevel
46251708Sstevel return (err);
46261708Sstevel }
46271708Sstevel
46281708Sstevel static int
drmach_get_portid(drmach_node_t * np)46291708Sstevel drmach_get_portid(drmach_node_t *np)
46301708Sstevel {
46311708Sstevel drmach_node_t pp;
46321708Sstevel int portid;
46331708Sstevel char type[OBP_MAXPROPNAME];
46341708Sstevel
46351708Sstevel if (np->n_getprop(np, "portid", &portid, sizeof (portid)) == 0)
46361708Sstevel return (portid);
46371708Sstevel
46381708Sstevel /*
46391708Sstevel * Get the device_type property to see if we should
46401708Sstevel * continue processing this node.
46411708Sstevel */
46421708Sstevel if (np->n_getprop(np, "device_type", &type, sizeof (type)) != 0)
46431708Sstevel return (-1);
46441708Sstevel
46451708Sstevel /*
46461708Sstevel * If the device is a CPU without a 'portid' property,
46471708Sstevel * it is a CMP core. For such cases, the parent node
46481708Sstevel * has the portid.
46491708Sstevel */
46501708Sstevel if (strcmp(type, DRMACH_CPU_NAMEPROP) == 0) {
46511708Sstevel if (np->get_parent(np, &pp) != 0)
46521708Sstevel return (-1);
46531708Sstevel
46541708Sstevel if (pp.n_getprop(&pp, "portid", &portid, sizeof (portid)) == 0)
46551708Sstevel return (portid);
46561708Sstevel }
46571708Sstevel
46581708Sstevel return (-1);
46591708Sstevel }
46601708Sstevel
46611708Sstevel /*
46621708Sstevel * This is a helper function to determine if a given
46631708Sstevel * node should be considered for a dr operation according
46641708Sstevel * to predefined dr type nodes and the node's name.
46651708Sstevel * Formal Parameter : The name of a device node.
46661708Sstevel * Return Value: -1, name does not map to a valid dr type.
46671708Sstevel * A value greater or equal to 0, name is a valid dr type.
46681708Sstevel */
46691708Sstevel static int
drmach_name2type_idx(char * name)46701708Sstevel drmach_name2type_idx(char *name)
46711708Sstevel {
46721708Sstevel int index, ntypes;
46731708Sstevel
46741708Sstevel if (name == NULL)
46751708Sstevel return (-1);
46761708Sstevel
46771708Sstevel /*
46781708Sstevel * Determine how many possible types are currently supported
46791708Sstevel * for dr.
46801708Sstevel */
46811708Sstevel ntypes = sizeof (drmach_name2type) / sizeof (drmach_name2type[0]);
46821708Sstevel
46831708Sstevel /* Determine if the node's name correspond to a predefined type. */
46841708Sstevel for (index = 0; index < ntypes; index++) {
46851708Sstevel if (strcmp(drmach_name2type[index].name, name) == 0)
46861708Sstevel /* The node is an allowed type for dr. */
46871708Sstevel return (index);
46881708Sstevel }
46891708Sstevel
46901708Sstevel /*
46911708Sstevel * If the name of the node does not map to any of the
46921708Sstevel * types in the array drmach_name2type then the node is not of
46931708Sstevel * interest to dr.
46941708Sstevel */
46951708Sstevel return (-1);
46961708Sstevel }
46971708Sstevel
46981708Sstevel static int
drmach_board_find_devices_cb(drmach_node_walk_args_t * args)46991708Sstevel drmach_board_find_devices_cb(drmach_node_walk_args_t *args)
47001708Sstevel {
47011708Sstevel drmach_node_t *node = args->node;
47021708Sstevel drmach_board_cb_data_t *data = args->data;
47031708Sstevel drmach_board_t *obj = data->obj;
47041708Sstevel
47051708Sstevel int rv, portid;
47061708Sstevel drmachid_t id;
47071708Sstevel drmach_device_t *device;
47081708Sstevel char name[OBP_MAXDRVNAME];
47091708Sstevel
47101708Sstevel portid = drmach_get_portid(node);
47111708Sstevel if (portid == -1) {
47121708Sstevel /*
47131708Sstevel * if the node does not have a portid property, then
47141708Sstevel * by that information alone it is known that drmach
47151708Sstevel * is not interested in it.
47161708Sstevel */
47171708Sstevel return (0);
47181708Sstevel }
47191708Sstevel rv = node->n_getprop(node, "name", name, OBP_MAXDRVNAME);
47201708Sstevel
47211708Sstevel /* The node must have a name */
47221708Sstevel if (rv)
47231708Sstevel return (0);
47241708Sstevel
47251708Sstevel /*
47261708Sstevel * Ignore devices whose portid do not map to this board,
47271708Sstevel * or that their name property is not mapped to a valid
47281708Sstevel * dr device name.
47291708Sstevel */
47301708Sstevel if ((drmach_portid2bnum(portid) != obj->bnum) ||
47311708Sstevel (drmach_name2type_idx(name) < 0))
47321708Sstevel return (0);
47331708Sstevel
47341708Sstevel /*
47351708Sstevel * Create a device data structure from this node data.
47361708Sstevel * The call may yield nothing if the node is not of interest
47371708Sstevel * to drmach.
47381708Sstevel */
47391708Sstevel data->err = drmach_device_new(node, obj, portid, &id);
47401708Sstevel if (data->err)
47411708Sstevel return (-1);
47421708Sstevel else if (!id) {
47431708Sstevel /*
47441708Sstevel * drmach_device_new examined the node we passed in
47451708Sstevel * and determined that it was either one not of
47461708Sstevel * interest to drmach or the PIM dr layer.
47471708Sstevel * So, it is skipped.
47481708Sstevel */
47491708Sstevel return (0);
47501708Sstevel }
47511708Sstevel
47521708Sstevel rv = drmach_array_set(obj->devices, data->ndevs++, id);
47531708Sstevel if (rv) {
47541708Sstevel data->err = DRMACH_INTERNAL_ERROR();
47551708Sstevel return (-1);
47561708Sstevel }
47571708Sstevel
47581708Sstevel device = id;
47591708Sstevel
47601708Sstevel #ifdef DEBUG
47611708Sstevel DRMACH_PR("%d %s %d %p\n", portid, device->type, device->unum, id);
47621708Sstevel if (DRMACH_IS_IO_ID(id))
47631708Sstevel DRMACH_PR("ndevs = %d dip/node = %p", data->ndevs, node->here);
47641708Sstevel #endif
47651708Sstevel
47661708Sstevel data->err = (*data->found)(data->a, device->type, device->unum, id);
47671708Sstevel return (data->err == NULL ? 0 : -1);
47681708Sstevel }
47691708Sstevel
47701708Sstevel sbd_error_t *
drmach_board_find_devices(drmachid_t id,void * a,sbd_error_t * (* found)(void * a,const char *,int,drmachid_t))47711708Sstevel drmach_board_find_devices(drmachid_t id, void *a,
47721708Sstevel sbd_error_t *(*found)(void *a, const char *, int, drmachid_t))
47731708Sstevel {
47741708Sstevel drmach_board_t *bp = (drmach_board_t *)id;
47751708Sstevel sbd_error_t *err;
47761708Sstevel int max_devices;
47771708Sstevel int rv;
47781708Sstevel drmach_board_cb_data_t data;
47791708Sstevel
47801708Sstevel if (!DRMACH_IS_BOARD_ID(id))
47811708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
47821708Sstevel
47831708Sstevel max_devices = plat_max_cpu_units_per_board();
47841708Sstevel max_devices += plat_max_mem_units_per_board();
47851708Sstevel max_devices += plat_max_io_units_per_board();
47861708Sstevel
47871708Sstevel bp->devices = drmach_array_new(0, max_devices);
47881708Sstevel
47891708Sstevel if (bp->tree == NULL)
47901708Sstevel bp->tree = drmach_node_new();
47911708Sstevel
47921708Sstevel data.obj = bp;
47931708Sstevel data.ndevs = 0;
47941708Sstevel data.found = found;
47951708Sstevel data.a = a;
47961708Sstevel data.err = NULL;
47971708Sstevel
47981708Sstevel mutex_enter(&drmach_slice_table_lock);
47991708Sstevel mutex_enter(&drmach_bus_sync_lock);
48001708Sstevel
48011708Sstevel rv = drmach_node_walk(bp->tree, &data, drmach_board_find_devices_cb);
48021708Sstevel
48031708Sstevel drmach_slice_table_update(bp, 0);
48041708Sstevel drmach_bus_sync_list_update();
48051708Sstevel
48061708Sstevel mutex_exit(&drmach_bus_sync_lock);
48071708Sstevel mutex_exit(&drmach_slice_table_lock);
48081708Sstevel
48091708Sstevel if (rv == 0) {
48101708Sstevel err = NULL;
48111708Sstevel drmach_slot1_lpa_set(bp);
48121708Sstevel } else {
48131708Sstevel drmach_array_dispose(bp->devices, drmach_device_dispose);
48141708Sstevel bp->devices = NULL;
48151708Sstevel
48161708Sstevel if (data.err)
48171708Sstevel err = data.err;
48181708Sstevel else
48191708Sstevel err = DRMACH_INTERNAL_ERROR();
48201708Sstevel }
48211708Sstevel
48221708Sstevel return (err);
48231708Sstevel }
48241708Sstevel
48251708Sstevel int
drmach_board_lookup(int bnum,drmachid_t * id)48261708Sstevel drmach_board_lookup(int bnum, drmachid_t *id)
48271708Sstevel {
48281708Sstevel int rv = 0;
48291708Sstevel
48301708Sstevel if (!drmach_initialized && drmach_init() == -1) {
48311708Sstevel *id = 0;
48321708Sstevel return (-1);
48331708Sstevel }
48341708Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER);
48351708Sstevel if (drmach_array_get(drmach_boards, bnum, id)) {
48361708Sstevel *id = 0;
48371708Sstevel rv = -1;
48381708Sstevel } else {
48391708Sstevel caddr_t obufp;
48401708Sstevel dr_showboard_t shb;
48411708Sstevel sbd_error_t *err = NULL;
48421708Sstevel drmach_board_t *bp;
48431708Sstevel
48441708Sstevel bp = *id;
48451708Sstevel
48461708Sstevel if (bp)
48471708Sstevel rw_downgrade(&drmach_boards_rwlock);
48481708Sstevel
48491708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
48501708Sstevel err = drmach_mbox_trans(DRMSG_SHOWBOARD, bnum, obufp,
485111066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)&shb,
485211066Srafael.vanoni@sun.com sizeof (dr_showboard_t));
48531708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
48541708Sstevel
48551708Sstevel if (err) {
48561708Sstevel if (err->e_code == ESTC_UNAVAILABLE) {
48571708Sstevel *id = 0;
48581708Sstevel rv = -1;
48591708Sstevel }
48601708Sstevel sbd_err_clear(&err);
48611708Sstevel } else {
48621708Sstevel if (!bp)
48631708Sstevel bp = *id = (drmachid_t)drmach_board_new(bnum);
48641708Sstevel bp->connected = (shb.bd_assigned && shb.bd_active);
48651708Sstevel bp->empty = shb.slot_empty;
48661708Sstevel
48671708Sstevel switch (shb.test_status) {
48681708Sstevel case DR_TEST_STATUS_UNKNOWN:
48691708Sstevel case DR_TEST_STATUS_IPOST:
48701708Sstevel case DR_TEST_STATUS_ABORTED:
48711708Sstevel bp->cond = SBD_COND_UNKNOWN;
48721708Sstevel break;
48731708Sstevel case DR_TEST_STATUS_PASSED:
48741708Sstevel bp->cond = SBD_COND_OK;
48751708Sstevel break;
48761708Sstevel case DR_TEST_STATUS_FAILED:
48771708Sstevel bp->cond = SBD_COND_FAILED;
48781708Sstevel break;
48791708Sstevel default:
48801708Sstevel bp->cond = SBD_COND_UNKNOWN;
48811708Sstevel DRMACH_PR("Unknown test status=0x%x from SC\n",
488211066Srafael.vanoni@sun.com shb.test_status);
48831708Sstevel break;
48841708Sstevel }
488511311SSurya.Prakki@Sun.COM (void) strncpy(bp->type, shb.board_type,
488611311SSurya.Prakki@Sun.COM sizeof (bp->type));
48871708Sstevel bp->assigned = shb.bd_assigned;
48881708Sstevel bp->powered = shb.power_on;
48891708Sstevel }
48901708Sstevel }
48911708Sstevel rw_exit(&drmach_boards_rwlock);
48921708Sstevel return (rv);
48931708Sstevel }
48941708Sstevel
48951708Sstevel sbd_error_t *
drmach_board_name(int bnum,char * buf,int buflen)48961708Sstevel drmach_board_name(int bnum, char *buf, int buflen)
48971708Sstevel {
489811311SSurya.Prakki@Sun.COM (void) snprintf(buf, buflen, "%s%d", DRMACH_BNUM2SLOT(bnum) ?
48991708Sstevel "IO" : "SB", DRMACH_BNUM2EXP(bnum));
49001708Sstevel
49011708Sstevel return (NULL);
49021708Sstevel }
49031708Sstevel
49041708Sstevel sbd_error_t *
drmach_board_poweroff(drmachid_t id)49051708Sstevel drmach_board_poweroff(drmachid_t id)
49061708Sstevel {
49071708Sstevel drmach_board_t *bp;
49081708Sstevel sbd_error_t *err;
49091708Sstevel drmach_status_t stat;
49101708Sstevel
49111708Sstevel if (!DRMACH_IS_BOARD_ID(id))
49121708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
49131708Sstevel bp = id;
49141708Sstevel
49151708Sstevel err = drmach_board_status(id, &stat);
49161708Sstevel if (!err) {
49171708Sstevel if (stat.configured || stat.busy)
49181708Sstevel err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
49191708Sstevel else {
49201708Sstevel caddr_t obufp;
49211708Sstevel
49221708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
49231708Sstevel err = drmach_mbox_trans(DRMSG_POWEROFF, bp->bnum, obufp,
492411066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
49251708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
49261708Sstevel if (!err)
49271708Sstevel bp->powered = 0;
49281708Sstevel }
49291708Sstevel }
49301708Sstevel return (err);
49311708Sstevel }
49321708Sstevel
49331708Sstevel sbd_error_t *
drmach_board_poweron(drmachid_t id)49341708Sstevel drmach_board_poweron(drmachid_t id)
49351708Sstevel {
49361708Sstevel drmach_board_t *bp;
49371708Sstevel caddr_t obufp;
49381708Sstevel sbd_error_t *err;
49391708Sstevel
49401708Sstevel if (!DRMACH_IS_BOARD_ID(id))
49411708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
49421708Sstevel bp = id;
49431708Sstevel
49441708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
49451708Sstevel err = drmach_mbox_trans(DRMSG_POWERON, bp->bnum, obufp,
494611066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
49471708Sstevel if (!err)
49481708Sstevel bp->powered = 1;
49491708Sstevel
49501708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
49511708Sstevel
49521708Sstevel return (err);
49531708Sstevel }
49541708Sstevel
49551708Sstevel static sbd_error_t *
drmach_board_release(drmachid_t id)49561708Sstevel drmach_board_release(drmachid_t id)
49571708Sstevel {
49581708Sstevel if (!DRMACH_IS_BOARD_ID(id))
49591708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
49601708Sstevel return (NULL);
49611708Sstevel }
49621708Sstevel
49631708Sstevel sbd_error_t *
drmach_board_test(drmachid_t id,drmach_opts_t * opts,int force)49641708Sstevel drmach_board_test(drmachid_t id, drmach_opts_t *opts, int force)
49651708Sstevel {
49661708Sstevel drmach_board_t *bp;
49671772Sjl139090 drmach_device_t *dp[MAX_CORES_PER_CMP];
49681708Sstevel dr_mbox_msg_t *obufp;
49691708Sstevel sbd_error_t *err;
49701708Sstevel dr_testboard_reply_t tbr;
49711708Sstevel int cpylen;
49721708Sstevel char *copts;
49731708Sstevel int is_io;
49741772Sjl139090 cpu_flag_t oflags[MAX_CORES_PER_CMP];
49751708Sstevel
49761708Sstevel if (!DRMACH_IS_BOARD_ID(id))
49771708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
49781708Sstevel bp = id;
49791708Sstevel
49801708Sstevel /*
49811708Sstevel * If the board is an I/O or MAXCAT board, setup I/O cage for
49821708Sstevel * testing. Slot 1 indicates I/O or MAXCAT board.
49831708Sstevel */
49841708Sstevel
49851708Sstevel is_io = DRMACH_BNUM2SLOT(bp->bnum);
49861708Sstevel
49871708Sstevel obufp = kmem_zalloc(sizeof (dr_mbox_msg_t), KM_SLEEP);
49881708Sstevel
49891708Sstevel if (force)
49901708Sstevel obufp->msgdata.dm_tb.force = 1;
49911708Sstevel
49921708Sstevel obufp->msgdata.dm_tb.immediate = 1;
49931708Sstevel
49941708Sstevel if ((opts->size > 0) && ((copts = opts->copts) != NULL)) {
49951708Sstevel cpylen = (opts->size > DR_HPOPTLEN ? DR_HPOPTLEN : opts->size);
49961708Sstevel bcopy(copts, obufp->msgdata.dm_tb.hpost_opts, cpylen);
49971708Sstevel }
49981708Sstevel
49991708Sstevel if (is_io) {
50001708Sstevel err = drmach_iocage_setup(&obufp->msgdata.dm_tb, dp, oflags);
50011708Sstevel
50021708Sstevel if (err) {
50031708Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t));
50041708Sstevel return (err);
50051708Sstevel }
50061708Sstevel }
50071708Sstevel
50081708Sstevel err = drmach_mbox_trans(DRMSG_TESTBOARD, bp->bnum, (caddr_t)obufp,
500911066Srafael.vanoni@sun.com sizeof (dr_mbox_msg_t), (caddr_t)&tbr, sizeof (tbr));
50101708Sstevel
50111708Sstevel if (!err)
50121708Sstevel bp->cond = SBD_COND_OK;
50131708Sstevel else
50141708Sstevel bp->cond = SBD_COND_UNKNOWN;
50151708Sstevel
50161708Sstevel if ((!err) && (tbr.test_status != DR_TEST_STATUS_PASSED)) {
50171708Sstevel /* examine test status */
50181708Sstevel switch (tbr.test_status) {
50191708Sstevel case DR_TEST_STATUS_IPOST:
50201708Sstevel bp->cond = SBD_COND_UNKNOWN;
502111066Srafael.vanoni@sun.com err = drerr_new(0, ESTC_TEST_IN_PROGRESS, NULL);
50221708Sstevel break;
50231708Sstevel case DR_TEST_STATUS_UNKNOWN:
50241708Sstevel bp->cond = SBD_COND_UNKNOWN;
50251708Sstevel err = drerr_new(1,
502611066Srafael.vanoni@sun.com ESTC_TEST_STATUS_UNKNOWN, NULL);
50271708Sstevel break;
50281708Sstevel case DR_TEST_STATUS_FAILED:
50291708Sstevel bp->cond = SBD_COND_FAILED;
503011066Srafael.vanoni@sun.com err = drerr_new(1, ESTC_TEST_FAILED, NULL);
50311708Sstevel break;
50321708Sstevel case DR_TEST_STATUS_ABORTED:
50331708Sstevel bp->cond = SBD_COND_UNKNOWN;
503411066Srafael.vanoni@sun.com err = drerr_new(1, ESTC_TEST_ABORTED, NULL);
50351708Sstevel break;
50361708Sstevel default:
50371708Sstevel bp->cond = SBD_COND_UNKNOWN;
503811066Srafael.vanoni@sun.com err = drerr_new(1, ESTC_TEST_RESULT_UNKNOWN,
503911066Srafael.vanoni@sun.com NULL);
50401708Sstevel break;
50411708Sstevel }
50421708Sstevel }
50431708Sstevel
50441708Sstevel /*
50451708Sstevel * If I/O cage test was performed, check for availability of the
50461708Sstevel * cpu used. If cpu has been returned, it's OK to proceed with
50471708Sstevel * reconfiguring it for use.
50481708Sstevel */
50491708Sstevel if (is_io) {
50501708Sstevel DRMACH_PR("drmach_board_test: tbr.cpu_recovered: %d",
505111066Srafael.vanoni@sun.com tbr.cpu_recovered);
50521708Sstevel DRMACH_PR("drmach_board_test: port id: %d",
505311066Srafael.vanoni@sun.com tbr.cpu_portid);
50541708Sstevel
50551708Sstevel /*
50561708Sstevel * Check the cpu_recovered flag in the testboard reply, or
50571708Sstevel * if the testboard request message was not sent to SMS due
50581708Sstevel * to an mboxsc_putmsg() failure, it's OK to recover the
50591708Sstevel * cpu since hpost hasn't touched it.
50601708Sstevel */
50611708Sstevel if ((tbr.cpu_recovered && tbr.cpu_portid ==
50621708Sstevel obufp->msgdata.dm_tb.cpu_portid) ||
50631708Sstevel ((err) && (err->e_code == ESTC_MBXRQST))) {
50641708Sstevel
50651708Sstevel int i;
50661708Sstevel
50671708Sstevel mutex_enter(&cpu_lock);
50681772Sjl139090 for (i = 0; i < MAX_CORES_PER_CMP; i++) {
50691708Sstevel if (dp[i] != NULL) {
50701708Sstevel (void) drmach_iocage_cpu_return(dp[i],
50711708Sstevel oflags[i]);
50721708Sstevel }
50731708Sstevel }
50741708Sstevel mutex_exit(&cpu_lock);
50751708Sstevel } else {
50761708Sstevel cmn_err(CE_WARN, "Unable to recover port id %d "
50771708Sstevel "after I/O cage test: cpu_recovered=%d, "
50781708Sstevel "returned portid=%d",
50791708Sstevel obufp->msgdata.dm_tb.cpu_portid,
50801708Sstevel tbr.cpu_recovered, tbr.cpu_portid);
50811708Sstevel }
508211311SSurya.Prakki@Sun.COM (void) drmach_iocage_mem_return(&tbr);
50831708Sstevel }
50841708Sstevel kmem_free(obufp, sizeof (dr_mbox_msg_t));
50851708Sstevel
50861708Sstevel return (err);
50871708Sstevel }
50881708Sstevel
50891708Sstevel sbd_error_t *
drmach_board_unassign(drmachid_t id)50901708Sstevel drmach_board_unassign(drmachid_t id)
50911708Sstevel {
50921708Sstevel drmach_board_t *bp;
50931708Sstevel sbd_error_t *err;
50941708Sstevel drmach_status_t stat;
50951708Sstevel caddr_t obufp;
50961708Sstevel
50971708Sstevel rw_enter(&drmach_boards_rwlock, RW_WRITER);
50981708Sstevel
50991708Sstevel if (!DRMACH_IS_BOARD_ID(id)) {
51001708Sstevel rw_exit(&drmach_boards_rwlock);
51011708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
51021708Sstevel }
51031708Sstevel bp = id;
51041708Sstevel
51051708Sstevel err = drmach_board_status(id, &stat);
51061708Sstevel if (err) {
51071708Sstevel rw_exit(&drmach_boards_rwlock);
51081708Sstevel return (err);
51091708Sstevel }
51101708Sstevel
51111708Sstevel if (stat.configured || stat.busy) {
51121708Sstevel err = drerr_new(0, ESTC_CONFIGBUSY, bp->cm.name);
51131708Sstevel } else {
51141708Sstevel
51151708Sstevel obufp = kmem_zalloc(sizeof (dr_proto_hdr_t), KM_SLEEP);
51161708Sstevel err = drmach_mbox_trans(DRMSG_UNASSIGN, bp->bnum, obufp,
511711066Srafael.vanoni@sun.com sizeof (dr_proto_hdr_t), (caddr_t)NULL, 0);
51181708Sstevel kmem_free(obufp, sizeof (dr_proto_hdr_t));
51191708Sstevel if (!err) {
51201708Sstevel if (drmach_array_set(drmach_boards, bp->bnum, 0) != 0)
51211708Sstevel err = DRMACH_INTERNAL_ERROR();
51221708Sstevel else
51231708Sstevel drmach_board_dispose(bp);
51241708Sstevel }
51251708Sstevel }
51261708Sstevel rw_exit(&drmach_boards_rwlock);
51271708Sstevel return (err);
51281708Sstevel }
51291708Sstevel
51301708Sstevel static sbd_error_t *
drmach_read_reg_addr(drmach_device_t * dp,uint64_t * p)51311708Sstevel drmach_read_reg_addr(drmach_device_t *dp, uint64_t *p)
51321708Sstevel {
51331708Sstevel int len;
51341708Sstevel drmach_reg_t reg;
51351708Sstevel drmach_node_t pp;
51361708Sstevel drmach_node_t *np = dp->node;
51371708Sstevel
51381708Sstevel /*
51391708Sstevel * If the node does not have a portid property,
51401708Sstevel * it represents a CMP device. For a CMP, the reg
51411708Sstevel * property of the parent holds the information of
51421708Sstevel * interest.
51431708Sstevel */
51441708Sstevel if (dp->node->n_getproplen(dp->node, "portid", &len) != 0) {
51451708Sstevel
51461708Sstevel if (dp->node->get_parent(dp->node, &pp) != 0) {
51471708Sstevel return (DRMACH_INTERNAL_ERROR());
51481708Sstevel }
51491708Sstevel np = &pp;
51501708Sstevel }
51511708Sstevel
51521708Sstevel if (np->n_getproplen(np, "reg", &len) != 0)
51531708Sstevel return (DRMACH_INTERNAL_ERROR());
51541708Sstevel
51551708Sstevel if (len != sizeof (reg))
51561708Sstevel return (DRMACH_INTERNAL_ERROR());
51571708Sstevel
51581708Sstevel if (np->n_getprop(np, "reg", ®, sizeof (reg)) != 0)
51591708Sstevel return (DRMACH_INTERNAL_ERROR());
51601708Sstevel
51611708Sstevel /* reassemble 64-bit base address */
51621708Sstevel *p = ((uint64_t)reg.reg_addr_hi << 32) | reg.reg_addr_lo;
51631708Sstevel
51641708Sstevel return (NULL);
51651708Sstevel }
51661708Sstevel
51671708Sstevel static void
drmach_cpu_read(uint64_t arg1,uint64_t arg2)51681708Sstevel drmach_cpu_read(uint64_t arg1, uint64_t arg2)
51691708Sstevel {
51701708Sstevel uint64_t *saf_config_reg = (uint64_t *)arg1;
51711708Sstevel uint_t *reg_read = (uint_t *)arg2;
51721708Sstevel
51731708Sstevel *saf_config_reg = lddsafconfig();
51741708Sstevel *reg_read = 0x1;
51751708Sstevel }
51761708Sstevel
51771708Sstevel /*
51781708Sstevel * A return value of 1 indicates success and 0 indicates a failure
51791708Sstevel */
51801708Sstevel static int
drmach_cpu_read_scr(drmach_cpu_t * cp,uint64_t * scr)51811708Sstevel drmach_cpu_read_scr(drmach_cpu_t *cp, uint64_t *scr)
51821708Sstevel {
51831708Sstevel
51841708Sstevel int rv = 0x0;
51851708Sstevel
51861708Sstevel *scr = 0x0;
51871708Sstevel
51881708Sstevel /*
51891708Sstevel * Confirm cpu was in ready set when xc was issued.
51901708Sstevel * This is done by verifying rv which is
51911708Sstevel * set to 0x1 when xc_one is successful.
51921708Sstevel */
51931708Sstevel xc_one(cp->dev.portid, (xcfunc_t *)drmach_cpu_read,
51941708Sstevel (uint64_t)scr, (uint64_t)&rv);
51951708Sstevel
51961708Sstevel return (rv);
51971708Sstevel
51981708Sstevel }
51991708Sstevel
52001708Sstevel static sbd_error_t *
drmach_cpu_read_cpuid(drmach_cpu_t * cp,processorid_t * cpuid)52011708Sstevel drmach_cpu_read_cpuid(drmach_cpu_t *cp, processorid_t *cpuid)
52021708Sstevel {
52031708Sstevel drmach_node_t *np;
52041708Sstevel
52051708Sstevel np = cp->dev.node;
52061708Sstevel
52071708Sstevel /*
52081708Sstevel * If a CPU does not have a portid property, it must
52091708Sstevel * be a CMP device with a cpuid property.
52101708Sstevel */
52111708Sstevel if (np->n_getprop(np, "portid", cpuid, sizeof (*cpuid)) != 0) {
52121708Sstevel
52131708Sstevel if (np->n_getprop(np, "cpuid", cpuid, sizeof (*cpuid)) != 0) {
52141708Sstevel return (DRMACH_INTERNAL_ERROR());
52151708Sstevel }
52161708Sstevel }
52171708Sstevel
52181708Sstevel return (NULL);
52191708Sstevel }
52201708Sstevel
52211708Sstevel /* Starcat CMP core id is bit 2 of the cpuid */
52221708Sstevel #define DRMACH_COREID_MASK (1u << 2)
52231708Sstevel #define DRMACH_CPUID2SRAM_IDX(id) \
52241708Sstevel ((id & DRMACH_COREID_MASK) >> 1 | (id & 0x1))
52251708Sstevel
52261708Sstevel static sbd_error_t *
drmach_cpu_new(drmach_device_t * proto,drmachid_t * idp)52271708Sstevel drmach_cpu_new(drmach_device_t *proto, drmachid_t *idp)
52281708Sstevel {
52291708Sstevel static void drmach_cpu_dispose(drmachid_t);
52301708Sstevel static sbd_error_t *drmach_cpu_release(drmachid_t);
52311708Sstevel static sbd_error_t *drmach_cpu_status(drmachid_t, drmach_status_t *);
52321708Sstevel
52331708Sstevel sbd_error_t *err;
52341708Sstevel uint64_t scr_pa;
52351708Sstevel drmach_cpu_t *cp = NULL;
52361708Sstevel pfn_t pfn;
52371708Sstevel uint64_t cpu_stardrb_offset, cpu_sram_pa;
52381708Sstevel int idx;
52391708Sstevel int impl;
52401708Sstevel processorid_t cpuid;
52411708Sstevel
52421708Sstevel err = drmach_read_reg_addr(proto, &scr_pa);
52431708Sstevel if (err) {
52441708Sstevel goto fail;
52451708Sstevel }
52461708Sstevel
52471708Sstevel cp = kmem_zalloc(sizeof (drmach_cpu_t), KM_SLEEP);
52481708Sstevel bcopy(proto, &cp->dev, sizeof (cp->dev));
52491708Sstevel cp->dev.node = drmach_node_dup(proto->node);
52501708Sstevel cp->dev.cm.isa = (void *)drmach_cpu_new;
52511708Sstevel cp->dev.cm.dispose = drmach_cpu_dispose;
52521708Sstevel cp->dev.cm.release = drmach_cpu_release;
52531708Sstevel cp->dev.cm.status = drmach_cpu_status;
52541708Sstevel cp->scr_pa = scr_pa;
52551708Sstevel
52561708Sstevel err = drmach_cpu_read_cpuid(cp, &cpuid);
52571708Sstevel if (err) {
52581708Sstevel goto fail;
52591708Sstevel }
52601708Sstevel
52611708Sstevel err = drmach_cpu_get_impl(cp, &impl);
52621708Sstevel if (err) {
52631708Sstevel goto fail;
52641708Sstevel }
52651708Sstevel
52661708Sstevel cp->cpuid = cpuid;
52671708Sstevel cp->coreid = STARCAT_CPUID_TO_COREID(cp->cpuid);
52681708Sstevel cp->dev.unum = STARCAT_CPUID_TO_AGENT(cp->cpuid);
52691708Sstevel
52701708Sstevel /*
52711708Sstevel * Init the board cpu type. Assumes all board cpus are the same type.
52721708Sstevel */
52731708Sstevel if (cp->dev.bp->cpu_impl == 0) {
52741708Sstevel cp->dev.bp->cpu_impl = impl;
52751708Sstevel }
52761708Sstevel ASSERT(cp->dev.bp->cpu_impl == impl);
52771708Sstevel
52781708Sstevel /*
52791708Sstevel * XXX CHEETAH SUPPORT
52801708Sstevel * determine if the domain uses Cheetah procs
52811708Sstevel */
52821708Sstevel if (drmach_is_cheetah < 0) {
52831708Sstevel drmach_is_cheetah = IS_CHEETAH(impl);
52841708Sstevel }
52851708Sstevel
52861708Sstevel /*
52871708Sstevel * Initialize TTE for mapping CPU SRAM STARDRB buffer.
52881708Sstevel * The STARDRB buffer (16KB on Cheetah+ boards, 32KB on
52891708Sstevel * Jaguar/Panther boards) is shared by all cpus in a Safari port
52901708Sstevel * pair. Each cpu uses 8KB according to the following layout:
52911708Sstevel *
52921708Sstevel * Page 0: even numbered Cheetah+'s and Panther/Jaguar core 0's
52931708Sstevel * Page 1: odd numbered Cheetah+'s and Panther/Jaguar core 0's
52941708Sstevel * Page 2: even numbered Panther/Jaguar core 1's
52951708Sstevel * Page 3: odd numbered Panther/Jaguar core 1's
52961708Sstevel */
52971708Sstevel idx = DRMACH_CPUID2SRAM_IDX(cp->cpuid);
52981708Sstevel cpu_stardrb_offset = cp->dev.bp->stardrb_offset + (PAGESIZE * idx);
52991708Sstevel cpu_sram_pa = DRMACH_CPU_SRAM_ADDR + cpu_stardrb_offset;
53001708Sstevel pfn = cpu_sram_pa >> PAGESHIFT;
53011708Sstevel
53021708Sstevel ASSERT(drmach_cpu_sram_tte[cp->cpuid].tte_inthi == 0 &&
53031708Sstevel drmach_cpu_sram_tte[cp->cpuid].tte_intlo == 0);
53041708Sstevel drmach_cpu_sram_tte[cp->cpuid].tte_inthi = TTE_PFN_INTHI(pfn) |
530511066Srafael.vanoni@sun.com TTE_VALID_INT | TTE_SZ_INT(TTE8K);
53061708Sstevel drmach_cpu_sram_tte[cp->cpuid].tte_intlo = TTE_PFN_INTLO(pfn) |
530711066Srafael.vanoni@sun.com TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
53081708Sstevel
53091708Sstevel DRMACH_PR("drmach_cpu_new: cpuid=%d, coreid=%d, stardrb_offset=0x%lx, "
53101708Sstevel "cpu_sram_offset=0x%lx, idx=%d\n", cp->cpuid, cp->coreid,
53111708Sstevel cp->dev.bp->stardrb_offset, cpu_stardrb_offset, idx);
53121708Sstevel
531311311SSurya.Prakki@Sun.COM (void) snprintf(cp->dev.cm.name, sizeof (cp->dev.cm.name), "%s%d",
53141708Sstevel cp->dev.type, cp->dev.unum);
53151708Sstevel
53161708Sstevel *idp = (drmachid_t)cp;
53171708Sstevel return (NULL);
53181708Sstevel
53191708Sstevel fail:
53201708Sstevel if (cp) {
53211708Sstevel drmach_node_dispose(cp->dev.node);
53221708Sstevel kmem_free(cp, sizeof (*cp));
53231708Sstevel }
53241708Sstevel
53251708Sstevel *idp = (drmachid_t)0;
53261708Sstevel return (err);
53271708Sstevel }
53281708Sstevel
53291708Sstevel static void
drmach_cpu_dispose(drmachid_t id)53301708Sstevel drmach_cpu_dispose(drmachid_t id)
53311708Sstevel {
53321708Sstevel drmach_cpu_t *self;
53331708Sstevel processorid_t cpuid;
53341708Sstevel
53351708Sstevel ASSERT(DRMACH_IS_CPU_ID(id));
53361708Sstevel
53371708Sstevel self = id;
53381708Sstevel if (self->dev.node)
53391708Sstevel drmach_node_dispose(self->dev.node);
53401708Sstevel
53411708Sstevel cpuid = self->cpuid;
53421708Sstevel ASSERT(TTE_IS_VALID(&drmach_cpu_sram_tte[cpuid]) &&
53431708Sstevel TTE_IS_8K(&drmach_cpu_sram_tte[cpuid]) &&
53441708Sstevel TTE_IS_PRIVILEGED(&drmach_cpu_sram_tte[cpuid]) &&
53451708Sstevel TTE_IS_LOCKED(&drmach_cpu_sram_tte[cpuid]));
53461708Sstevel drmach_cpu_sram_tte[cpuid].tte_inthi = 0;
53471708Sstevel drmach_cpu_sram_tte[cpuid].tte_intlo = 0;
53481708Sstevel
53491708Sstevel kmem_free(self, sizeof (*self));
53501708Sstevel }
53511708Sstevel
53521708Sstevel static int
drmach_cpu_start(struct cpu * cp)53531708Sstevel drmach_cpu_start(struct cpu *cp)
53541708Sstevel {
53551708Sstevel extern xcfunc_t drmach_set_lpa;
53561708Sstevel extern void restart_other_cpu(int);
53571708Sstevel int cpuid = cp->cpu_id;
53581708Sstevel int rv, bnum;
53591708Sstevel drmach_board_t *bp;
53601708Sstevel
53611708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
53621708Sstevel ASSERT(cpunodes[cpuid].nodeid != (pnode_t)0);
53631708Sstevel
53641708Sstevel cp->cpu_flags &= ~CPU_POWEROFF;
53651708Sstevel
53661708Sstevel /*
53671708Sstevel * NOTE: restart_other_cpu pauses cpus during the
53681708Sstevel * slave cpu start. This helps to quiesce the
53691708Sstevel * bus traffic a bit which makes the tick sync
53701708Sstevel * routine in the prom more robust.
53711708Sstevel */
53721708Sstevel DRMACH_PR("COLD START for cpu (%d)\n", cpuid);
53731708Sstevel
53741708Sstevel if (prom_hotaddcpu(cpuid) != 0) {
53751708Sstevel cmn_err(CE_PANIC, "prom_hotaddcpu() for cpuid=%d failed.",
537611066Srafael.vanoni@sun.com cpuid);
53771708Sstevel }
53781708Sstevel
53791708Sstevel restart_other_cpu(cpuid);
53801708Sstevel
53811708Sstevel bnum = drmach_portid2bnum(cpunodes[cpuid].portid);
53821708Sstevel rv = drmach_array_get(drmach_boards, bnum, (drmachid_t)&bp);
53831708Sstevel if (rv == -1 || bp == NULL) {
53841708Sstevel DRMACH_PR("drmach_cpu_start: cannot read board info for "
538511311SSurya.Prakki@Sun.COM "cpuid=%d: rv=%d, bp=%p\n", cpuid, rv, (void *)bp);
53861708Sstevel } else if (DRMACH_L1_SET_LPA(bp) && drmach_reprogram_lpa) {
53871708Sstevel int exp;
53881708Sstevel int ntries;
53891708Sstevel
53901708Sstevel mutex_enter(&drmach_xt_mb_lock);
53911708Sstevel mutex_enter(&drmach_slice_table_lock);
53921708Sstevel bzero((void *)drmach_xt_mb, drmach_xt_mb_size);
53931708Sstevel
53941708Sstevel /*
53951708Sstevel * drmach_slice_table[*]
53961708Sstevel * bit 5 valid
53971708Sstevel * bit 0:4 slice number
53981708Sstevel *
53991708Sstevel * drmach_xt_mb[*] format for drmach_set_lpa
54001708Sstevel * bit 7 valid
54011708Sstevel * bit 6 set null LPA (overrides bits 0:4)
54021708Sstevel * bit 0:4 slice number
54031708Sstevel *
54041708Sstevel * drmach_set_lpa derives processor CBASE and CBND
54051708Sstevel * from bits 6 and 0:4 of drmach_xt_mb. If bit 6 is
54061708Sstevel * set, then CBASE = CBND = 0. Otherwise, CBASE = slice
54071708Sstevel * number; CBND = slice number + 1.
54081708Sstevel * No action is taken if bit 7 is zero.
54091708Sstevel */
54101708Sstevel exp = (cpuid >> 5) & 0x1f;
54111708Sstevel if (drmach_slice_table[exp] & 0x20) {
54121708Sstevel drmach_xt_mb[cpuid] = 0x80 |
541311066Srafael.vanoni@sun.com (drmach_slice_table[exp] & 0x1f);
54141708Sstevel } else {
54151708Sstevel drmach_xt_mb[cpuid] = 0x80 | 0x40;
54161708Sstevel }
54171708Sstevel
54181708Sstevel drmach_xt_ready = 0;
54191708Sstevel
54201708Sstevel xt_one(cpuid, drmach_set_lpa, NULL, NULL);
54211708Sstevel
54221708Sstevel ntries = drmach_cpu_ntries;
54231708Sstevel while (!drmach_xt_ready && ntries) {
54241708Sstevel DELAY(drmach_cpu_delay);
54251708Sstevel ntries--;
54261708Sstevel }
54271708Sstevel
54281708Sstevel mutex_exit(&drmach_slice_table_lock);
54291708Sstevel mutex_exit(&drmach_xt_mb_lock);
54301708Sstevel
54311708Sstevel DRMACH_PR(
543211066Srafael.vanoni@sun.com "waited %d out of %d tries for drmach_set_lpa on cpu%d",
543311066Srafael.vanoni@sun.com drmach_cpu_ntries - ntries, drmach_cpu_ntries,
543411066Srafael.vanoni@sun.com cp->cpu_id);
543511066Srafael.vanoni@sun.com }
543611066Srafael.vanoni@sun.com
543711066Srafael.vanoni@sun.com xt_one(cpuid, vtag_flushpage_tl1, (uint64_t)drmach_cpu_sram_va,
543811066Srafael.vanoni@sun.com (uint64_t)ksfmmup);
54391708Sstevel
54401708Sstevel return (0);
54411708Sstevel }
54421708Sstevel
54431708Sstevel /*
54441708Sstevel * A detaching CPU is xcalled with an xtrap to drmach_cpu_stop_self() after
54451708Sstevel * it has been offlined. The function of this routine is to get the cpu
54461708Sstevel * spinning in a safe place. The requirement is that the system will not
54471708Sstevel * reference anything on the detaching board (memory and i/o is detached
54481708Sstevel * elsewhere) and that the CPU not reference anything on any other board
54491708Sstevel * in the system. This isolation is required during and after the writes
54501708Sstevel * to the domain masks to remove the board from the domain.
54511708Sstevel *
54521708Sstevel * To accomplish this isolation the following is done:
54531708Sstevel * 1) Create a locked mapping to the STARDRB data buffer located
54541708Sstevel * in this cpu's sram. There is one TTE per cpu, initialized in
54551708Sstevel * drmach_cpu_new(). The cpuid is used to select which TTE to use.
54561708Sstevel * Each Safari port pair shares the CPU SRAM on a Serengeti CPU/MEM
54571708Sstevel * board. The STARDRB buffer is 16KB on Cheetah+ boards, 32KB on Jaguar
54581708Sstevel * boards. Each STARDRB buffer is logically divided by DR into one
54591708Sstevel * 8KB page per cpu (or Jaguar core).
54601708Sstevel * 2) Copy the target function (drmach_shutdown_asm) into buffer.
54611708Sstevel * 3) Jump to function now in the cpu sram.
54621708Sstevel * Function will:
54631708Sstevel * 3.1) Flush its Ecache (displacement).
54641708Sstevel * 3.2) Flush its Dcache with HW mechanism.
54651708Sstevel * 3.3) Flush its Icache with HW mechanism.
54661708Sstevel * 3.4) Flush all valid and _unlocked_ D-TLB and I-TLB entries.
54671708Sstevel * 3.5) Set LPA to NULL
54681708Sstevel * 3.6) Clear xt_mb to signal completion. Note: cache line is
54691708Sstevel * recovered by drmach_cpu_poweroff().
54701708Sstevel * 4) Jump into an infinite loop.
54711708Sstevel */
54721708Sstevel
54731708Sstevel static void
drmach_cpu_stop_self(void)54741708Sstevel drmach_cpu_stop_self(void)
54751708Sstevel {
547611066Srafael.vanoni@sun.com extern void drmach_shutdown_asm(uint64_t, uint64_t, int, int, uint64_t);
547711066Srafael.vanoni@sun.com extern void drmach_shutdown_asm_end(void);
54781708Sstevel
54791708Sstevel tte_t *tte;
54801708Sstevel uint_t *p, *q;
54811708Sstevel uint64_t stack_pointer;
54821708Sstevel
54831708Sstevel ASSERT(((ptrdiff_t)drmach_shutdown_asm_end -
548411066Srafael.vanoni@sun.com (ptrdiff_t)drmach_shutdown_asm) < PAGESIZE);
54851708Sstevel
54861708Sstevel tte = &drmach_cpu_sram_tte[CPU->cpu_id];
548711066Srafael.vanoni@sun.com ASSERT(TTE_IS_VALID(tte) && TTE_IS_8K(tte) && TTE_IS_PRIVILEGED(tte) &&
548811066Srafael.vanoni@sun.com TTE_IS_LOCKED(tte));
54892241Shuah sfmmu_dtlb_ld_kva(drmach_cpu_sram_va, tte);
54902241Shuah sfmmu_itlb_ld_kva(drmach_cpu_sram_va, tte);
54911708Sstevel
54921708Sstevel /* copy text. standard bcopy not designed to work in nc space */
54931708Sstevel p = (uint_t *)drmach_cpu_sram_va;
54941708Sstevel q = (uint_t *)drmach_shutdown_asm;
54951708Sstevel while (q < (uint_t *)drmach_shutdown_asm_end)
54961708Sstevel *p++ = *q++;
54971708Sstevel
54981708Sstevel /* zero to assist debug */
54991708Sstevel q = (uint_t *)(drmach_cpu_sram_va + PAGESIZE);
55001708Sstevel while (p < q)
55011708Sstevel *p++ = 0;
55021708Sstevel
55031708Sstevel /* a parking spot for the stack pointer */
55041708Sstevel stack_pointer = (uint64_t)q;
55051708Sstevel
55061708Sstevel /* call copy of drmach_shutdown_asm */
55071708Sstevel (*(void (*)())drmach_cpu_sram_va)(
550811066Srafael.vanoni@sun.com stack_pointer,
550911066Srafael.vanoni@sun.com drmach_iocage_paddr,
551011066Srafael.vanoni@sun.com cpunodes[CPU->cpu_id].ecache_size,
551111066Srafael.vanoni@sun.com cpunodes[CPU->cpu_id].ecache_linesize,
551211066Srafael.vanoni@sun.com va_to_pa((void *)&drmach_xt_mb[CPU->cpu_id]));
55131708Sstevel }
55141708Sstevel
55151708Sstevel static void
drmach_cpu_shutdown_self(void)55161708Sstevel drmach_cpu_shutdown_self(void)
55171708Sstevel {
55181708Sstevel cpu_t *cp = CPU;
55191708Sstevel int cpuid = cp->cpu_id;
55201708Sstevel extern void flush_windows(void);
55211708Sstevel
55221708Sstevel flush_windows();
55231708Sstevel
55241708Sstevel (void) spl8();
55251708Sstevel
55261708Sstevel ASSERT(cp->cpu_intr_actv == 0);
55271708Sstevel ASSERT(cp->cpu_thread == cp->cpu_idle_thread ||
55281708Sstevel cp->cpu_thread == cp->cpu_startup_thread);
55291708Sstevel
55301708Sstevel cp->cpu_flags = CPU_OFFLINE | CPU_QUIESCED | CPU_POWEROFF;
55311708Sstevel
55321708Sstevel drmach_cpu_stop_self();
55331708Sstevel
55341708Sstevel cmn_err(CE_PANIC, "CPU %d FAILED TO SHUTDOWN", cpuid);
55351708Sstevel }
55361708Sstevel
55371708Sstevel static sbd_error_t *
drmach_cpu_release(drmachid_t id)55381708Sstevel drmach_cpu_release(drmachid_t id)
55391708Sstevel {
55401708Sstevel drmach_cpu_t *cp;
55411708Sstevel struct cpu *cpu;
55421708Sstevel sbd_error_t *err;
55431708Sstevel
55441708Sstevel if (!DRMACH_IS_CPU_ID(id))
55451708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
55461708Sstevel cp = id;
55471708Sstevel
55481708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
55491708Sstevel
55501708Sstevel cpu = cpu_get(cp->cpuid);
55511708Sstevel if (cpu == NULL)
55521708Sstevel err = DRMACH_INTERNAL_ERROR();
55531708Sstevel else
55541708Sstevel err = NULL;
55551708Sstevel
55561708Sstevel return (err);
55571708Sstevel }
55581708Sstevel
55591708Sstevel static sbd_error_t *
drmach_cpu_status(drmachid_t id,drmach_status_t * stat)55601708Sstevel drmach_cpu_status(drmachid_t id, drmach_status_t *stat)
55611708Sstevel {
55621708Sstevel drmach_cpu_t *cp;
55631708Sstevel drmach_device_t *dp;
55641708Sstevel
55651708Sstevel ASSERT(DRMACH_IS_CPU_ID(id));
55661708Sstevel cp = id;
55671708Sstevel dp = &cp->dev;
55681708Sstevel
55691708Sstevel stat->assigned = dp->bp->assigned;
55701708Sstevel stat->powered = dp->bp->powered;
55711708Sstevel mutex_enter(&cpu_lock);
55721708Sstevel stat->configured = (cpu_get(cp->cpuid) != NULL);
55731708Sstevel mutex_exit(&cpu_lock);
55741708Sstevel stat->busy = dp->busy;
557511311SSurya.Prakki@Sun.COM (void) strncpy(stat->type, dp->type, sizeof (stat->type));
55761708Sstevel stat->info[0] = '\0';
55771708Sstevel
55781708Sstevel return (NULL);
55791708Sstevel }
55801708Sstevel
55811708Sstevel sbd_error_t *
drmach_cpu_disconnect(drmachid_t id)55821708Sstevel drmach_cpu_disconnect(drmachid_t id)
55831708Sstevel {
55841708Sstevel if (!DRMACH_IS_CPU_ID(id))
55851708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
55861708Sstevel
55871708Sstevel return (NULL);
55881708Sstevel }
55891708Sstevel
55901708Sstevel sbd_error_t *
drmach_cpu_get_id(drmachid_t id,processorid_t * cpuid)55911708Sstevel drmach_cpu_get_id(drmachid_t id, processorid_t *cpuid)
55921708Sstevel {
55931708Sstevel drmach_cpu_t *cpu;
55941708Sstevel
55951708Sstevel if (!DRMACH_IS_CPU_ID(id))
55961708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
55971708Sstevel cpu = id;
55981708Sstevel
55991708Sstevel *cpuid = cpu->cpuid;
56001708Sstevel return (NULL);
56011708Sstevel }
56021708Sstevel
56031708Sstevel sbd_error_t *
drmach_cpu_get_impl(drmachid_t id,int * ip)56041708Sstevel drmach_cpu_get_impl(drmachid_t id, int *ip)
56051708Sstevel {
56061708Sstevel drmach_node_t *np;
56071708Sstevel int impl;
56081708Sstevel
56091708Sstevel if (!DRMACH_IS_CPU_ID(id))
56101708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
56111708Sstevel
56121708Sstevel np = ((drmach_device_t *)id)->node;
56131708Sstevel
56141708Sstevel if (np->n_getprop(np, "implementation#", &impl, sizeof (impl)) == -1) {
56151708Sstevel return (DRMACH_INTERNAL_ERROR());
56161708Sstevel }
56171708Sstevel
56181708Sstevel *ip = impl;
56191708Sstevel
56201708Sstevel return (NULL);
56211708Sstevel }
56221708Sstevel
56231708Sstevel /*
56241708Sstevel * Flush this cpu's ecache, then ensure all outstanding safari
56251708Sstevel * transactions have retired.
56261708Sstevel */
56271708Sstevel void
drmach_cpu_flush_ecache_sync(void)56281708Sstevel drmach_cpu_flush_ecache_sync(void)
56291708Sstevel {
56301708Sstevel uint64_t *p;
56311708Sstevel
56321708Sstevel ASSERT(curthread->t_bound_cpu == CPU);
56331708Sstevel
56341708Sstevel cpu_flush_ecache();
56351708Sstevel
56361708Sstevel mutex_enter(&drmach_bus_sync_lock);
56371708Sstevel for (p = drmach_bus_sync_list; *p; p++)
56381708Sstevel (void) ldphys(*p);
56391708Sstevel mutex_exit(&drmach_bus_sync_lock);
56401708Sstevel
56411708Sstevel cpu_flush_ecache();
56421708Sstevel }
56431708Sstevel
56441708Sstevel sbd_error_t *
drmach_get_dip(drmachid_t id,dev_info_t ** dip)56451708Sstevel drmach_get_dip(drmachid_t id, dev_info_t **dip)
56461708Sstevel {
56471708Sstevel drmach_device_t *dp;
56481708Sstevel
56491708Sstevel if (!DRMACH_IS_DEVICE_ID(id))
56501708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
56511708Sstevel dp = id;
56521708Sstevel
56531708Sstevel *dip = dp->node->n_getdip(dp->node);
56541708Sstevel return (NULL);
56551708Sstevel }
56561708Sstevel
56571708Sstevel sbd_error_t *
drmach_io_is_attached(drmachid_t id,int * yes)56581708Sstevel drmach_io_is_attached(drmachid_t id, int *yes)
56591708Sstevel {
56601708Sstevel drmach_device_t *dp;
56611708Sstevel dev_info_t *dip;
56621708Sstevel int state;
56631708Sstevel
56641708Sstevel if (!DRMACH_IS_IO_ID(id))
56651708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
56661708Sstevel dp = id;
56671708Sstevel
56681708Sstevel dip = dp->node->n_getdip(dp->node);
56691708Sstevel if (dip == NULL) {
56701708Sstevel *yes = 0;
56711708Sstevel return (NULL);
56721708Sstevel }
56731708Sstevel
56741708Sstevel state = ddi_get_devstate(dip);
56751708Sstevel *yes = i_ddi_devi_attached(dip) || (state == DDI_DEVSTATE_UP);
56761708Sstevel
56771708Sstevel return (NULL);
56781708Sstevel }
56791708Sstevel
56801708Sstevel static int
drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t * dip)56811708Sstevel drmach_dip_is_schizo_xmits_0_pci_b(dev_info_t *dip)
56821708Sstevel {
56831708Sstevel char dtype[OBP_MAXPROPNAME];
56841708Sstevel int portid;
56851708Sstevel uint_t pci_csr_base;
56861708Sstevel struct pci_phys_spec *regbuf = NULL;
56871708Sstevel int rv, len;
56881708Sstevel
56891708Sstevel ASSERT(dip != NULL);
56901708Sstevel rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "device_type", &len);
56911708Sstevel if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (dtype)))
56921708Sstevel return (0);
56931708Sstevel
56941708Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0, "device_type",
569511066Srafael.vanoni@sun.com (caddr_t)dtype, &len) == DDI_PROP_SUCCESS) {
56961708Sstevel
56971708Sstevel if (strncmp(dtype, "pci", 3) == 0) {
56981708Sstevel
56991708Sstevel /*
57001708Sstevel * Get safari portid. All schizo/xmits 0
57011708Sstevel * safari IDs end in 0x1C.
57021708Sstevel */
570311066Srafael.vanoni@sun.com rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid",
570411066Srafael.vanoni@sun.com &len);
57051708Sstevel
57061708Sstevel if ((rv != DDI_PROP_SUCCESS) ||
570711066Srafael.vanoni@sun.com (len > sizeof (portid)))
570811066Srafael.vanoni@sun.com return (0);
57091708Sstevel
57101708Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
571111066Srafael.vanoni@sun.com "portid", (caddr_t)&portid, &len);
57121708Sstevel
57131708Sstevel if (rv != DDI_PROP_SUCCESS)
57141708Sstevel return (0);
57151708Sstevel
57161708Sstevel if ((portid & 0x1F) != 0x1C)
57171708Sstevel return (0);
57181708Sstevel
57191708Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
572011066Srafael.vanoni@sun.com DDI_PROP_DONTPASS, "reg", (caddr_t)®buf,
572111066Srafael.vanoni@sun.com &len) == DDI_PROP_SUCCESS) {
57221708Sstevel
57231708Sstevel pci_csr_base = regbuf[0].pci_phys_mid &
572411066Srafael.vanoni@sun.com PCI_CONF_ADDR_MASK;
57251708Sstevel kmem_free(regbuf, len);
57261708Sstevel /*
57271708Sstevel * All PCI B-Leafs are at configspace 0x70.0000.
57281708Sstevel */
57291708Sstevel if (pci_csr_base == 0x700000)
57301708Sstevel return (1);
57311708Sstevel }
57321708Sstevel }
57331708Sstevel }
57341708Sstevel return (0);
57351708Sstevel }
57361708Sstevel
57371708Sstevel #define SCHIZO_BINDING_NAME "pci108e,8001"
57381708Sstevel #define XMITS_BINDING_NAME "pci108e,8002"
57391708Sstevel
57401708Sstevel /*
57411708Sstevel * Verify if the dip is an instance of MAN 'eri'.
57421708Sstevel */
57431708Sstevel static int
drmach_dip_is_man_eri(dev_info_t * dip)57441708Sstevel drmach_dip_is_man_eri(dev_info_t *dip)
57451708Sstevel {
57461708Sstevel struct pci_phys_spec *regbuf = NULL;
57471708Sstevel dev_info_t *parent_dip;
57481708Sstevel char *name;
57491708Sstevel uint_t pci_device;
57501708Sstevel uint_t pci_function;
57511708Sstevel int len;
57521708Sstevel
57531708Sstevel if (dip == NULL)
57541708Sstevel return (0);
57551708Sstevel /*
57561708Sstevel * Verify if the parent is schizo(xmits)0 and pci B leaf.
57571708Sstevel */
57581708Sstevel if (((parent_dip = ddi_get_parent(dip)) == NULL) ||
575911066Srafael.vanoni@sun.com ((name = ddi_binding_name(parent_dip)) == NULL))
57601708Sstevel return (0);
57611708Sstevel if (strcmp(name, SCHIZO_BINDING_NAME) != 0) {
57621708Sstevel /*
57631708Sstevel * This RIO could be on XMITS, so get the dip to
57641708Sstevel * XMITS PCI Leaf.
57651708Sstevel */
57661708Sstevel if ((parent_dip = ddi_get_parent(parent_dip)) == NULL)
57671708Sstevel return (0);
57681708Sstevel if (((name = ddi_binding_name(parent_dip)) == NULL) ||
576911066Srafael.vanoni@sun.com (strcmp(name, XMITS_BINDING_NAME) != 0)) {
57701708Sstevel return (0);
57711708Sstevel }
57721708Sstevel }
57731708Sstevel if (!drmach_dip_is_schizo_xmits_0_pci_b(parent_dip))
57741708Sstevel return (0);
57751708Sstevel /*
57761708Sstevel * Finally make sure it is the MAN eri.
57771708Sstevel */
57781708Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
577911066Srafael.vanoni@sun.com "reg", (caddr_t)®buf, &len) == DDI_PROP_SUCCESS) {
57801708Sstevel
57811708Sstevel pci_device = PCI_REG_DEV_G(regbuf->pci_phys_hi);
57821708Sstevel pci_function = PCI_REG_FUNC_G(regbuf->pci_phys_hi);
57831708Sstevel kmem_free(regbuf, len);
57841708Sstevel
57851708Sstevel /*
57861708Sstevel * The network function of the RIO ASIC will always be
57871708Sstevel * device 3 and function 1 ("network@3,1").
57881708Sstevel */
57891708Sstevel if ((pci_device == 3) && (pci_function == 1))
57901708Sstevel return (1);
57911708Sstevel }
57921708Sstevel return (0);
57931708Sstevel }
57941708Sstevel
57951708Sstevel typedef struct {
57961708Sstevel int iosram_inst;
57971708Sstevel dev_info_t *eri_dip;
57981708Sstevel int bnum;
57991708Sstevel } drmach_io_inst_t;
58001708Sstevel
58011708Sstevel int
drmach_board_find_io_insts(dev_info_t * dip,void * args)58021708Sstevel drmach_board_find_io_insts(dev_info_t *dip, void *args)
58031708Sstevel {
58041708Sstevel drmach_io_inst_t *ios = (drmach_io_inst_t *)args;
58051708Sstevel
58061708Sstevel int rv;
58071708Sstevel int len;
58081708Sstevel int portid;
58091708Sstevel char name[OBP_MAXDRVNAME];
58101708Sstevel
58111708Sstevel rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "portid", &len);
58121708Sstevel
58131708Sstevel if ((rv != DDI_PROP_SUCCESS) || (len > sizeof (portid))) {
58141708Sstevel return (DDI_WALK_CONTINUE);
58151708Sstevel }
58161708Sstevel
58171708Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, 0,
581811066Srafael.vanoni@sun.com "portid", (caddr_t)&portid, &len);
58191708Sstevel if (rv != DDI_PROP_SUCCESS)
58201708Sstevel return (DDI_WALK_CONTINUE);
58211708Sstevel
58221708Sstevel /* ignore devices that are not on this board */
58231708Sstevel if (drmach_portid2bnum(portid) != ios->bnum)
58241708Sstevel return (DDI_WALK_CONTINUE);
58251708Sstevel
58261708Sstevel if ((ios->iosram_inst < 0) || (ios->eri_dip == NULL)) {
582711066Srafael.vanoni@sun.com rv = ddi_getproplen(DDI_DEV_T_ANY, dip, 0, "name", &len);
58281708Sstevel if (rv == DDI_PROP_SUCCESS) {
58291708Sstevel
58301708Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
583111066Srafael.vanoni@sun.com 0, "name",
583211066Srafael.vanoni@sun.com (caddr_t)name, &len);
58331708Sstevel if (rv != DDI_PROP_SUCCESS)
58341708Sstevel return (DDI_WALK_CONTINUE);
58351708Sstevel
58361708Sstevel if (strncmp("iosram", name, 6) == 0) {
58371708Sstevel ios->iosram_inst = ddi_get_instance(dip);
58381708Sstevel if (ios->eri_dip == NULL)
58391708Sstevel return (DDI_WALK_CONTINUE);
58401708Sstevel else
58411708Sstevel return (DDI_WALK_TERMINATE);
58421708Sstevel } else {
58431708Sstevel if (drmach_dip_is_man_eri(dip)) {
58441708Sstevel ASSERT(ios->eri_dip == NULL);
58451708Sstevel ndi_hold_devi(dip);
58461708Sstevel ios->eri_dip = dip;
58471708Sstevel if (ios->iosram_inst < 0)
58481708Sstevel return (DDI_WALK_CONTINUE);
58491708Sstevel else
58501708Sstevel return (DDI_WALK_TERMINATE);
58511708Sstevel }
58521708Sstevel }
58531708Sstevel }
58541708Sstevel }
58551708Sstevel return (DDI_WALK_CONTINUE);
58561708Sstevel }
58571708Sstevel
58581708Sstevel sbd_error_t *
drmach_io_pre_release(drmachid_t id)58591708Sstevel drmach_io_pre_release(drmachid_t id)
58601708Sstevel {
58611708Sstevel drmach_io_inst_t ios;
58621708Sstevel drmach_board_t *bp;
58631708Sstevel int rv = 0;
58641708Sstevel sbd_error_t *err = NULL;
58651708Sstevel drmach_device_t *dp;
58661708Sstevel dev_info_t *rdip;
58671708Sstevel int circ;
58681708Sstevel
58691708Sstevel if (!DRMACH_IS_IO_ID(id))
58701708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
58711708Sstevel dp = id;
58721708Sstevel bp = dp->bp;
58731708Sstevel
58741708Sstevel rdip = dp->node->n_getdip(dp->node);
58751708Sstevel
58761708Sstevel /* walk device tree to find iosram instance for the board */
58771708Sstevel ios.iosram_inst = -1;
58781708Sstevel ios.eri_dip = NULL;
58791708Sstevel ios.bnum = bp->bnum;
58801708Sstevel
58811708Sstevel ndi_devi_enter(rdip, &circ);
58821708Sstevel ddi_walk_devs(ddi_get_child(rdip), drmach_board_find_io_insts,
588311066Srafael.vanoni@sun.com (void *)&ios);
58841708Sstevel
58851708Sstevel DRMACH_PR("drmach_io_pre_release: bnum=%d iosram=%d eri=0x%p\n",
588611311SSurya.Prakki@Sun.COM ios.bnum, ios.iosram_inst, (void *)ios.eri_dip);
58871708Sstevel ndi_devi_exit(rdip, circ);
58881708Sstevel
58891708Sstevel if (ios.eri_dip) {
58901708Sstevel /*
58911708Sstevel * Release hold acquired in drmach_board_find_io_insts()
58921708Sstevel */
58931708Sstevel ndi_rele_devi(ios.eri_dip);
58941708Sstevel }
58951708Sstevel if (ios.iosram_inst >= 0) {
58961708Sstevel /* call for tunnel switch */
58971708Sstevel do {
58981708Sstevel DRMACH_PR("calling iosram_switchfrom(%d)\n",
589911066Srafael.vanoni@sun.com ios.iosram_inst);
59001708Sstevel rv = iosram_switchfrom(ios.iosram_inst);
59011708Sstevel if (rv)
59021708Sstevel DRMACH_PR("iosram_switchfrom returned %d\n",
590311066Srafael.vanoni@sun.com rv);
59041708Sstevel } while (rv == EAGAIN);
59051708Sstevel
59061708Sstevel if (rv)
59071708Sstevel err = drerr_new(0, ESTC_IOSWITCH, NULL);
59081708Sstevel }
59091708Sstevel return (err);
59101708Sstevel }
59111708Sstevel
59121708Sstevel sbd_error_t *
drmach_io_unrelease(drmachid_t id)59131708Sstevel drmach_io_unrelease(drmachid_t id)
59141708Sstevel {
59151708Sstevel dev_info_t *dip;
59161708Sstevel sbd_error_t *err = NULL;
59171708Sstevel drmach_device_t *dp;
59181708Sstevel
59191708Sstevel if (!DRMACH_IS_IO_ID(id))
59201708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
59211708Sstevel dp = id;
59221708Sstevel
59231708Sstevel dip = dp->node->n_getdip(dp->node);
59241708Sstevel
59251708Sstevel if (dip == NULL)
59261708Sstevel err = DRMACH_INTERNAL_ERROR();
59271708Sstevel else {
59281708Sstevel int (*func)(dev_info_t *dip);
59291708Sstevel
59301708Sstevel func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach",
593111066Srafael.vanoni@sun.com 0);
59321708Sstevel
59331708Sstevel if (func) {
59341708Sstevel drmach_io_inst_t ios;
59351708Sstevel dev_info_t *pdip;
59361708Sstevel int circ;
59371708Sstevel
59381708Sstevel /*
59391708Sstevel * Walk device tree to find rio dip for the board
59401708Sstevel * Since we are not interested in iosram instance here,
59411708Sstevel * initialize it to 0, so that the walk terminates as
59421708Sstevel * soon as eri dip is found.
59431708Sstevel */
59441708Sstevel ios.iosram_inst = 0;
59451708Sstevel ios.eri_dip = NULL;
59461708Sstevel ios.bnum = dp->bp->bnum;
59471708Sstevel
59481708Sstevel if (pdip = ddi_get_parent(dip)) {
59491708Sstevel ndi_hold_devi(pdip);
59501708Sstevel ndi_devi_enter(pdip, &circ);
59511708Sstevel }
59521708Sstevel /*
59531708Sstevel * Root node doesn't have to be held in any way.
59541708Sstevel */
595511066Srafael.vanoni@sun.com ddi_walk_devs(dip, drmach_board_find_io_insts,
595611066Srafael.vanoni@sun.com (void *)&ios);
59571708Sstevel
59581708Sstevel if (pdip) {
59591708Sstevel ndi_devi_exit(pdip, circ);
59601708Sstevel ndi_rele_devi(pdip);
59611708Sstevel }
59621708Sstevel
59631708Sstevel DRMACH_PR("drmach_io_unrelease: bnum=%d eri=0x%p\n",
596411311SSurya.Prakki@Sun.COM ios.bnum, (void *)ios.eri_dip);
59651708Sstevel
59661708Sstevel if (ios.eri_dip) {
59671708Sstevel DRMACH_PR("calling man_dr_attach\n");
59681708Sstevel if ((*func)(ios.eri_dip))
596911066Srafael.vanoni@sun.com err = drerr_new(0, ESTC_NWSWITCH, NULL);
59701708Sstevel /*
59711708Sstevel * Release hold acquired in
59721708Sstevel * drmach_board_find_io_insts()
59731708Sstevel */
59741708Sstevel ndi_rele_devi(ios.eri_dip);
59751708Sstevel }
59761708Sstevel } else
59771708Sstevel DRMACH_PR("man_dr_attach NOT present\n");
59781708Sstevel }
59791708Sstevel return (err);
59801708Sstevel }
59811708Sstevel
59821708Sstevel static sbd_error_t *
drmach_io_release(drmachid_t id)59831708Sstevel drmach_io_release(drmachid_t id)
59841708Sstevel {
59851708Sstevel dev_info_t *dip;
59861708Sstevel sbd_error_t *err = NULL;
59871708Sstevel drmach_device_t *dp;
59881708Sstevel
59891708Sstevel if (!DRMACH_IS_IO_ID(id))
59901708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
59911708Sstevel dp = id;
59921708Sstevel
59931708Sstevel dip = dp->node->n_getdip(dp->node);
59941708Sstevel
59951708Sstevel if (dip == NULL)
59961708Sstevel err = DRMACH_INTERNAL_ERROR();
59971708Sstevel else {
59981708Sstevel int (*func)(dev_info_t *dip);
59991708Sstevel
60001708Sstevel func = (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_detach",
600111066Srafael.vanoni@sun.com 0);
60021708Sstevel
60031708Sstevel if (func) {
60041708Sstevel drmach_io_inst_t ios;
60051708Sstevel dev_info_t *pdip;
60061708Sstevel int circ;
60071708Sstevel
60081708Sstevel /*
60091708Sstevel * Walk device tree to find rio dip for the board
60101708Sstevel * Since we are not interested in iosram instance here,
60111708Sstevel * initialize it to 0, so that the walk terminates as
60121708Sstevel * soon as eri dip is found.
60131708Sstevel */
60141708Sstevel ios.iosram_inst = 0;
60151708Sstevel ios.eri_dip = NULL;
60161708Sstevel ios.bnum = dp->bp->bnum;
60171708Sstevel
60181708Sstevel if (pdip = ddi_get_parent(dip)) {
60191708Sstevel ndi_hold_devi(pdip);
60201708Sstevel ndi_devi_enter(pdip, &circ);
60211708Sstevel }
60221708Sstevel /*
60231708Sstevel * Root node doesn't have to be held in any way.
60241708Sstevel */
602511066Srafael.vanoni@sun.com ddi_walk_devs(dip, drmach_board_find_io_insts,
602611066Srafael.vanoni@sun.com (void *)&ios);
60271708Sstevel
60281708Sstevel if (pdip) {
60291708Sstevel ndi_devi_exit(pdip, circ);
60301708Sstevel ndi_rele_devi(pdip);
60311708Sstevel }
60321708Sstevel
60331708Sstevel DRMACH_PR("drmach_io_release: bnum=%d eri=0x%p\n",
603411311SSurya.Prakki@Sun.COM ios.bnum, (void *)ios.eri_dip);
60351708Sstevel
60361708Sstevel if (ios.eri_dip) {
60371708Sstevel DRMACH_PR("calling man_dr_detach\n");
60381708Sstevel if ((*func)(ios.eri_dip))
603911066Srafael.vanoni@sun.com err = drerr_new(0, ESTC_NWSWITCH, NULL);
60401708Sstevel /*
60411708Sstevel * Release hold acquired in
60421708Sstevel * drmach_board_find_io_insts()
60431708Sstevel */
60441708Sstevel ndi_rele_devi(ios.eri_dip);
60451708Sstevel }
60461708Sstevel } else
60471708Sstevel DRMACH_PR("man_dr_detach NOT present\n");
60481708Sstevel }
60491708Sstevel return (err);
60501708Sstevel }
60511708Sstevel
60521708Sstevel sbd_error_t *
drmach_io_post_release(drmachid_t id)60531708Sstevel drmach_io_post_release(drmachid_t id)
60541708Sstevel {
60551708Sstevel char *path;
60561708Sstevel dev_info_t *rdip;
60571708Sstevel drmach_device_t *dp;
60581708Sstevel
60591708Sstevel if (!DRMACH_IS_DEVICE_ID(id))
60601708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
60611708Sstevel dp = id;
60621708Sstevel
60631708Sstevel rdip = dp->node->n_getdip(dp->node);
60641708Sstevel
60651708Sstevel /*
60661708Sstevel * Always called after drmach_unconfigure() which on Starcat
60671708Sstevel * unconfigures the branch but doesn't remove it so the
60681708Sstevel * dip must always exist.
60691708Sstevel */
60701708Sstevel ASSERT(rdip);
60711708Sstevel
60721708Sstevel ASSERT(e_ddi_branch_held(rdip));
60731708Sstevel #ifdef DEBUG
60741708Sstevel path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
60751708Sstevel (void) ddi_pathname(rdip, path);
60761708Sstevel DRMACH_PR("post_release dip path is: %s\n", path);
60771708Sstevel kmem_free(path, MAXPATHLEN);
60781708Sstevel #endif
60791708Sstevel
60801708Sstevel if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
60811708Sstevel if (schpc_remove_pci(rdip)) {
60821708Sstevel DRMACH_PR("schpc_remove_pci failed\n");
60831708Sstevel return (drerr_new(0, ESBD_OFFLINE, NULL));
60841708Sstevel } else {
60851708Sstevel DRMACH_PR("schpc_remove_pci succeeded\n");
60861708Sstevel }
60871708Sstevel }
60881708Sstevel
60891708Sstevel return (NULL);
60901708Sstevel }
60911708Sstevel
60921708Sstevel sbd_error_t *
drmach_io_post_attach(drmachid_t id)60931708Sstevel drmach_io_post_attach(drmachid_t id)
60941708Sstevel {
60951708Sstevel int circ;
60961708Sstevel dev_info_t *dip;
60971708Sstevel dev_info_t *pdip;
60981708Sstevel drmach_device_t *dp;
60991708Sstevel drmach_io_inst_t ios;
61001708Sstevel
61011708Sstevel if (!DRMACH_IS_DEVICE_ID(id))
61021708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
61031708Sstevel dp = id;
61041708Sstevel
61051708Sstevel dip = dp->node->n_getdip(dp->node);
61061708Sstevel
61071708Sstevel /*
61081708Sstevel * We held the branch rooted at dip earlier, so at a minimum the
61091708Sstevel * root i.e. dip must be present in the device tree.
61101708Sstevel */
61111708Sstevel ASSERT(dip);
61121708Sstevel
61131708Sstevel if (strcmp(dp->type, DRMACH_DEVTYPE_PCI) == 0) {
61141708Sstevel if (schpc_add_pci(dip)) {
61151708Sstevel DRMACH_PR("schpc_add_pci failed\n");
61161708Sstevel } else {
61171708Sstevel DRMACH_PR("schpc_add_pci succeeded\n");
61181708Sstevel }
61191708Sstevel }
61201708Sstevel
61211708Sstevel /*
61221708Sstevel * Walk device tree to find rio dip for the board
61231708Sstevel * Since we are not interested in iosram instance here,
61241708Sstevel * initialize it to 0, so that the walk terminates as
61251708Sstevel * soon as eri dip is found.
61261708Sstevel */
61271708Sstevel ios.iosram_inst = 0;
61281708Sstevel ios.eri_dip = NULL;
61291708Sstevel ios.bnum = dp->bp->bnum;
61301708Sstevel
61311708Sstevel if (pdip = ddi_get_parent(dip)) {
61321708Sstevel ndi_hold_devi(pdip);
61331708Sstevel ndi_devi_enter(pdip, &circ);
61341708Sstevel }
61351708Sstevel /*
61361708Sstevel * Root node doesn't have to be held in any way.
61371708Sstevel */
613811066Srafael.vanoni@sun.com ddi_walk_devs(dip, drmach_board_find_io_insts, (void *)&ios);
61391708Sstevel if (pdip) {
61401708Sstevel ndi_devi_exit(pdip, circ);
61411708Sstevel ndi_rele_devi(pdip);
61421708Sstevel }
61431708Sstevel
614411311SSurya.Prakki@Sun.COM DRMACH_PR("drmach_io_post_attach: bnum=%d eri=0x%p\n",
614511311SSurya.Prakki@Sun.COM ios.bnum, (void *)ios.eri_dip);
61461708Sstevel
61471708Sstevel if (ios.eri_dip) {
61481708Sstevel int (*func)(dev_info_t *dip);
61491708Sstevel
61501708Sstevel func =
615111066Srafael.vanoni@sun.com (int (*)(dev_info_t *))kobj_getsymvalue("man_dr_attach", 0);
61521708Sstevel
61531708Sstevel if (func) {
61541708Sstevel DRMACH_PR("calling man_dr_attach\n");
61551708Sstevel (void) (*func)(ios.eri_dip);
61561708Sstevel } else {
61571708Sstevel DRMACH_PR("man_dr_attach NOT present\n");
61581708Sstevel }
61591708Sstevel
61601708Sstevel /*
61611708Sstevel * Release hold acquired in drmach_board_find_io_insts()
61621708Sstevel */
61631708Sstevel ndi_rele_devi(ios.eri_dip);
61641708Sstevel
61651708Sstevel }
61661708Sstevel
61671708Sstevel return (NULL);
61681708Sstevel }
61691708Sstevel
61701708Sstevel static sbd_error_t *
drmach_io_status(drmachid_t id,drmach_status_t * stat)61711708Sstevel drmach_io_status(drmachid_t id, drmach_status_t *stat)
61721708Sstevel {
61731708Sstevel drmach_device_t *dp;
61741708Sstevel sbd_error_t *err;
61751708Sstevel int configured;
61761708Sstevel
61771708Sstevel ASSERT(DRMACH_IS_IO_ID(id));
61781708Sstevel dp = id;
61791708Sstevel
61801708Sstevel err = drmach_io_is_attached(id, &configured);
61811708Sstevel if (err)
61821708Sstevel return (err);
61831708Sstevel
61841708Sstevel stat->assigned = dp->bp->assigned;
61851708Sstevel stat->powered = dp->bp->powered;
61861708Sstevel stat->configured = (configured != 0);
61871708Sstevel stat->busy = dp->busy;
618811311SSurya.Prakki@Sun.COM (void) strncpy(stat->type, dp->type, sizeof (stat->type));
61891708Sstevel stat->info[0] = '\0';
61901708Sstevel
61911708Sstevel return (NULL);
61921708Sstevel }
61931708Sstevel
61941708Sstevel sbd_error_t *
drmach_mem_init_size(drmachid_t id)61951708Sstevel drmach_mem_init_size(drmachid_t id)
61961708Sstevel {
61971708Sstevel drmach_mem_t *mp;
61981708Sstevel sbd_error_t *err;
61991708Sstevel gdcd_t *gdcd;
62001708Sstevel mem_chunk_t *chunk;
62011708Sstevel uint64_t chunks, pa, mask, sz;
62021708Sstevel
62031708Sstevel if (!DRMACH_IS_MEM_ID(id))
62041708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
62051708Sstevel mp = id;
62061708Sstevel
62071708Sstevel err = drmach_mem_get_base_physaddr(id, &pa);
62081708Sstevel if (err)
62091708Sstevel return (err);
62101708Sstevel
62111708Sstevel mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
62121708Sstevel pa &= mask;
62131708Sstevel
62141708Sstevel gdcd = drmach_gdcd_new();
62151708Sstevel if (gdcd == NULL)
62161708Sstevel return (DRMACH_INTERNAL_ERROR());
62171708Sstevel
62181708Sstevel sz = 0;
62191708Sstevel chunk = gdcd->dcd_chunk_list.dcl_chunk;
62201708Sstevel chunks = gdcd->dcd_chunk_list.dcl_chunks;
62211708Sstevel while (chunks-- != 0) {
62221708Sstevel if ((chunk->mc_base_pa & mask) == pa) {
62231708Sstevel sz += chunk->mc_mbytes * 1048576;
62241708Sstevel }
62251708Sstevel
62261708Sstevel ++chunk;
62271708Sstevel }
62281708Sstevel mp->nbytes = sz;
62291708Sstevel
62301708Sstevel drmach_gdcd_dispose(gdcd);
62311708Sstevel return (NULL);
62321708Sstevel }
62331708Sstevel
62341708Sstevel /*
62351708Sstevel * Hardware registers are organized into consecutively
62361708Sstevel * addressed registers. The reg property's hi and lo fields
62371708Sstevel * together describe the base address of the register set for
62381708Sstevel * this memory-controller. Register descriptions and offsets
62391708Sstevel * (from the base address) are as follows:
62401708Sstevel *
62411708Sstevel * Description Offset Size (bytes)
62421708Sstevel * Memory Timing Control Register I 0x00 8
62431708Sstevel * Memory Timing Control Register II 0x08 8
62441708Sstevel * Memory Address Decoding Register I 0x10 8
62451708Sstevel * Memory Address Decoding Register II 0x18 8
62461708Sstevel * Memory Address Decoding Register III 0x20 8
62471708Sstevel * Memory Address Decoding Register IV 0x28 8
62481708Sstevel * Memory Address Control Register 0x30 8
62491708Sstevel * Memory Timing Control Register III 0x38 8
62501708Sstevel * Memory Timing Control Register IV 0x40 8
62511708Sstevel * Memory Timing Control Register V 0x48 8 (Jaguar, Panther only)
62521708Sstevel * EMU Activity Status Register 0x50 8 (Panther only)
62531708Sstevel *
62541708Sstevel * Only the Memory Address Decoding Register and EMU Activity Status
62551708Sstevel * Register addresses are needed for DRMACH.
62561708Sstevel */
62571708Sstevel static sbd_error_t *
drmach_mem_new(drmach_device_t * proto,drmachid_t * idp)62581708Sstevel drmach_mem_new(drmach_device_t *proto, drmachid_t *idp)
62591708Sstevel {
62601708Sstevel static void drmach_mem_dispose(drmachid_t);
62611708Sstevel static sbd_error_t *drmach_mem_release(drmachid_t);
62621708Sstevel static sbd_error_t *drmach_mem_status(drmachid_t, drmach_status_t *);
62631708Sstevel
62641708Sstevel sbd_error_t *err;
62651708Sstevel uint64_t madr_pa;
62661708Sstevel drmach_mem_t *mp;
62671708Sstevel int bank, count;
62681708Sstevel
62691708Sstevel err = drmach_read_reg_addr(proto, &madr_pa);
62701708Sstevel if (err)
62711708Sstevel return (err);
62721708Sstevel
62731708Sstevel mp = kmem_zalloc(sizeof (drmach_mem_t), KM_SLEEP);
62741708Sstevel bcopy(proto, &mp->dev, sizeof (mp->dev));
62751708Sstevel mp->dev.node = drmach_node_dup(proto->node);
62761708Sstevel mp->dev.cm.isa = (void *)drmach_mem_new;
62771708Sstevel mp->dev.cm.dispose = drmach_mem_dispose;
62781708Sstevel mp->dev.cm.release = drmach_mem_release;
62791708Sstevel mp->dev.cm.status = drmach_mem_status;
62801708Sstevel mp->madr_pa = madr_pa;
62811708Sstevel
628211311SSurya.Prakki@Sun.COM (void) snprintf(mp->dev.cm.name,
628311311SSurya.Prakki@Sun.COM sizeof (mp->dev.cm.name), "%s", mp->dev.type);
62841708Sstevel
62851708Sstevel for (count = bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
62861708Sstevel uint64_t madr;
62871708Sstevel
62881708Sstevel drmach_mem_read_madr(mp, bank, &madr);
62891708Sstevel if (madr & DRMACH_MC_VALID_MASK) {
62901708Sstevel count += 1;
62911708Sstevel break;
62921708Sstevel }
62931708Sstevel }
62941708Sstevel
62951708Sstevel /*
62961708Sstevel * If none of the banks had their valid bit set, that means
62971708Sstevel * post did not configure this MC to participate in the
62981708Sstevel * domain. So, pretend this node does not exist by returning
62991708Sstevel * a drmachid of zero.
63001708Sstevel */
63011708Sstevel if (count == 0) {
63021708Sstevel /* drmach_mem_dispose frees board mem list */
63031708Sstevel drmach_node_dispose(mp->dev.node);
63041708Sstevel kmem_free(mp, sizeof (*mp));
63051708Sstevel *idp = (drmachid_t)0;
63061708Sstevel return (NULL);
63071708Sstevel }
63081708Sstevel
63091708Sstevel /*
63101708Sstevel * Only one mem unit per board is exposed to the
63111708Sstevel * PIM layer. The first mem unit encountered during
63121708Sstevel * tree walk is used to represent all mem units on
63131708Sstevel * the same board.
63141708Sstevel */
63151708Sstevel if (mp->dev.bp->mem == NULL) {
63161708Sstevel /* start list of mem units on this board */
63171708Sstevel mp->dev.bp->mem = mp;
63181708Sstevel
63191708Sstevel /*
63201708Sstevel * force unum to zero since this is the only mem unit
63211708Sstevel * that will be visible to the PIM layer.
63221708Sstevel */
63231708Sstevel mp->dev.unum = 0;
63241708Sstevel
63251708Sstevel /*
63261708Sstevel * board memory size kept in this mem unit only
63271708Sstevel */
63281708Sstevel err = drmach_mem_init_size(mp);
63291708Sstevel if (err) {
63301708Sstevel mp->dev.bp->mem = NULL;
63311708Sstevel /* drmach_mem_dispose frees board mem list */
63321708Sstevel drmach_node_dispose(mp->dev.node);
63331708Sstevel kmem_free(mp, sizeof (*mp));
63341708Sstevel *idp = (drmachid_t)0;
63351708Sstevel return (NULL);
63361708Sstevel }
63371708Sstevel
63381708Sstevel /*
63391708Sstevel * allow this instance (the first encountered on this board)
63401708Sstevel * to be visible to the PIM layer.
63411708Sstevel */
63421708Sstevel *idp = (drmachid_t)mp;
63431708Sstevel } else {
63441708Sstevel drmach_mem_t *lp;
63451708Sstevel
63461708Sstevel /* hide this mem instance behind the first. */
63471708Sstevel for (lp = mp->dev.bp->mem; lp->next; lp = lp->next)
63481708Sstevel ;
63491708Sstevel lp->next = mp;
63501708Sstevel
63511708Sstevel /*
63521708Sstevel * hide this instance from the caller.
63531708Sstevel * See drmach_board_find_devices_cb() for details.
63541708Sstevel */
63551708Sstevel *idp = (drmachid_t)0;
63561708Sstevel }
63571708Sstevel
63581708Sstevel return (NULL);
63591708Sstevel }
63601708Sstevel
63611708Sstevel static void
drmach_mem_dispose(drmachid_t id)63621708Sstevel drmach_mem_dispose(drmachid_t id)
63631708Sstevel {
63641708Sstevel drmach_mem_t *mp, *next;
63651708Sstevel drmach_board_t *bp;
63661708Sstevel
63671708Sstevel ASSERT(DRMACH_IS_MEM_ID(id));
63681708Sstevel
63691708Sstevel mutex_enter(&drmach_bus_sync_lock);
63701708Sstevel
63711708Sstevel mp = id;
63721708Sstevel bp = mp->dev.bp;
63731708Sstevel
63741708Sstevel do {
63751708Sstevel if (mp->dev.node)
63761708Sstevel drmach_node_dispose(mp->dev.node);
63771708Sstevel
63781708Sstevel next = mp->next;
63791708Sstevel kmem_free(mp, sizeof (*mp));
63801708Sstevel mp = next;
63811708Sstevel } while (mp);
63821708Sstevel
63831708Sstevel bp->mem = NULL;
63841708Sstevel
63851708Sstevel drmach_bus_sync_list_update();
63861708Sstevel mutex_exit(&drmach_bus_sync_lock);
63871708Sstevel }
63881708Sstevel
63891708Sstevel sbd_error_t *
drmach_mem_add_span(drmachid_t id,uint64_t basepa,uint64_t size)63901708Sstevel drmach_mem_add_span(drmachid_t id, uint64_t basepa, uint64_t size)
63911708Sstevel {
63921708Sstevel pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
63931708Sstevel pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
63941708Sstevel int rv;
63951708Sstevel
63961708Sstevel ASSERT(size != 0);
63971708Sstevel
63981708Sstevel if (!DRMACH_IS_MEM_ID(id))
63991708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
64001708Sstevel
64014266Sdp78419 rv = kcage_range_add(basepfn, npages, KCAGE_DOWN);
64021708Sstevel if (rv == ENOMEM) {
64031708Sstevel cmn_err(CE_WARN, "%lu megabytes not available"
640411066Srafael.vanoni@sun.com " to kernel cage", size >> 20);
64051708Sstevel } else if (rv != 0) {
64061708Sstevel /* catch this in debug kernels */
64071708Sstevel ASSERT(0);
64081708Sstevel
64091708Sstevel cmn_err(CE_WARN, "unexpected kcage_range_add"
641011066Srafael.vanoni@sun.com " return value %d", rv);
64111708Sstevel }
64121708Sstevel
64131708Sstevel return (NULL);
64141708Sstevel }
64151708Sstevel
64161708Sstevel sbd_error_t *
drmach_mem_del_span(drmachid_t id,uint64_t basepa,uint64_t size)64171708Sstevel drmach_mem_del_span(drmachid_t id, uint64_t basepa, uint64_t size)
64181708Sstevel {
64191708Sstevel pfn_t basepfn = (pfn_t)(basepa >> PAGESHIFT);
64201708Sstevel pgcnt_t npages = (pgcnt_t)(size >> PAGESHIFT);
64211708Sstevel int rv;
64221708Sstevel
64231708Sstevel if (!DRMACH_IS_MEM_ID(id))
64241708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
64251708Sstevel
64261708Sstevel if (size > 0) {
64271708Sstevel rv = kcage_range_delete_post_mem_del(basepfn, npages);
64281708Sstevel if (rv != 0) {
64291708Sstevel cmn_err(CE_WARN,
64301708Sstevel "unexpected kcage_range_delete_post_mem_del"
64311708Sstevel " return value %d", rv);
64321708Sstevel return (DRMACH_INTERNAL_ERROR());
64331708Sstevel }
64341708Sstevel }
64351708Sstevel
64361708Sstevel return (NULL);
64371708Sstevel }
64381708Sstevel
64391708Sstevel sbd_error_t *
drmach_mem_disable(drmachid_t id)64401708Sstevel drmach_mem_disable(drmachid_t id)
64411708Sstevel {
64421708Sstevel if (!DRMACH_IS_MEM_ID(id))
64431708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
64441708Sstevel else
64451708Sstevel return (NULL);
64461708Sstevel }
64471708Sstevel
64481708Sstevel sbd_error_t *
drmach_mem_enable(drmachid_t id)64491708Sstevel drmach_mem_enable(drmachid_t id)
64501708Sstevel {
64511708Sstevel if (!DRMACH_IS_MEM_ID(id))
64521708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
64531708Sstevel else
64541708Sstevel return (NULL);
64551708Sstevel }
64561708Sstevel
64571708Sstevel sbd_error_t *
drmach_mem_get_alignment(drmachid_t id,uint64_t * mask)64581708Sstevel drmach_mem_get_alignment(drmachid_t id, uint64_t *mask)
64591708Sstevel {
64601708Sstevel #define MB(mb) ((mb) * 1048576ull)
64611708Sstevel
64621708Sstevel static struct {
64631708Sstevel uint_t uk;
64641708Sstevel uint64_t segsz;
64651708Sstevel } uk2segsz[] = {
64661708Sstevel { 0x003, MB(256) },
64671708Sstevel { 0x007, MB(512) },
64681708Sstevel { 0x00f, MB(1024) },
64691708Sstevel { 0x01f, MB(2048) },
64701708Sstevel { 0x03f, MB(4096) },
64711708Sstevel { 0x07f, MB(8192) },
64721708Sstevel { 0x0ff, MB(16384) },
64731708Sstevel { 0x1ff, MB(32768) },
64741708Sstevel { 0x3ff, MB(65536) },
64751708Sstevel { 0x7ff, MB(131072) }
64761708Sstevel };
64771708Sstevel static int len = sizeof (uk2segsz) / sizeof (uk2segsz[0]);
64781708Sstevel
64791708Sstevel #undef MB
64801708Sstevel
64811708Sstevel uint64_t largest_sz = 0;
64821708Sstevel drmach_mem_t *mp;
64831708Sstevel
64841708Sstevel if (!DRMACH_IS_MEM_ID(id))
64851708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
64861708Sstevel
64871708Sstevel /* prime the result with a default value */
64881708Sstevel *mask = (DRMACH_MEM_SLICE_SIZE - 1);
64891708Sstevel
64901708Sstevel for (mp = id; mp; mp = mp->next) {
64911708Sstevel int bank;
64921708Sstevel
64931708Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
64941708Sstevel int i;
64951708Sstevel uint_t uk;
64961708Sstevel uint64_t madr;
64971708Sstevel
64981708Sstevel /* get register value, extract uk and normalize */
64991708Sstevel drmach_mem_read_madr(mp, bank, &madr);
65001708Sstevel
65011708Sstevel if (!(madr & DRMACH_MC_VALID_MASK))
65021708Sstevel continue;
65031708Sstevel
65041708Sstevel uk = DRMACH_MC_UK(madr);
65051708Sstevel
65061708Sstevel /* match uk value */
65071708Sstevel for (i = 0; i < len; i++)
65081708Sstevel if (uk == uk2segsz[i].uk)
65091708Sstevel break;
65101708Sstevel
65111708Sstevel if (i < len) {
65121708Sstevel uint64_t sz = uk2segsz[i].segsz;
65131708Sstevel
65141708Sstevel /*
65151708Sstevel * remember largest segment size,
65161708Sstevel * update mask result
65171708Sstevel */
65181708Sstevel if (sz > largest_sz) {
65191708Sstevel largest_sz = sz;
65201708Sstevel *mask = sz - 1;
65211708Sstevel }
65221708Sstevel } else {
65231708Sstevel /*
65241708Sstevel * uk not in table, punt using
65251708Sstevel * entire slice size. no longer any
65261708Sstevel * reason to check other banks.
65271708Sstevel */
65281708Sstevel *mask = (DRMACH_MEM_SLICE_SIZE - 1);
65291708Sstevel return (NULL);
65301708Sstevel }
65311708Sstevel }
65321708Sstevel }
65331708Sstevel
65341708Sstevel return (NULL);
65351708Sstevel }
65361708Sstevel
65371708Sstevel sbd_error_t *
drmach_mem_get_base_physaddr(drmachid_t id,uint64_t * base_addr)65381708Sstevel drmach_mem_get_base_physaddr(drmachid_t id, uint64_t *base_addr)
65391708Sstevel {
65401708Sstevel drmach_mem_t *mp;
65411708Sstevel
65421708Sstevel if (!DRMACH_IS_MEM_ID(id))
65431708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
65441708Sstevel
65451708Sstevel *base_addr = (uint64_t)-1;
65461708Sstevel for (mp = id; mp; mp = mp->next) {
65471708Sstevel int bank;
65481708Sstevel
65491708Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
65501708Sstevel uint64_t addr, madr;
65511708Sstevel
65521708Sstevel drmach_mem_read_madr(mp, bank, &madr);
65531708Sstevel if (madr & DRMACH_MC_VALID_MASK) {
65541708Sstevel addr = DRMACH_MC_UM_TO_PA(madr) |
655511066Srafael.vanoni@sun.com DRMACH_MC_LM_TO_PA(madr);
65561708Sstevel
65571708Sstevel if (addr < *base_addr)
65581708Sstevel *base_addr = addr;
65591708Sstevel }
65601708Sstevel }
65611708Sstevel }
65621708Sstevel
65631708Sstevel /* should not happen, but ... */
65641708Sstevel if (*base_addr == (uint64_t)-1)
65651708Sstevel return (DRMACH_INTERNAL_ERROR());
65661708Sstevel
65671708Sstevel return (NULL);
65681708Sstevel }
65691708Sstevel
65701708Sstevel void
drmach_bus_sync_list_update(void)65711708Sstevel drmach_bus_sync_list_update(void)
65721708Sstevel {
65731708Sstevel int rv, idx, cnt = 0;
65741708Sstevel drmachid_t id;
65751708Sstevel
65761708Sstevel ASSERT(MUTEX_HELD(&drmach_bus_sync_lock));
65771708Sstevel
65781708Sstevel rv = drmach_array_first(drmach_boards, &idx, &id);
65791708Sstevel while (rv == 0) {
65801708Sstevel drmach_board_t *bp = id;
65811708Sstevel drmach_mem_t *mp = bp->mem;
65821708Sstevel
65831708Sstevel while (mp) {
65841708Sstevel int bank;
65851708Sstevel
65861708Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
65871708Sstevel uint64_t madr;
65881708Sstevel
65891708Sstevel drmach_mem_read_madr(mp, bank, &madr);
65901708Sstevel if (madr & DRMACH_MC_VALID_MASK) {
65911708Sstevel uint64_t pa;
65921708Sstevel
65931708Sstevel pa = DRMACH_MC_UM_TO_PA(madr);
65941708Sstevel pa |= DRMACH_MC_LM_TO_PA(madr);
65951708Sstevel
65961708Sstevel /*
65971708Sstevel * The list is zero terminated.
65981708Sstevel * Offset the pa by a doubleword
65991708Sstevel * to avoid confusing a pa value of
66001708Sstevel * of zero with the terminator.
66011708Sstevel */
66021708Sstevel pa += sizeof (uint64_t);
66031708Sstevel
66041708Sstevel drmach_bus_sync_list[cnt++] = pa;
66051708Sstevel }
66061708Sstevel }
66071708Sstevel
66081708Sstevel mp = mp->next;
66091708Sstevel }
66101708Sstevel
66111708Sstevel rv = drmach_array_next(drmach_boards, &idx, &id);
66121708Sstevel }
66131708Sstevel
66141708Sstevel drmach_bus_sync_list[cnt] = 0;
66151708Sstevel }
66161708Sstevel
66171708Sstevel sbd_error_t *
drmach_mem_get_memlist(drmachid_t id,struct memlist ** ml)66181708Sstevel drmach_mem_get_memlist(drmachid_t id, struct memlist **ml)
66191708Sstevel {
66201708Sstevel sbd_error_t *err;
66211708Sstevel struct memlist *mlist;
66221708Sstevel gdcd_t *gdcd;
66231708Sstevel mem_chunk_t *chunk;
66241708Sstevel uint64_t chunks, pa, mask;
66251708Sstevel
66261708Sstevel err = drmach_mem_get_base_physaddr(id, &pa);
66271708Sstevel if (err)
66281708Sstevel return (err);
66291708Sstevel
66301708Sstevel gdcd = drmach_gdcd_new();
66311708Sstevel if (gdcd == NULL)
66321708Sstevel return (DRMACH_INTERNAL_ERROR());
66331708Sstevel
66341708Sstevel mask = ~ (DRMACH_MEM_SLICE_SIZE - 1);
66351708Sstevel pa &= mask;
66361708Sstevel
66371708Sstevel mlist = NULL;
66381708Sstevel chunk = gdcd->dcd_chunk_list.dcl_chunk;
66391708Sstevel chunks = gdcd->dcd_chunk_list.dcl_chunks;
66401708Sstevel while (chunks-- != 0) {
66411708Sstevel if ((chunk->mc_base_pa & mask) == pa) {
664211066Srafael.vanoni@sun.com mlist = memlist_add_span(mlist, chunk->mc_base_pa,
664311066Srafael.vanoni@sun.com chunk->mc_mbytes * 1048576);
66441708Sstevel }
66451708Sstevel
66461708Sstevel ++chunk;
66471708Sstevel }
66481708Sstevel
66491708Sstevel drmach_gdcd_dispose(gdcd);
66501708Sstevel
66511708Sstevel #ifdef DEBUG
66521708Sstevel DRMACH_PR("GDCD derived memlist:");
66531708Sstevel memlist_dump(mlist);
66541708Sstevel #endif
66551708Sstevel
66561708Sstevel *ml = mlist;
66571708Sstevel return (NULL);
66581708Sstevel }
66591708Sstevel
66601708Sstevel sbd_error_t *
drmach_mem_get_size(drmachid_t id,uint64_t * bytes)66611708Sstevel drmach_mem_get_size(drmachid_t id, uint64_t *bytes)
66621708Sstevel {
66631708Sstevel drmach_mem_t *mp;
66641708Sstevel
66651708Sstevel if (!DRMACH_IS_MEM_ID(id))
66661708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
66671708Sstevel mp = id;
66681708Sstevel
66691708Sstevel ASSERT(mp->nbytes != 0);
66701708Sstevel *bytes = mp->nbytes;
66711708Sstevel
66721708Sstevel return (NULL);
66731708Sstevel }
66741708Sstevel
66751708Sstevel sbd_error_t *
drmach_mem_get_slice_size(drmachid_t id,uint64_t * bytes)66761708Sstevel drmach_mem_get_slice_size(drmachid_t id, uint64_t *bytes)
66771708Sstevel {
66781708Sstevel sbd_error_t *err;
66791708Sstevel drmach_device_t *mp;
66801708Sstevel
66811708Sstevel if (!DRMACH_IS_MEM_ID(id))
66821708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
66831708Sstevel mp = id;
66841708Sstevel
66851708Sstevel switch (DRMACH_BNUM2SLOT(mp->bp->bnum)) {
66861708Sstevel case 0: *bytes = DRMACH_MEM_USABLE_SLICE_SIZE;
66871708Sstevel err = NULL;
66881708Sstevel break;
66891708Sstevel
66901708Sstevel case 1: *bytes = 0;
66911708Sstevel err = NULL;
66921708Sstevel break;
66931708Sstevel
66941708Sstevel default:
66951708Sstevel err = DRMACH_INTERNAL_ERROR();
66961708Sstevel break;
66971708Sstevel }
66981708Sstevel
66991708Sstevel return (err);
67001708Sstevel }
67011708Sstevel
67021708Sstevel processorid_t drmach_mem_cpu_affinity_nail;
67031708Sstevel
67041708Sstevel processorid_t
drmach_mem_cpu_affinity(drmachid_t id)67051708Sstevel drmach_mem_cpu_affinity(drmachid_t id)
67061708Sstevel {
67071708Sstevel drmach_device_t *mp;
67081708Sstevel drmach_board_t *bp;
67091708Sstevel processorid_t cpuid;
67101708Sstevel
67111708Sstevel if (!DRMACH_IS_MEM_ID(id))
67121708Sstevel return (CPU_CURRENT);
67131708Sstevel
67141708Sstevel if (drmach_mem_cpu_affinity_nail) {
67151708Sstevel cpuid = drmach_mem_cpu_affinity_nail;
67161708Sstevel
67171708Sstevel if (cpuid < 0 || cpuid > NCPU)
67181708Sstevel return (CPU_CURRENT);
67191708Sstevel
67201708Sstevel mutex_enter(&cpu_lock);
67211708Sstevel if (cpu[cpuid] == NULL || !CPU_ACTIVE(cpu[cpuid]))
67221708Sstevel cpuid = CPU_CURRENT;
67231708Sstevel mutex_exit(&cpu_lock);
67241708Sstevel
67251708Sstevel return (cpuid);
67261708Sstevel }
67271708Sstevel
67281708Sstevel /* try to choose a proc on the target board */
67291708Sstevel mp = id;
67301708Sstevel bp = mp->bp;
67311708Sstevel if (bp->devices) {
67321708Sstevel int rv;
67331708Sstevel int d_idx;
67341708Sstevel drmachid_t d_id;
67351708Sstevel
67361708Sstevel rv = drmach_array_first(bp->devices, &d_idx, &d_id);
67371708Sstevel while (rv == 0) {
67381708Sstevel if (DRMACH_IS_CPU_ID(d_id)) {
67391708Sstevel drmach_cpu_t *cp = d_id;
67401708Sstevel
67411708Sstevel mutex_enter(&cpu_lock);
67421708Sstevel cpuid = cp->cpuid;
67431708Sstevel if (cpu[cpuid] && CPU_ACTIVE(cpu[cpuid])) {
67441708Sstevel mutex_exit(&cpu_lock);
67451708Sstevel return (cpuid);
67461708Sstevel } else {
67471708Sstevel mutex_exit(&cpu_lock);
67481708Sstevel }
67491708Sstevel }
67501708Sstevel
67511708Sstevel rv = drmach_array_next(bp->devices, &d_idx, &d_id);
67521708Sstevel }
67531708Sstevel }
67541708Sstevel
67551708Sstevel /* otherwise, this proc, wherever it is */
67561708Sstevel return (CPU_CURRENT);
67571708Sstevel }
67581708Sstevel
67591708Sstevel static sbd_error_t *
drmach_mem_release(drmachid_t id)67601708Sstevel drmach_mem_release(drmachid_t id)
67611708Sstevel {
67621708Sstevel if (!DRMACH_IS_MEM_ID(id))
67631708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
67641708Sstevel return (NULL);
67651708Sstevel }
67661708Sstevel
67671708Sstevel static sbd_error_t *
drmach_mem_status(drmachid_t id,drmach_status_t * stat)67681708Sstevel drmach_mem_status(drmachid_t id, drmach_status_t *stat)
67691708Sstevel {
67701708Sstevel drmach_mem_t *mp;
67711708Sstevel sbd_error_t *err;
67721708Sstevel uint64_t pa, slice_size;
67731708Sstevel struct memlist *ml;
67741708Sstevel
67751708Sstevel ASSERT(DRMACH_IS_MEM_ID(id));
67761708Sstevel mp = id;
67771708Sstevel
67781708Sstevel /* get starting physical address of target memory */
67791708Sstevel err = drmach_mem_get_base_physaddr(id, &pa);
67801708Sstevel if (err)
67811708Sstevel return (err);
67821708Sstevel
67831708Sstevel /* round down to slice boundary */
67841708Sstevel slice_size = DRMACH_MEM_SLICE_SIZE;
67851708Sstevel pa &= ~ (slice_size - 1);
67861708Sstevel
67871708Sstevel /* stop at first span that is in slice */
67881708Sstevel memlist_read_lock();
6789*11474SJonathan.Adams@Sun.COM for (ml = phys_install; ml; ml = ml->ml_next)
6790*11474SJonathan.Adams@Sun.COM if (ml->ml_address >= pa && ml->ml_address < pa + slice_size)
67911708Sstevel break;
67921708Sstevel memlist_read_unlock();
67931708Sstevel
67941708Sstevel stat->assigned = mp->dev.bp->assigned;
67951708Sstevel stat->powered = mp->dev.bp->powered;
67961708Sstevel stat->configured = (ml != NULL);
67971708Sstevel stat->busy = mp->dev.busy;
679811311SSurya.Prakki@Sun.COM (void) strncpy(stat->type, mp->dev.type, sizeof (stat->type));
67991708Sstevel stat->info[0] = '\0';
68001708Sstevel
68011708Sstevel return (NULL);
68021708Sstevel }
68031708Sstevel
68041708Sstevel sbd_error_t *
drmach_board_deprobe(drmachid_t id)68051708Sstevel drmach_board_deprobe(drmachid_t id)
68061708Sstevel {
68071708Sstevel drmach_board_t *bp;
68081708Sstevel sbd_error_t *err = NULL;
68091708Sstevel
68101708Sstevel if (!DRMACH_IS_BOARD_ID(id))
68111708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
68121708Sstevel bp = id;
68131708Sstevel
68141708Sstevel if (bp->tree) {
68151708Sstevel drmach_node_dispose(bp->tree);
68161708Sstevel bp->tree = NULL;
68171708Sstevel }
68181708Sstevel if (bp->devices) {
68191708Sstevel drmach_array_dispose(bp->devices, drmach_device_dispose);
68201708Sstevel bp->devices = NULL;
68211708Sstevel bp->mem = NULL; /* TODO: still needed? */
68221708Sstevel }
68231708Sstevel return (err);
68241708Sstevel }
68251708Sstevel
68261708Sstevel /*ARGSUSED1*/
68271708Sstevel static sbd_error_t *
drmach_pt_showlpa(drmachid_t id,drmach_opts_t * opts)68281708Sstevel drmach_pt_showlpa(drmachid_t id, drmach_opts_t *opts)
68291708Sstevel {
68301708Sstevel drmach_device_t *dp;
68311708Sstevel uint64_t val;
68321708Sstevel int err = 1;
68331708Sstevel
68341708Sstevel if (DRMACH_IS_CPU_ID(id)) {
68351708Sstevel drmach_cpu_t *cp = id;
68361708Sstevel if (drmach_cpu_read_scr(cp, &val))
68371708Sstevel err = 0;
68381708Sstevel } else if (DRMACH_IS_IO_ID(id) && ((drmach_io_t *)id)->scsr_pa != 0) {
68391708Sstevel drmach_io_t *io = id;
68401708Sstevel val = lddphysio(io->scsr_pa);
68411708Sstevel err = 0;
68421708Sstevel }
68431708Sstevel if (err)
68441708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
68451708Sstevel
68461708Sstevel dp = id;
68471708Sstevel uprintf("showlpa %s::%s portid %d, base pa %lx, bound pa %lx\n",
684811066Srafael.vanoni@sun.com dp->bp->cm.name,
684911066Srafael.vanoni@sun.com dp->cm.name,
685011066Srafael.vanoni@sun.com dp->portid,
685111311SSurya.Prakki@Sun.COM (long)(DRMACH_LPA_BASE_TO_PA(val)),
685211311SSurya.Prakki@Sun.COM (long)(DRMACH_LPA_BND_TO_PA(val)));
68531708Sstevel
68541708Sstevel return (NULL);
68551708Sstevel }
68561708Sstevel
68571708Sstevel /*ARGSUSED*/
68581708Sstevel static sbd_error_t *
drmach_pt_ikprobe(drmachid_t id,drmach_opts_t * opts)68591708Sstevel drmach_pt_ikprobe(drmachid_t id, drmach_opts_t *opts)
68601708Sstevel {
68611708Sstevel drmach_board_t *bp = (drmach_board_t *)id;
68621708Sstevel sbd_error_t *err;
68631708Sstevel sc_gptwocfg_cookie_t scc;
68641708Sstevel
68651708Sstevel if (!DRMACH_IS_BOARD_ID(id))
68661708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
68671708Sstevel
68681708Sstevel /* do saf configurator stuff */
68691708Sstevel DRMACH_PR("calling sc_probe_board for bnum=%d\n", bp->bnum);
68701708Sstevel scc = sc_probe_board(bp->bnum);
68711708Sstevel if (scc == NULL) {
68721708Sstevel err = drerr_new(0, ESTC_PROBE, bp->cm.name);
68731708Sstevel return (err);
68741708Sstevel }
68751708Sstevel
68761708Sstevel return (err);
68771708Sstevel }
68781708Sstevel
68791708Sstevel /*ARGSUSED*/
68801708Sstevel static sbd_error_t *
drmach_pt_ikdeprobe(drmachid_t id,drmach_opts_t * opts)68811708Sstevel drmach_pt_ikdeprobe(drmachid_t id, drmach_opts_t *opts)
68821708Sstevel {
68831708Sstevel drmach_board_t *bp;
68841708Sstevel sbd_error_t *err = NULL;
68851708Sstevel sc_gptwocfg_cookie_t scc;
68861708Sstevel
68871708Sstevel if (!DRMACH_IS_BOARD_ID(id))
68881708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
68891708Sstevel bp = id;
68901708Sstevel
68911708Sstevel cmn_err(CE_CONT, "DR: in-kernel unprobe board %d\n", bp->bnum);
68921708Sstevel scc = sc_unprobe_board(bp->bnum);
68931708Sstevel if (scc != NULL) {
68941708Sstevel err = drerr_new(0, ESTC_DEPROBE, bp->cm.name);
68951708Sstevel }
68961708Sstevel
68971708Sstevel if (err == NULL)
68981708Sstevel err = drmach_board_deprobe(id);
68991708Sstevel
69001708Sstevel return (err);
69011708Sstevel }
69021708Sstevel
69031708Sstevel static sbd_error_t *
drmach_pt_readmem(drmachid_t id,drmach_opts_t * opts)69041708Sstevel drmach_pt_readmem(drmachid_t id, drmach_opts_t *opts)
69051708Sstevel {
69061708Sstevel _NOTE(ARGUNUSED(id))
69071708Sstevel _NOTE(ARGUNUSED(opts))
69081708Sstevel
69091708Sstevel struct memlist *ml;
69101708Sstevel uint64_t src_pa;
69111708Sstevel uint64_t dst_pa;
69121708Sstevel uint64_t dst;
69131708Sstevel
69141708Sstevel dst_pa = va_to_pa(&dst);
69151708Sstevel
69161708Sstevel memlist_read_lock();
6917*11474SJonathan.Adams@Sun.COM for (ml = phys_install; ml; ml = ml->ml_next) {
69181708Sstevel uint64_t nbytes;
69191708Sstevel
6920*11474SJonathan.Adams@Sun.COM src_pa = ml->ml_address;
6921*11474SJonathan.Adams@Sun.COM nbytes = ml->ml_size;
69221708Sstevel
69231708Sstevel while (nbytes != 0ull) {
69241708Sstevel
69251708Sstevel /* copy 32 bytes at src_pa to dst_pa */
69261708Sstevel bcopy32_il(src_pa, dst_pa);
69271708Sstevel
69281708Sstevel /* increment by 32 bytes */
69291708Sstevel src_pa += (4 * sizeof (uint64_t));
69301708Sstevel
69311708Sstevel /* decrement by 32 bytes */
69321708Sstevel nbytes -= (4 * sizeof (uint64_t));
69331708Sstevel }
69341708Sstevel }
69351708Sstevel memlist_read_unlock();
69361708Sstevel
69371708Sstevel return (NULL);
69381708Sstevel }
69391708Sstevel
69401708Sstevel static sbd_error_t *
drmach_pt_recovercpu(drmachid_t id,drmach_opts_t * opts)69411708Sstevel drmach_pt_recovercpu(drmachid_t id, drmach_opts_t *opts)
69421708Sstevel {
69431708Sstevel _NOTE(ARGUNUSED(opts))
69441708Sstevel
69451708Sstevel drmach_cpu_t *cp;
69461708Sstevel
69471708Sstevel if (!DRMACH_IS_CPU_ID(id))
69481708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
69491708Sstevel cp = id;
69501708Sstevel
69511708Sstevel mutex_enter(&cpu_lock);
69521708Sstevel (void) drmach_iocage_cpu_return(&(cp->dev),
69531708Sstevel CPU_ENABLE | CPU_EXISTS | CPU_READY | CPU_RUNNING);
69541708Sstevel mutex_exit(&cpu_lock);
69551708Sstevel
69561708Sstevel return (NULL);
69571708Sstevel }
69581708Sstevel
69591708Sstevel /*
69601708Sstevel * Starcat DR passthrus are for debugging purposes only.
69611708Sstevel */
69621708Sstevel static struct {
69631708Sstevel const char *name;
69641708Sstevel sbd_error_t *(*handler)(drmachid_t id, drmach_opts_t *opts);
69651708Sstevel } drmach_pt_arr[] = {
69661708Sstevel { "showlpa", drmach_pt_showlpa },
69671708Sstevel { "ikprobe", drmach_pt_ikprobe },
69681708Sstevel { "ikdeprobe", drmach_pt_ikdeprobe },
69691708Sstevel { "readmem", drmach_pt_readmem },
69701708Sstevel { "recovercpu", drmach_pt_recovercpu },
69711708Sstevel
69721708Sstevel /* the following line must always be last */
69731708Sstevel { NULL, NULL }
69741708Sstevel };
69751708Sstevel
69761708Sstevel /*ARGSUSED*/
69771708Sstevel sbd_error_t *
drmach_passthru(drmachid_t id,drmach_opts_t * opts)69781708Sstevel drmach_passthru(drmachid_t id, drmach_opts_t *opts)
69791708Sstevel {
69801708Sstevel int i;
69811708Sstevel sbd_error_t *err;
69821708Sstevel
69831708Sstevel i = 0;
69841708Sstevel while (drmach_pt_arr[i].name != NULL) {
69851708Sstevel int len = strlen(drmach_pt_arr[i].name);
69861708Sstevel
69871708Sstevel if (strncmp(drmach_pt_arr[i].name, opts->copts, len) == 0)
69881708Sstevel break;
69891708Sstevel
69901708Sstevel i += 1;
69911708Sstevel }
69921708Sstevel
69931708Sstevel if (drmach_pt_arr[i].name == NULL)
69941708Sstevel err = drerr_new(0, ESTC_UNKPTCMD, opts->copts);
69951708Sstevel else
69961708Sstevel err = (*drmach_pt_arr[i].handler)(id, opts);
69971708Sstevel
69981708Sstevel return (err);
69991708Sstevel }
70001708Sstevel
70011708Sstevel sbd_error_t *
drmach_release(drmachid_t id)70021708Sstevel drmach_release(drmachid_t id)
70031708Sstevel {
70041708Sstevel drmach_common_t *cp;
70051708Sstevel
70061708Sstevel if (!DRMACH_IS_DEVICE_ID(id))
70071708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
70081708Sstevel cp = id;
70091708Sstevel
70101708Sstevel return (cp->release(id));
70111708Sstevel }
70121708Sstevel
70131708Sstevel sbd_error_t *
drmach_status(drmachid_t id,drmach_status_t * stat)70141708Sstevel drmach_status(drmachid_t id, drmach_status_t *stat)
70151708Sstevel {
70161708Sstevel drmach_common_t *cp;
70171708Sstevel sbd_error_t *err;
70181708Sstevel
70191708Sstevel rw_enter(&drmach_boards_rwlock, RW_READER);
70201708Sstevel
70211708Sstevel if (!DRMACH_IS_ID(id)) {
70221708Sstevel rw_exit(&drmach_boards_rwlock);
70231708Sstevel return (drerr_new(0, ESTC_NOTID, NULL));
70241708Sstevel }
70251708Sstevel
70261708Sstevel cp = id;
70271708Sstevel
70281708Sstevel err = cp->status(id, stat);
70291708Sstevel rw_exit(&drmach_boards_rwlock);
70301708Sstevel return (err);
70311708Sstevel }
70321708Sstevel
70331708Sstevel static sbd_error_t *
drmach_i_status(drmachid_t id,drmach_status_t * stat)70341708Sstevel drmach_i_status(drmachid_t id, drmach_status_t *stat)
70351708Sstevel {
70361708Sstevel drmach_common_t *cp;
70371708Sstevel
70381708Sstevel if (!DRMACH_IS_ID(id))
70391708Sstevel return (drerr_new(0, ESTC_NOTID, NULL));
70401708Sstevel cp = id;
70411708Sstevel
70421708Sstevel return (cp->status(id, stat));
70431708Sstevel }
70441708Sstevel
70451708Sstevel /*ARGSUSED*/
70461708Sstevel sbd_error_t *
drmach_unconfigure(drmachid_t id,int flags)70471708Sstevel drmach_unconfigure(drmachid_t id, int flags)
70481708Sstevel {
70491708Sstevel drmach_device_t *dp;
70501708Sstevel dev_info_t *rdip;
70511708Sstevel
70521708Sstevel char name[OBP_MAXDRVNAME];
70531708Sstevel int rv;
70541708Sstevel
70551708Sstevel /*
70561708Sstevel * Since CPU nodes are not configured, it is
70571708Sstevel * necessary to skip the unconfigure step as
70581708Sstevel * well.
70591708Sstevel */
70601708Sstevel if (DRMACH_IS_CPU_ID(id)) {
70611708Sstevel return (NULL);
70621708Sstevel }
70631708Sstevel
70641708Sstevel for (; id; ) {
70651708Sstevel dev_info_t *fdip = NULL;
70661708Sstevel
70671708Sstevel if (!DRMACH_IS_DEVICE_ID(id))
70681708Sstevel return (drerr_new(0, ESTC_INAPPROP, NULL));
70691708Sstevel dp = id;
70701708Sstevel
70711708Sstevel rdip = dp->node->n_getdip(dp->node);
70721708Sstevel
70731708Sstevel /*
70741708Sstevel * drmach_unconfigure() is always called on a configured branch.
70751708Sstevel * So the root of the branch was held earlier and must exist.
70761708Sstevel */
70771708Sstevel ASSERT(rdip);
70781708Sstevel
70791708Sstevel DRMACH_PR("drmach_unconfigure: unconfiguring DDI branch");
70801708Sstevel
70811708Sstevel rv = dp->node->n_getprop(dp->node,
70821708Sstevel "name", name, OBP_MAXDRVNAME);
70831708Sstevel
70841708Sstevel /* The node must have a name */
70851708Sstevel if (rv)
70861708Sstevel return (0);
70871708Sstevel
70881708Sstevel if (drmach_name2type_idx(name) < 0) {
70891708Sstevel if (DRMACH_IS_MEM_ID(id)) {
70901708Sstevel drmach_mem_t *mp = id;
70911708Sstevel id = mp->next;
70921708Sstevel } else {
70931708Sstevel id = NULL;
70941708Sstevel }
70951708Sstevel continue;
70961708Sstevel }
70971708Sstevel
70981708Sstevel /*
70991708Sstevel * NOTE: FORCE flag is no longer needed under devfs
71001708Sstevel */
71011708Sstevel ASSERT(e_ddi_branch_held(rdip));
71021708Sstevel if (e_ddi_branch_unconfigure(rdip, &fdip, 0) != 0) {
710311066Srafael.vanoni@sun.com sbd_error_t *err = NULL;
710411066Srafael.vanoni@sun.com char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
71051708Sstevel
71061708Sstevel /*
71071708Sstevel * If non-NULL, fdip is returned held and must be
71081708Sstevel * released.
71091708Sstevel */
71101708Sstevel if (fdip != NULL) {
71111708Sstevel (void) ddi_pathname(fdip, path);
71121708Sstevel ddi_release_devi(fdip);
71131708Sstevel } else {
71141708Sstevel (void) ddi_pathname(rdip, path);
71151708Sstevel }
71161708Sstevel
71171708Sstevel err = drerr_new(1, ESTC_DRVFAIL, path);
71181708Sstevel
71191708Sstevel kmem_free(path, MAXPATHLEN);
71201708Sstevel
71211708Sstevel /*
71221708Sstevel * If we were unconfiguring an IO board, a call was
71231708Sstevel * made to man_dr_detach. We now need to call
71241708Sstevel * man_dr_attach to regain man use of the eri.
71251708Sstevel */
71261708Sstevel if (DRMACH_IS_IO_ID(id)) {
71271708Sstevel int (*func)(dev_info_t *dip);
71281708Sstevel
71291708Sstevel func = (int (*)(dev_info_t *))kobj_getsymvalue\
713011066Srafael.vanoni@sun.com ("man_dr_attach", 0);
71311708Sstevel
71321708Sstevel if (func) {
71331708Sstevel drmach_io_inst_t ios;
71341708Sstevel dev_info_t *pdip;
71351708Sstevel int circ;
71361708Sstevel
71371708Sstevel /*
71381708Sstevel * Walk device tree to find rio dip for
71391708Sstevel * the board
71401708Sstevel * Since we are not interested in iosram
71411708Sstevel * instance here, initialize it to 0, so
71421708Sstevel * that the walk terminates as soon as
71431708Sstevel * eri dip is found.
71441708Sstevel */
71451708Sstevel ios.iosram_inst = 0;
71461708Sstevel ios.eri_dip = NULL;
71471708Sstevel ios.bnum = dp->bp->bnum;
71481708Sstevel
71491708Sstevel if (pdip = ddi_get_parent(rdip)) {
71501708Sstevel ndi_hold_devi(pdip);
71511708Sstevel ndi_devi_enter(pdip, &circ);
71521708Sstevel }
71531708Sstevel /*
71541708Sstevel * Root node doesn't have to be held in
71551708Sstevel * any way.
71561708Sstevel */
71571708Sstevel ASSERT(e_ddi_branch_held(rdip));
71581708Sstevel ddi_walk_devs(rdip,
715911066Srafael.vanoni@sun.com drmach_board_find_io_insts,
716011066Srafael.vanoni@sun.com (void *)&ios);
71611708Sstevel
71621708Sstevel DRMACH_PR("drmach_unconfigure: bnum=%d"
716311066Srafael.vanoni@sun.com " eri=0x%p\n",
716411311SSurya.Prakki@Sun.COM ios.bnum, (void *)ios.eri_dip);
71651708Sstevel
71661708Sstevel if (pdip) {
71671708Sstevel ndi_devi_exit(pdip, circ);
71681708Sstevel ndi_rele_devi(pdip);
71691708Sstevel }
71701708Sstevel
71711708Sstevel if (ios.eri_dip) {
71721708Sstevel DRMACH_PR("calling"
717311066Srafael.vanoni@sun.com " man_dr_attach\n");
71741708Sstevel (void) (*func)(ios.eri_dip);
71751708Sstevel /*
71761708Sstevel * Release hold acquired in
71771708Sstevel * drmach_board_find_io_insts()
71781708Sstevel */
71791708Sstevel ndi_rele_devi(ios.eri_dip);
71801708Sstevel }
71811708Sstevel }
71821708Sstevel }
71831708Sstevel return (err);
71841708Sstevel }
71851708Sstevel
71861708Sstevel if (DRMACH_IS_MEM_ID(id)) {
71871708Sstevel drmach_mem_t *mp = id;
71881708Sstevel id = mp->next;
71891708Sstevel } else {
71901708Sstevel id = NULL;
71911708Sstevel }
71921708Sstevel }
71931708Sstevel
71941708Sstevel return (NULL);
71951708Sstevel }
71961708Sstevel
71971708Sstevel /*
71981708Sstevel * drmach interfaces to legacy Starfire platmod logic
71991708Sstevel * linkage via runtime symbol look up, called from plat_cpu_power*
72001708Sstevel */
72011708Sstevel
72021708Sstevel /*
72031708Sstevel * Start up a cpu. It is possible that we're attempting to restart
72041708Sstevel * the cpu after an UNCONFIGURE in which case the cpu will be
72051708Sstevel * spinning in its cache. So, all we have to do is wakeup him up.
72061708Sstevel * Under normal circumstances the cpu will be coming from a previous
72071708Sstevel * CONNECT and thus will be spinning in OBP. In both cases, the
72081708Sstevel * startup sequence is the same.
72091708Sstevel */
72101708Sstevel int
drmach_cpu_poweron(struct cpu * cp)72111708Sstevel drmach_cpu_poweron(struct cpu *cp)
72121708Sstevel {
72131708Sstevel DRMACH_PR("drmach_cpu_poweron: starting cpuid %d\n", cp->cpu_id);
72141708Sstevel
72151708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
72161708Sstevel
72171708Sstevel if (drmach_cpu_start(cp) != 0)
72181708Sstevel return (EBUSY);
72191708Sstevel else
72201708Sstevel return (0);
72211708Sstevel }
72221708Sstevel
72231708Sstevel int
drmach_cpu_poweroff(struct cpu * cp)72241708Sstevel drmach_cpu_poweroff(struct cpu *cp)
72251708Sstevel {
72261708Sstevel int ntries;
72271708Sstevel processorid_t cpuid;
72281708Sstevel void drmach_cpu_shutdown_self(void);
72291708Sstevel
72301708Sstevel DRMACH_PR("drmach_cpu_poweroff: stopping cpuid %d\n", cp->cpu_id);
72311708Sstevel
72321708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
72331708Sstevel
72341708Sstevel /*
72351708Sstevel * XXX CHEETAH SUPPORT
72361708Sstevel * for cheetah, we need to grab the iocage lock since iocage
72371708Sstevel * memory is used for e$ flush.
72381708Sstevel */
72391708Sstevel if (drmach_is_cheetah) {
72401708Sstevel mutex_enter(&drmach_iocage_lock);
72411708Sstevel while (drmach_iocage_is_busy)
72421708Sstevel cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
72431708Sstevel drmach_iocage_is_busy = 1;
72441708Sstevel drmach_iocage_mem_scrub(ecache_size * 2);
72451708Sstevel mutex_exit(&drmach_iocage_lock);
72461708Sstevel }
72471708Sstevel
72481708Sstevel cpuid = cp->cpu_id;
72491708Sstevel
72501708Sstevel /*
72511708Sstevel * Set affinity to ensure consistent reading and writing of
72521708Sstevel * drmach_xt_mb[cpuid] by one "master" CPU directing
72531708Sstevel * the shutdown of the target CPU.
72541708Sstevel */
72551708Sstevel affinity_set(CPU->cpu_id);
72561708Sstevel
72571708Sstevel /*
72581708Sstevel * Capture all CPUs (except for detaching proc) to prevent
72591708Sstevel * crosscalls to the detaching proc until it has cleared its
72601708Sstevel * bit in cpu_ready_set.
72611708Sstevel *
72621708Sstevel * The CPUs remain paused and the prom_mutex is known to be free.
72631708Sstevel * This prevents blocking when doing prom IEEE-1275 calls at a
72641708Sstevel * high PIL level.
72651708Sstevel */
72661708Sstevel promsafe_pause_cpus();
72671708Sstevel
72681708Sstevel /*
72691708Sstevel * Quiesce interrupts on the target CPU. We do this by setting
72701708Sstevel * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
72711708Sstevel * prevent it from receiving cross calls and cross traps.
72721708Sstevel * This prevents the processor from receiving any new soft interrupts.
72731708Sstevel */
72741708Sstevel mp_cpu_quiesce(cp);
72751708Sstevel
727611311SSurya.Prakki@Sun.COM (void) prom_hotremovecpu(cpuid);
72771708Sstevel
72781708Sstevel start_cpus();
72791708Sstevel
72801708Sstevel /* setup xt_mb, will be cleared by drmach_shutdown_asm when ready */
72811708Sstevel drmach_xt_mb[cpuid] = 0x80;
72821708Sstevel
72831708Sstevel xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
728411066Srafael.vanoni@sun.com (uint64_t)drmach_cpu_shutdown_self, NULL);
72851708Sstevel
72861708Sstevel ntries = drmach_cpu_ntries;
72871708Sstevel while (drmach_xt_mb[cpuid] && ntries) {
72881708Sstevel DELAY(drmach_cpu_delay);
72891708Sstevel ntries--;
72901708Sstevel }
72911708Sstevel
72921708Sstevel drmach_xt_mb[cpuid] = 0; /* steal the cache line back */
72931708Sstevel
72941708Sstevel membar_sync(); /* make sure copy-back retires */
72951708Sstevel
72961708Sstevel affinity_clear();
72971708Sstevel
72981708Sstevel /*
72991708Sstevel * XXX CHEETAH SUPPORT
73001708Sstevel */
73011708Sstevel if (drmach_is_cheetah) {
73021708Sstevel mutex_enter(&drmach_iocage_lock);
73031708Sstevel drmach_iocage_mem_scrub(ecache_size * 2);
73041708Sstevel drmach_iocage_is_busy = 0;
73051708Sstevel cv_signal(&drmach_iocage_cv);
73061708Sstevel mutex_exit(&drmach_iocage_lock);
73071708Sstevel }
73081708Sstevel
73091708Sstevel DRMACH_PR("waited %d out of %d tries for "
731011066Srafael.vanoni@sun.com "drmach_cpu_shutdown_self on cpu%d",
731111066Srafael.vanoni@sun.com drmach_cpu_ntries - ntries, drmach_cpu_ntries, cp->cpu_id);
73121708Sstevel
73131708Sstevel /*
73141708Sstevel * Do this here instead of drmach_cpu_shutdown_self() to
73151708Sstevel * avoid an assertion failure panic in turnstile.c.
73161708Sstevel */
73171708Sstevel CPU_SIGNATURE(OS_SIG, SIGST_DETACHED, SIGSUBST_NULL, cpuid);
73181708Sstevel
73191708Sstevel return (0);
73201708Sstevel }
73211708Sstevel
73221708Sstevel void
drmach_iocage_mem_scrub(uint64_t nbytes)73231708Sstevel drmach_iocage_mem_scrub(uint64_t nbytes)
73241708Sstevel {
732511311SSurya.Prakki@Sun.COM extern uint32_t drmach_bc_bzero(void*, size_t);
732611311SSurya.Prakki@Sun.COM uint32_t rv;
73271708Sstevel
73281708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
73291708Sstevel
73301708Sstevel affinity_set(CPU->cpu_id);
73311708Sstevel
73321708Sstevel rv = drmach_bc_bzero(drmach_iocage_vaddr, nbytes);
73331708Sstevel if (rv != 0) {
73341708Sstevel DRMACH_PR(
73351708Sstevel "iocage scrub failed, drmach_bc_bzero returned %d\n", rv);
733611066Srafael.vanoni@sun.com rv = drmach_bc_bzero(drmach_iocage_vaddr, drmach_iocage_size);
73371708Sstevel if (rv != 0)
73381708Sstevel cmn_err(CE_PANIC,
73391708Sstevel "iocage scrub failed, drmach_bc_bzero rv=%d\n",
73401708Sstevel rv);
73411708Sstevel }
73421708Sstevel
73431708Sstevel cpu_flush_ecache();
73441708Sstevel
73451708Sstevel affinity_clear();
73461708Sstevel }
73471708Sstevel
73481708Sstevel #define ALIGN(x, a) ((a) == 0 ? (uintptr_t)(x) : \
73491708Sstevel (((uintptr_t)(x) + (uintptr_t)(a) - 1l) & ~((uintptr_t)(a) - 1l)))
73501708Sstevel
73511708Sstevel static sbd_error_t *
drmach_iocage_mem_get(dr_testboard_req_t * tbrq)73521708Sstevel drmach_iocage_mem_get(dr_testboard_req_t *tbrq)
73531708Sstevel {
73541708Sstevel pfn_t basepfn;
73551708Sstevel pgcnt_t npages;
73561708Sstevel extern int memscrub_delete_span(pfn_t, pgcnt_t);
73571708Sstevel uint64_t drmach_iocage_paddr_mbytes;
73581708Sstevel
73591708Sstevel ASSERT(drmach_iocage_paddr != -1);
73601708Sstevel
73611708Sstevel basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
73621708Sstevel npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
73631708Sstevel
736411311SSurya.Prakki@Sun.COM (void) memscrub_delete_span(basepfn, npages);
73651708Sstevel
73661708Sstevel mutex_enter(&cpu_lock);
73671708Sstevel drmach_iocage_mem_scrub(drmach_iocage_size);
73681708Sstevel mutex_exit(&cpu_lock);
73691708Sstevel
73701708Sstevel /*
73711708Sstevel * HPOST wants the address of the cage to be 64 megabyte-aligned
73721708Sstevel * and in megabyte units.
73731708Sstevel * The size of the cage is also in megabyte units.
73741708Sstevel */
73751708Sstevel ASSERT(drmach_iocage_paddr == ALIGN(drmach_iocage_paddr, 0x4000000));
73761708Sstevel
73771708Sstevel drmach_iocage_paddr_mbytes = drmach_iocage_paddr / 0x100000;
73781708Sstevel
73791708Sstevel tbrq->memaddrhi = (uint32_t)(drmach_iocage_paddr_mbytes >> 32);
73801708Sstevel tbrq->memaddrlo = (uint32_t)drmach_iocage_paddr_mbytes;
73811708Sstevel tbrq->memlen = drmach_iocage_size / 0x100000;
73821708Sstevel
73831708Sstevel DRMACH_PR("drmach_iocage_mem_get: hi: 0x%x", tbrq->memaddrhi);
73841708Sstevel DRMACH_PR("drmach_iocage_mem_get: lo: 0x%x", tbrq->memaddrlo);
73851708Sstevel DRMACH_PR("drmach_iocage_mem_get: size: 0x%x", tbrq->memlen);
73861708Sstevel
73871708Sstevel return (NULL);
73881708Sstevel }
73891708Sstevel
73901708Sstevel static sbd_error_t *
drmach_iocage_mem_return(dr_testboard_reply_t * tbr)73911708Sstevel drmach_iocage_mem_return(dr_testboard_reply_t *tbr)
73921708Sstevel {
73931708Sstevel _NOTE(ARGUNUSED(tbr))
73941708Sstevel
73951708Sstevel pfn_t basepfn;
73961708Sstevel pgcnt_t npages;
73971708Sstevel extern int memscrub_add_span(pfn_t, pgcnt_t);
73981708Sstevel
73991708Sstevel ASSERT(drmach_iocage_paddr != -1);
74001708Sstevel
74011708Sstevel basepfn = (pfn_t)(drmach_iocage_paddr >> PAGESHIFT);
74021708Sstevel npages = (pgcnt_t)(drmach_iocage_size >> PAGESHIFT);
74031708Sstevel
740411311SSurya.Prakki@Sun.COM (void) memscrub_add_span(basepfn, npages);
74051708Sstevel
74061708Sstevel mutex_enter(&cpu_lock);
74071708Sstevel mutex_enter(&drmach_iocage_lock);
74081708Sstevel drmach_iocage_mem_scrub(drmach_iocage_size);
74091708Sstevel drmach_iocage_is_busy = 0;
74101708Sstevel cv_signal(&drmach_iocage_cv);
74111708Sstevel mutex_exit(&drmach_iocage_lock);
74121708Sstevel mutex_exit(&cpu_lock);
74131708Sstevel
74141708Sstevel return (NULL);
74151708Sstevel }
74161708Sstevel
74171708Sstevel static int
drmach_cpu_intr_disable(cpu_t * cp)74181708Sstevel drmach_cpu_intr_disable(cpu_t *cp)
74191708Sstevel {
74201708Sstevel if (cpu_intr_disable(cp) != 0)
74211708Sstevel return (-1);
74221708Sstevel return (0);
74231708Sstevel }
74241708Sstevel
74251708Sstevel static int
drmach_iocage_cpu_acquire(drmach_device_t * dp,cpu_flag_t * oflags)74261708Sstevel drmach_iocage_cpu_acquire(drmach_device_t *dp, cpu_flag_t *oflags)
74271708Sstevel {
74281708Sstevel struct cpu *cp;
74291708Sstevel processorid_t cpuid;
74301708Sstevel static char *fn = "drmach_iocage_cpu_acquire";
74311708Sstevel sbd_error_t *err;
74321708Sstevel int impl;
74331708Sstevel
74341708Sstevel ASSERT(DRMACH_IS_CPU_ID(dp));
74351708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
74361708Sstevel
74371708Sstevel cpuid = ((drmach_cpu_t *)dp)->cpuid;
74381708Sstevel
74391708Sstevel DRMACH_PR("%s: attempting to acquire CPU id %d", fn, cpuid);
74401708Sstevel
74411708Sstevel if (dp->busy)
74421708Sstevel return (-1);
74431708Sstevel
74441708Sstevel if ((cp = cpu_get(cpuid)) == NULL) {
74451708Sstevel DRMACH_PR("%s: cpu_get(%d) returned NULL", fn, cpuid);
74461708Sstevel return (-1);
74471708Sstevel }
74481708Sstevel
74491708Sstevel if (!CPU_ACTIVE(cp)) {
74501708Sstevel DRMACH_PR("%s: skipping offlined CPU id %d", fn, cpuid);
74511708Sstevel return (-1);
74521708Sstevel }
74531708Sstevel
74541708Sstevel /*
74551708Sstevel * There is a known HW bug where a Jaguar CPU in Safari port 0 (SBX/P0)
74561708Sstevel * can fail to receive an XIR. To workaround this issue until a hardware
74571708Sstevel * fix is implemented, we will exclude the selection of these CPUs.
74581708Sstevel *
74591708Sstevel * Once a fix is implemented in hardware, this code should be updated
74601708Sstevel * to allow Jaguar CPUs that have the fix to be used. However, support
74611708Sstevel * must be retained to skip revisions that do not have this fix.
74621708Sstevel */
74631708Sstevel
74641708Sstevel err = drmach_cpu_get_impl(dp, &impl);
74651708Sstevel if (err) {
74661708Sstevel DRMACH_PR("%s: error getting impl. of CPU id %d", fn, cpuid);
74671708Sstevel sbd_err_clear(&err);
74681708Sstevel return (-1);
74691708Sstevel }
74701708Sstevel
74711708Sstevel if (IS_JAGUAR(impl) && (STARCAT_CPUID_TO_LPORT(cpuid) == 0) &&
74721708Sstevel drmach_iocage_exclude_jaguar_port_zero) {
74731708Sstevel DRMACH_PR("%s: excluding CPU id %d: port 0 on jaguar",
74741708Sstevel fn, cpuid);
74751708Sstevel return (-1);
74761708Sstevel }
74771708Sstevel
74781708Sstevel ASSERT(oflags);
74791708Sstevel *oflags = cp->cpu_flags;
74801708Sstevel
74811708Sstevel if (cpu_offline(cp, 0)) {
74821708Sstevel DRMACH_PR("%s: cpu_offline failed for CPU id %d", fn, cpuid);
74831708Sstevel return (-1);
74841708Sstevel }
74851708Sstevel
74861708Sstevel if (cpu_poweroff(cp)) {
74871708Sstevel DRMACH_PR("%s: cpu_poweroff failed for CPU id %d", fn, cpuid);
74881708Sstevel if (cpu_online(cp)) {
74891708Sstevel cmn_err(CE_WARN, "failed to online CPU id %d "
74901708Sstevel "during I/O cage test selection", cpuid);
74911708Sstevel }
74921708Sstevel if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
74931708Sstevel drmach_cpu_intr_disable(cp) != 0) {
74941708Sstevel cmn_err(CE_WARN, "failed to restore CPU id %d "
74951708Sstevel "no-intr during I/O cage test selection", cpuid);
74961708Sstevel }
74971708Sstevel return (-1);
74981708Sstevel }
74991708Sstevel
75001708Sstevel if (cpu_unconfigure(cpuid)) {
75011708Sstevel DRMACH_PR("%s: cpu_unconfigure failed for CPU id %d", fn,
75021708Sstevel cpuid);
75031708Sstevel (void) cpu_configure(cpuid);
75041708Sstevel if ((cp = cpu_get(cpuid)) == NULL) {
75051708Sstevel cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
75061708Sstevel "during I/O cage test selection", cpuid);
75071708Sstevel dp->busy = 1;
75081708Sstevel return (-1);
75091708Sstevel }
75101708Sstevel if (cpu_poweron(cp) || cpu_online(cp)) {
75111708Sstevel cmn_err(CE_WARN, "failed to %s CPU id %d "
75121708Sstevel "during I/O cage test selection",
75131708Sstevel cpu_is_poweredoff(cp) ?
75141708Sstevel "poweron" : "online", cpuid);
75151708Sstevel }
75161708Sstevel if (CPU_ACTIVE(cp) && cpu_flagged_nointr(*oflags) &&
75171708Sstevel drmach_cpu_intr_disable(cp) != 0) {
75181708Sstevel cmn_err(CE_WARN, "failed to restore CPU id %d "
75191708Sstevel "no-intr during I/O cage test selection", cpuid);
75201708Sstevel }
75211708Sstevel return (-1);
75221708Sstevel }
75231708Sstevel
75241708Sstevel dp->busy = 1;
75251708Sstevel
75261708Sstevel DRMACH_PR("%s: acquired CPU id %d", fn, cpuid);
75271708Sstevel
75281708Sstevel return (0);
75291708Sstevel }
75301708Sstevel
75311708Sstevel /*
75321708Sstevel * Attempt to acquire all the CPU devices passed in. It is
75331708Sstevel * assumed that all the devices in the list are the cores of
75341708Sstevel * a single CMP device. Non CMP devices can be handled as a
75351708Sstevel * single core CMP by passing in a one element list.
75361708Sstevel *
75371708Sstevel * Success is only returned if *all* the devices in the list
75381708Sstevel * can be acquired. In the failure case, none of the devices
75391708Sstevel * in the list will be held as acquired.
75401708Sstevel */
75411708Sstevel static int
drmach_iocage_cmp_acquire(drmach_device_t ** dpp,cpu_flag_t * oflags)75421708Sstevel drmach_iocage_cmp_acquire(drmach_device_t **dpp, cpu_flag_t *oflags)
75431708Sstevel {
75441708Sstevel int curr;
75451708Sstevel int i;
75461708Sstevel int rv = 0;
75471708Sstevel
75481708Sstevel ASSERT((dpp != NULL) && (*dpp != NULL));
75491708Sstevel
75501708Sstevel /*
75511708Sstevel * Walk the list of CPU devices (cores of a CMP)
75521708Sstevel * and attempt to acquire them. Bail out if an
75531708Sstevel * error is encountered.
75541708Sstevel */
75551772Sjl139090 for (curr = 0; curr < MAX_CORES_PER_CMP; curr++) {
75561708Sstevel
75571708Sstevel /* check for the end of the list */
75581708Sstevel if (dpp[curr] == NULL) {
75591708Sstevel break;
75601708Sstevel }
75611708Sstevel
75621708Sstevel ASSERT(DRMACH_IS_CPU_ID(dpp[curr]));
75631708Sstevel ASSERT(dpp[curr]->portid == (*dpp)->portid);
75641708Sstevel
75651708Sstevel rv = drmach_iocage_cpu_acquire(dpp[curr], &oflags[curr]);
75661708Sstevel if (rv != 0) {
75671708Sstevel break;
75681708Sstevel }
75691708Sstevel }
75701708Sstevel
75711708Sstevel /*
75721708Sstevel * Check for an error.
75731708Sstevel */
75741708Sstevel if (rv != 0) {
75751708Sstevel /*
75761708Sstevel * Make a best effort attempt to return any cores
75771708Sstevel * that were already acquired before the error was
75781708Sstevel * encountered.
75791708Sstevel */
75801708Sstevel for (i = 0; i < curr; i++) {
75811708Sstevel (void) drmach_iocage_cpu_return(dpp[i], oflags[i]);
75821708Sstevel }
75831708Sstevel }
75841708Sstevel
75851708Sstevel return (rv);
75861708Sstevel }
75871708Sstevel
75881708Sstevel static int
drmach_iocage_cpu_return(drmach_device_t * dp,cpu_flag_t oflags)75891708Sstevel drmach_iocage_cpu_return(drmach_device_t *dp, cpu_flag_t oflags)
75901708Sstevel {
75911708Sstevel processorid_t cpuid;
75921708Sstevel struct cpu *cp;
75931708Sstevel int rv = 0;
75941708Sstevel static char *fn = "drmach_iocage_cpu_return";
75951708Sstevel
75961708Sstevel ASSERT(DRMACH_IS_CPU_ID(dp));
75971708Sstevel ASSERT(MUTEX_HELD(&cpu_lock));
75981708Sstevel
75991708Sstevel cpuid = ((drmach_cpu_t *)dp)->cpuid;
76001708Sstevel
76011708Sstevel DRMACH_PR("%s: attempting to return CPU id: %d", fn, cpuid);
76021708Sstevel
76031708Sstevel if (cpu_configure(cpuid)) {
76041708Sstevel cmn_err(CE_WARN, "failed to reconfigure CPU id %d "
76051708Sstevel "after I/O cage test", cpuid);
76061708Sstevel /*
76071708Sstevel * The component was never set to unconfigured during the IO
76081708Sstevel * cage test, so we need to leave marked as busy to prevent
76091708Sstevel * further DR operations involving this component.
76101708Sstevel */
76111708Sstevel return (-1);
76121708Sstevel }
76131708Sstevel
76141708Sstevel if ((cp = cpu_get(cpuid)) == NULL) {
76151708Sstevel cmn_err(CE_WARN, "cpu_get failed on CPU id %d after "
76161708Sstevel "I/O cage test", cpuid);
76171708Sstevel dp->busy = 0;
76181708Sstevel return (-1);
76191708Sstevel }
76201708Sstevel
76211708Sstevel if (cpu_poweron(cp) || cpu_online(cp)) {
76221708Sstevel cmn_err(CE_WARN, "failed to %s CPU id %d after I/O "
76231708Sstevel "cage test", cpu_is_poweredoff(cp) ?
76241708Sstevel "poweron" : "online", cpuid);
76251708Sstevel rv = -1;
76261708Sstevel }
76271708Sstevel
76281708Sstevel /*
76291708Sstevel * drmach_iocage_cpu_acquire will accept cpus in state P_ONLINE or
76301708Sstevel * P_NOINTR. Need to return to previous user-visible state.
76311708Sstevel */
76321708Sstevel if (CPU_ACTIVE(cp) && cpu_flagged_nointr(oflags) &&
76331708Sstevel drmach_cpu_intr_disable(cp) != 0) {
76341708Sstevel cmn_err(CE_WARN, "failed to restore CPU id %d "
76351708Sstevel "no-intr after I/O cage test", cpuid);
76361708Sstevel rv = -1;
76371708Sstevel }
76381708Sstevel
76391708Sstevel dp->busy = 0;
76401708Sstevel
76411708Sstevel DRMACH_PR("%s: returned CPU id: %d", fn, cpuid);
76421708Sstevel
76431708Sstevel return (rv);
76441708Sstevel }
76451708Sstevel
76461708Sstevel static sbd_error_t *
drmach_iocage_cpu_get(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)76471708Sstevel drmach_iocage_cpu_get(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
76481708Sstevel cpu_flag_t *oflags)
76491708Sstevel {
76501708Sstevel drmach_board_t *bp;
76511708Sstevel int b_rv;
76521708Sstevel int b_idx;
76531708Sstevel drmachid_t b_id;
76541708Sstevel int found;
76551708Sstevel
76561708Sstevel mutex_enter(&cpu_lock);
76571708Sstevel
76581708Sstevel ASSERT(drmach_boards != NULL);
76591708Sstevel
76601708Sstevel found = 0;
76611708Sstevel
76621708Sstevel /*
76631708Sstevel * Walk the board list.
76641708Sstevel */
76651708Sstevel b_rv = drmach_array_first(drmach_boards, &b_idx, &b_id);
76661708Sstevel
76671708Sstevel while (b_rv == 0) {
76681708Sstevel
76691708Sstevel int d_rv;
76701708Sstevel int d_idx;
76711708Sstevel drmachid_t d_id;
76721708Sstevel
76731708Sstevel bp = b_id;
76741708Sstevel
76751708Sstevel if (bp->connected == 0 || bp->devices == NULL) {
76761708Sstevel b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
76771708Sstevel continue;
76781708Sstevel }
76791708Sstevel
76801708Sstevel /* An AXQ restriction disqualifies MCPU's as candidates. */
76811708Sstevel if (DRMACH_BNUM2SLOT(bp->bnum) == 1) {
76821708Sstevel b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
76831708Sstevel continue;
76841708Sstevel }
76851708Sstevel
76861708Sstevel /*
76871708Sstevel * Walk the device list of this board.
76881708Sstevel */
76891708Sstevel d_rv = drmach_array_first(bp->devices, &d_idx, &d_id);
76901708Sstevel
76911708Sstevel while (d_rv == 0) {
76921708Sstevel
76931708Sstevel drmach_device_t *ndp;
76941708Sstevel
76951708Sstevel /* only interested in CPU devices */
76961708Sstevel if (!DRMACH_IS_CPU_ID(d_id)) {
76971708Sstevel d_rv = drmach_array_next(bp->devices, &d_idx,
76981708Sstevel &d_id);
76991708Sstevel continue;
77001708Sstevel }
77011708Sstevel
77021708Sstevel /*
77031708Sstevel * The following code assumes two properties
77041708Sstevel * of a CMP device:
77051708Sstevel *
77061708Sstevel * 1. All cores of a CMP are grouped together
77071708Sstevel * in the device list.
77081708Sstevel *
77091708Sstevel * 2. There will only be a maximum of two cores
77101708Sstevel * present in the CMP.
77111708Sstevel *
77121708Sstevel * If either of these two properties change,
77131708Sstevel * this code will have to be revisited.
77141708Sstevel */
77151708Sstevel
77161708Sstevel dpp[0] = d_id;
77171708Sstevel dpp[1] = NULL;
77181708Sstevel
77191708Sstevel /*
77201708Sstevel * Get the next device. It may or may not be used.
77211708Sstevel */
77221708Sstevel d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
77231708Sstevel ndp = d_id;
77241708Sstevel
77251708Sstevel if ((d_rv == 0) && DRMACH_IS_CPU_ID(d_id)) {
77261708Sstevel /*
77271708Sstevel * The second device is only interesting for
77281708Sstevel * this pass if it has the same portid as the
77291708Sstevel * first device. This implies that both are
77301708Sstevel * cores of the same CMP.
77311708Sstevel */
77321708Sstevel if (dpp[0]->portid == ndp->portid) {
77331708Sstevel dpp[1] = d_id;
77341708Sstevel }
77351708Sstevel }
77361708Sstevel
77371708Sstevel /*
77381708Sstevel * Attempt to acquire all cores of the CMP.
77391708Sstevel */
77401708Sstevel if (drmach_iocage_cmp_acquire(dpp, oflags) == 0) {
77411708Sstevel found = 1;
77421708Sstevel break;
77431708Sstevel }
77441708Sstevel
77451708Sstevel /*
77461708Sstevel * Check if the search for the second core was
77471708Sstevel * successful. If not, the next iteration should
77481708Sstevel * use that device.
77491708Sstevel */
77501708Sstevel if (dpp[1] == NULL) {
77511708Sstevel continue;
77521708Sstevel }
77531708Sstevel
77541708Sstevel d_rv = drmach_array_next(bp->devices, &d_idx, &d_id);
77551708Sstevel }
77561708Sstevel
77571708Sstevel if (found)
77581708Sstevel break;
77591708Sstevel
77601708Sstevel b_rv = drmach_array_next(drmach_boards, &b_idx, &b_id);
77611708Sstevel }
77621708Sstevel
77631708Sstevel mutex_exit(&cpu_lock);
77641708Sstevel
77651708Sstevel if (!found) {
77661708Sstevel return (drerr_new(1, ESTC_IOCAGE_NO_CPU_AVAIL, NULL));
77671708Sstevel }
77681708Sstevel
77691708Sstevel tbrq->cpu_portid = (*dpp)->portid;
77701708Sstevel
77711708Sstevel return (NULL);
77721708Sstevel }
77731708Sstevel
77741708Sstevel /*
77751708Sstevel * Setup an iocage by acquiring a cpu and memory.
77761708Sstevel */
77771708Sstevel static sbd_error_t *
drmach_iocage_setup(dr_testboard_req_t * tbrq,drmach_device_t ** dpp,cpu_flag_t * oflags)77781708Sstevel drmach_iocage_setup(dr_testboard_req_t *tbrq, drmach_device_t **dpp,
77791708Sstevel cpu_flag_t *oflags)
77801708Sstevel {
77811708Sstevel sbd_error_t *err;
77821708Sstevel
77831708Sstevel err = drmach_iocage_cpu_get(tbrq, dpp, oflags);
77841708Sstevel if (!err) {
77851708Sstevel mutex_enter(&drmach_iocage_lock);
77861708Sstevel while (drmach_iocage_is_busy)
77871708Sstevel cv_wait(&drmach_iocage_cv, &drmach_iocage_lock);
77881708Sstevel drmach_iocage_is_busy = 1;
77891708Sstevel mutex_exit(&drmach_iocage_lock);
77901708Sstevel err = drmach_iocage_mem_get(tbrq);
77911708Sstevel if (err) {
77921708Sstevel mutex_enter(&drmach_iocage_lock);
77931708Sstevel drmach_iocage_is_busy = 0;
77941708Sstevel cv_signal(&drmach_iocage_cv);
77951708Sstevel mutex_exit(&drmach_iocage_lock);
77961708Sstevel }
77971708Sstevel }
77981708Sstevel return (err);
77991708Sstevel }
78001708Sstevel
78011708Sstevel #define DRMACH_SCHIZO_PCI_LEAF_MAX 2
78021708Sstevel #define DRMACH_SCHIZO_PCI_SLOT_MAX 8
78031708Sstevel #define DRMACH_S1P_SAMPLE_MAX 2
78041708Sstevel
78051708Sstevel typedef enum {
78061708Sstevel DRMACH_POST_SUSPEND = 0,
78071708Sstevel DRMACH_PRE_RESUME
78081708Sstevel } drmach_sr_iter_t;
78091708Sstevel
78101708Sstevel typedef struct {
78111708Sstevel dev_info_t *dip;
78121708Sstevel uint32_t portid;
78131708Sstevel uint32_t pcr_sel_save;
78141708Sstevel uint32_t pic_l2_io_q[DRMACH_S1P_SAMPLE_MAX];
78151708Sstevel uint64_t reg_basepa;
78161708Sstevel } drmach_s1p_axq_t;
78171708Sstevel
78181708Sstevel typedef struct {
78191708Sstevel dev_info_t *dip;
78201708Sstevel uint32_t portid;
78211708Sstevel uint64_t csr_basepa;
78221708Sstevel struct {
78231708Sstevel uint64_t slot_intr_state_diag;
78241708Sstevel uint64_t obio_intr_state_diag;
78251708Sstevel uint_t nmap_regs;
78261708Sstevel uint64_t *intr_map_regs;
78271708Sstevel } regs[DRMACH_S1P_SAMPLE_MAX];
78281708Sstevel } drmach_s1p_pci_t;
78291708Sstevel
78301708Sstevel typedef struct {
78311708Sstevel uint64_t csr_basepa;
78321708Sstevel struct {
78331708Sstevel uint64_t csr;
78341708Sstevel uint64_t errctrl;
78351708Sstevel uint64_t errlog;
78361708Sstevel } regs[DRMACH_S1P_SAMPLE_MAX];
78371708Sstevel drmach_s1p_pci_t pci[DRMACH_SCHIZO_PCI_LEAF_MAX];
78381708Sstevel } drmach_s1p_schizo_t;
78391708Sstevel
78401708Sstevel typedef struct {
78411708Sstevel drmach_s1p_axq_t axq;
78421708Sstevel drmach_s1p_schizo_t schizo[STARCAT_SLOT1_IO_MAX];
78431708Sstevel } drmach_slot1_pause_t;
78441708Sstevel
78451708Sstevel /*
78461708Sstevel * Table of saved state for paused slot1 devices.
78471708Sstevel */
78481708Sstevel static drmach_slot1_pause_t *drmach_slot1_paused[STARCAT_BDSET_MAX];
78491708Sstevel static int drmach_slot1_pause_init = 1;
78501708Sstevel
78511708Sstevel #ifdef DEBUG
78521708Sstevel int drmach_slot1_pause_debug = 1;
78531708Sstevel #else
78541708Sstevel int drmach_slot1_pause_debug = 0;
78551708Sstevel #endif /* DEBUG */
78561708Sstevel
78571708Sstevel static int
drmach_is_slot1_pause_axq(dev_info_t * dip,char * name,int * id,uint64_t * reg)78581708Sstevel drmach_is_slot1_pause_axq(dev_info_t *dip, char *name, int *id, uint64_t *reg)
78591708Sstevel {
78601708Sstevel int portid, exp, slot, i;
78611708Sstevel drmach_reg_t regs[2];
78621708Sstevel int reglen = sizeof (regs);
78631708Sstevel
78641708Sstevel if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
78651708Sstevel DDI_PROP_DONTPASS, "portid", -1)) == -1) {
78661708Sstevel return (0);
78671708Sstevel }
78681708Sstevel
78691708Sstevel exp = (portid >> 5) & 0x1f;
78701708Sstevel slot = portid & 0x1;
78711708Sstevel
78721708Sstevel if (slot == 0 || strncmp(name, DRMACH_AXQ_NAMEPROP,
78731708Sstevel strlen(DRMACH_AXQ_NAMEPROP))) {
78741708Sstevel return (0);
78751708Sstevel }
78761708Sstevel
78771708Sstevel mutex_enter(&cpu_lock);
78781708Sstevel for (i = 0; i < STARCAT_SLOT1_CPU_MAX; i++) {
78791708Sstevel if (cpu[MAKE_CPUID(exp, slot, i)]) {
78801708Sstevel /* maxcat cpu present */
78811708Sstevel mutex_exit(&cpu_lock);
78821708Sstevel return (0);
78831708Sstevel }
78841708Sstevel }
78851708Sstevel mutex_exit(&cpu_lock);
78861708Sstevel
78871708Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
78881708Sstevel "reg", (caddr_t)regs, ®len) != DDI_PROP_SUCCESS) {
78891708Sstevel DRMACH_PR("drmach_is_slot1_pause_axq: no reg prop for "
789011311SSurya.Prakki@Sun.COM "axq dip=%p\n", (void *)dip);
78911708Sstevel return (0);
78921708Sstevel }
78931708Sstevel
78941708Sstevel ASSERT(id && reg);
78951708Sstevel *reg = (uint64_t)regs[0].reg_addr_hi << 32;
78961708Sstevel *reg |= (uint64_t)regs[0].reg_addr_lo;
78971708Sstevel *id = portid;
78981708Sstevel
78991708Sstevel return (1);
79001708Sstevel }
79011708Sstevel
79021708Sstevel /*
79031708Sstevel * Allocate an entry in the slot1_paused state table.
79041708Sstevel */
79051708Sstevel static void
drmach_slot1_pause_add_axq(dev_info_t * axq_dip,char * axq_name,int axq_portid,uint64_t reg,drmach_slot1_pause_t ** slot1_paused)79061708Sstevel drmach_slot1_pause_add_axq(dev_info_t *axq_dip, char *axq_name, int axq_portid,
79071708Sstevel uint64_t reg, drmach_slot1_pause_t **slot1_paused)
79081708Sstevel {
79091708Sstevel int axq_exp;
79101708Sstevel drmach_slot1_pause_t *slot1;
79111708Sstevel
79121708Sstevel axq_exp = (axq_portid >> 5) & 0x1f;
79131708Sstevel
79141708Sstevel ASSERT(axq_portid & 0x1);
79151708Sstevel ASSERT(slot1_paused[axq_exp] == NULL);
79161708Sstevel ASSERT(strncmp(axq_name, DRMACH_AXQ_NAMEPROP,
79171708Sstevel strlen(DRMACH_AXQ_NAMEPROP)) == 0);
79181708Sstevel
79191708Sstevel slot1 = kmem_zalloc(sizeof (*slot1), KM_SLEEP);
79201708Sstevel
79211708Sstevel /*
79221708Sstevel * XXX This dip should really be held (via ndi_hold_devi())
79231708Sstevel * before saving it in the axq pause structure. However that
79241708Sstevel * would prevent DR as the pause data structures persist until
79251708Sstevel * the next suspend. drmach code should be modified to free the
79261708Sstevel * the slot 1 pause data structures for a boardset when its
79271708Sstevel * slot 1 board is DRed out. The dip can then be released via
79281708Sstevel * ndi_rele_devi() when the pause data structure is freed
79291708Sstevel * allowing DR to proceed. Until this change is made, drmach
79301708Sstevel * code should be careful about dereferencing the saved dip
79311708Sstevel * as it may no longer exist.
79321708Sstevel */
79331708Sstevel slot1->axq.dip = axq_dip;
79341708Sstevel slot1->axq.portid = axq_portid;
79351708Sstevel slot1->axq.reg_basepa = reg;
79361708Sstevel slot1_paused[axq_exp] = slot1;
79371708Sstevel }
79381708Sstevel
79391708Sstevel static void
drmach_s1p_pci_free(drmach_s1p_pci_t * pci)79401708Sstevel drmach_s1p_pci_free(drmach_s1p_pci_t *pci)
79411708Sstevel {
79421708Sstevel int i;
79431708Sstevel
79441708Sstevel for (i = 0; i < DRMACH_S1P_SAMPLE_MAX; i++) {
79451708Sstevel if (pci->regs[i].intr_map_regs != NULL) {
79461708Sstevel ASSERT(pci->regs[i].nmap_regs > 0);
79471708Sstevel kmem_free(pci->regs[i].intr_map_regs,
79481708Sstevel pci->regs[i].nmap_regs * sizeof (uint64_t));
79491708Sstevel }
79501708Sstevel }
79511708Sstevel }
79521708Sstevel
79531708Sstevel static void
drmach_slot1_pause_free(drmach_slot1_pause_t ** slot1_paused)79541708Sstevel drmach_slot1_pause_free(drmach_slot1_pause_t **slot1_paused)
79551708Sstevel {
79561708Sstevel int i, j, k;
79571708Sstevel drmach_slot1_pause_t *slot1;
79581708Sstevel
79591708Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) {
79601708Sstevel if ((slot1 = slot1_paused[i]) == NULL)
79611708Sstevel continue;
79621708Sstevel
79631708Sstevel for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
79641708Sstevel for (k = 0; k < DRMACH_SCHIZO_PCI_LEAF_MAX; k++)
79651708Sstevel drmach_s1p_pci_free(&slot1->schizo[j].pci[k]);
79661708Sstevel
79671708Sstevel kmem_free(slot1, sizeof (*slot1));
79681708Sstevel slot1_paused[i] = NULL;
79691708Sstevel }
79701708Sstevel }
79711708Sstevel
79721708Sstevel /*
79731708Sstevel * Tree walk callback routine. If dip represents a Schizo PCI leaf,
79741708Sstevel * fill in the appropriate info in the slot1_paused state table.
79751708Sstevel */
79761708Sstevel static int
drmach_find_slot1_io(dev_info_t * dip,void * arg)79771708Sstevel drmach_find_slot1_io(dev_info_t *dip, void *arg)
79781708Sstevel {
79791708Sstevel int portid, exp, ioc_unum, leaf_unum;
79801708Sstevel char buf[OBP_MAXDRVNAME];
79811708Sstevel int buflen = sizeof (buf);
79821708Sstevel drmach_reg_t regs[3];
79831708Sstevel int reglen = sizeof (regs);
79841708Sstevel uint32_t leaf_offset;
79851708Sstevel uint64_t schizo_csr_pa, pci_csr_pa;
79861708Sstevel drmach_s1p_pci_t *pci;
79871708Sstevel drmach_slot1_pause_t **slot1_paused = (drmach_slot1_pause_t **)arg;
79881708Sstevel
79891708Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
79901708Sstevel "name", (caddr_t)buf, &buflen) != DDI_PROP_SUCCESS ||
79911708Sstevel strncmp(buf, DRMACH_PCI_NAMEPROP, strlen(DRMACH_PCI_NAMEPROP))) {
79921708Sstevel return (DDI_WALK_CONTINUE);
79931708Sstevel }
79941708Sstevel
79951708Sstevel if ((portid = ddi_getprop(DDI_DEV_T_ANY, dip,
79961708Sstevel DDI_PROP_DONTPASS, "portid", -1)) == -1) {
79971708Sstevel return (DDI_WALK_CONTINUE);
79981708Sstevel }
79991708Sstevel
80001708Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
80011708Sstevel "reg", (caddr_t)regs, ®len) != DDI_PROP_SUCCESS) {
80021708Sstevel DRMACH_PR("drmach_find_slot1_io: no reg prop for pci "
800311311SSurya.Prakki@Sun.COM "dip=%p\n", (void *)dip);
80041708Sstevel return (DDI_WALK_CONTINUE);
80051708Sstevel }
80061708Sstevel
80071708Sstevel exp = portid >> 5;
80081708Sstevel ioc_unum = portid & 0x1;
80091708Sstevel leaf_offset = regs[0].reg_addr_lo & 0x7fffff;
80101708Sstevel pci_csr_pa = (uint64_t)regs[0].reg_addr_hi << 32;
80111708Sstevel pci_csr_pa |= (uint64_t)regs[0].reg_addr_lo;
80121708Sstevel schizo_csr_pa = (uint64_t)regs[1].reg_addr_hi << 32;
80131708Sstevel schizo_csr_pa |= (uint64_t)regs[1].reg_addr_lo;
80141708Sstevel
80151708Sstevel ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
80161708Sstevel ASSERT(slot1_paused[exp] != NULL);
80171708Sstevel ASSERT(leaf_offset == 0x600000 || leaf_offset == 0x700000);
80181708Sstevel ASSERT(slot1_paused[exp]->schizo[ioc_unum].csr_basepa == 0x0UL ||
80191708Sstevel slot1_paused[exp]->schizo[ioc_unum].csr_basepa == schizo_csr_pa);
80201708Sstevel
80211708Sstevel leaf_unum = (leaf_offset == 0x600000) ? 0 : 1;
80221708Sstevel slot1_paused[exp]->schizo[ioc_unum].csr_basepa = schizo_csr_pa;
80231708Sstevel pci = &slot1_paused[exp]->schizo[ioc_unum].pci[leaf_unum];
80241708Sstevel
80251708Sstevel /*
80261708Sstevel * XXX This dip should really be held (via ndi_hold_devi())
80271708Sstevel * before saving it in the pci pause structure. However that
80281708Sstevel * would prevent DR as the pause data structures persist until
80291708Sstevel * the next suspend. drmach code should be modified to free the
80301708Sstevel * the slot 1 pause data structures for a boardset when its
80311708Sstevel * slot 1 board is DRed out. The dip can then be released via
80321708Sstevel * ndi_rele_devi() when the pause data structure is freed
80331708Sstevel * allowing DR to proceed. Until this change is made, drmach
80341708Sstevel * code should be careful about dereferencing the saved dip as
80351708Sstevel * it may no longer exist.
80361708Sstevel */
80371708Sstevel pci->dip = dip;
80381708Sstevel pci->portid = portid;
80391708Sstevel pci->csr_basepa = pci_csr_pa;
80401708Sstevel
80411708Sstevel DRMACH_PR("drmach_find_slot1_io: name=%s, portid=0x%x, dip=%p\n",
804211311SSurya.Prakki@Sun.COM buf, portid, (void *)dip);
80431708Sstevel
80441708Sstevel return (DDI_WALK_PRUNECHILD);
80451708Sstevel }
80461708Sstevel
80471708Sstevel static void
drmach_slot1_pause_add_io(drmach_slot1_pause_t ** slot1_paused)80481708Sstevel drmach_slot1_pause_add_io(drmach_slot1_pause_t **slot1_paused)
80491708Sstevel {
80501708Sstevel /*
80511708Sstevel * Root node doesn't have to be held
80521708Sstevel */
80531708Sstevel ddi_walk_devs(ddi_root_node(), drmach_find_slot1_io,
80541708Sstevel (void *)slot1_paused);
80551708Sstevel }
80561708Sstevel
80571708Sstevel /*
80581708Sstevel * Save the interrupt mapping registers for each non-idle interrupt
80591708Sstevel * represented by the bit pairs in the saved interrupt state
80601708Sstevel * diagnostic registers for this PCI leaf.
80611708Sstevel */
80621708Sstevel static void
drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t * pci,drmach_sr_iter_t iter)80631708Sstevel drmach_s1p_intr_map_reg_save(drmach_s1p_pci_t *pci, drmach_sr_iter_t iter)
80641708Sstevel {
80651708Sstevel int i, cnt, ino;
80661708Sstevel uint64_t reg;
80671708Sstevel char *dname;
80681708Sstevel uchar_t Xmits;
80691708Sstevel
80701708Sstevel dname = ddi_binding_name(pci->dip);
80711708Sstevel Xmits = (strcmp(dname, XMITS_BINDING_NAME) == 0) ? 1 : 0;
80721708Sstevel
80731708Sstevel /*
80741708Sstevel * 1st pass allocates, 2nd pass populates.
80751708Sstevel */
80761708Sstevel for (i = 0; i < 2; i++) {
80771708Sstevel cnt = ino = 0;
80781708Sstevel
80791708Sstevel /*
80801708Sstevel * PCI slot interrupts
80811708Sstevel */
80821708Sstevel reg = pci->regs[iter].slot_intr_state_diag;
80831708Sstevel while (reg) {
80841708Sstevel /*
80851708Sstevel * Xmits Interrupt Number Offset(ino) Assignments
80861708Sstevel * 00-17 PCI Slot Interrupts
80871708Sstevel * 18-1f Not Used
80881708Sstevel */
80891708Sstevel if ((Xmits) && (ino > 0x17))
80901708Sstevel break;
80911708Sstevel if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
80921708Sstevel COMMON_CLEAR_INTR_REG_IDLE) {
80931708Sstevel if (i) {
80941708Sstevel pci->regs[iter].intr_map_regs[cnt] =
80951708Sstevel lddphysio(pci->csr_basepa +
80961708Sstevel SCHIZO_IB_INTR_MAP_REG_OFFSET +
80971708Sstevel ino * sizeof (reg));
80981708Sstevel }
80991708Sstevel ++cnt;
81001708Sstevel }
81011708Sstevel ++ino;
81021708Sstevel reg >>= 2;
81031708Sstevel }
81041708Sstevel
81051708Sstevel /*
81061708Sstevel * Xmits Interrupt Number Offset(ino) Assignments
81071708Sstevel * 20-2f Not Used
81081708Sstevel * 30-37 Internal interrupts
81091708Sstevel * 38-3e Not Used
81101708Sstevel */
81111708Sstevel ino = (Xmits) ? 0x30 : 0x20;
81121708Sstevel
81131708Sstevel /*
81141708Sstevel * OBIO and internal schizo interrupts
81151708Sstevel * Each PCI leaf has a set of mapping registers for all
81161708Sstevel * possible interrupt sources except the NewLink interrupts.
81171708Sstevel */
81181708Sstevel reg = pci->regs[iter].obio_intr_state_diag;
81191708Sstevel while (reg && ino <= 0x38) {
81201708Sstevel if ((reg & COMMON_CLEAR_INTR_REG_MASK) !=
81211708Sstevel COMMON_CLEAR_INTR_REG_IDLE) {
81221708Sstevel if (i) {
81231708Sstevel pci->regs[iter].intr_map_regs[cnt] =
81241708Sstevel lddphysio(pci->csr_basepa +
81251708Sstevel SCHIZO_IB_INTR_MAP_REG_OFFSET +
81261708Sstevel ino * sizeof (reg));
81271708Sstevel }
81281708Sstevel ++cnt;
81291708Sstevel }
81301708Sstevel ++ino;
81311708Sstevel reg >>= 2;
81321708Sstevel }
81331708Sstevel
81341708Sstevel if (!i) {
81351708Sstevel pci->regs[iter].nmap_regs = cnt;
81361708Sstevel pci->regs[iter].intr_map_regs =
81371708Sstevel kmem_zalloc(cnt * sizeof (reg), KM_SLEEP);
81381708Sstevel }
81391708Sstevel }
81401708Sstevel }
81411708Sstevel
81421708Sstevel static void
drmach_s1p_axq_update(drmach_s1p_axq_t * axq,drmach_sr_iter_t iter)81431708Sstevel drmach_s1p_axq_update(drmach_s1p_axq_t *axq, drmach_sr_iter_t iter)
81441708Sstevel {
81451708Sstevel uint32_t reg;
81461708Sstevel
81471708Sstevel if (axq->reg_basepa == 0x0UL)
81481708Sstevel return;
81491708Sstevel
81501708Sstevel if (iter == DRMACH_POST_SUSPEND) {
81511708Sstevel axq->pcr_sel_save = ldphysio(axq->reg_basepa +
81521708Sstevel AXQ_SLOT1_PERFCNT_SEL);
81531708Sstevel /*
81541708Sstevel * Select l2_io_queue counter by writing L2_IO_Q mux
81551708Sstevel * input to bits 0-6 of perf cntr select reg.
81561708Sstevel */
81571708Sstevel reg = axq->pcr_sel_save;
81581708Sstevel reg &= ~AXQ_PIC_CLEAR_MASK;
81591708Sstevel reg |= L2_IO_Q;
81601708Sstevel
81611708Sstevel stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL, reg);
81621708Sstevel }
81631708Sstevel
81641708Sstevel axq->pic_l2_io_q[iter] = ldphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT0);
81651708Sstevel
81661708Sstevel if (iter == DRMACH_PRE_RESUME) {
81671708Sstevel stphysio(axq->reg_basepa + AXQ_SLOT1_PERFCNT_SEL,
81681708Sstevel axq->pcr_sel_save);
81691708Sstevel }
81701708Sstevel
81711708Sstevel DRMACH_PR("drmach_s1p_axq_update: axq #%d pic_l2_io_q[%d]=%d\n",
81721708Sstevel ddi_get_instance(axq->dip), iter, axq->pic_l2_io_q[iter]);
81731708Sstevel }
81741708Sstevel
81751708Sstevel static void
drmach_s1p_schizo_update(drmach_s1p_schizo_t * schizo,drmach_sr_iter_t iter)81761708Sstevel drmach_s1p_schizo_update(drmach_s1p_schizo_t *schizo, drmach_sr_iter_t iter)
81771708Sstevel {
81781708Sstevel int i;
81791708Sstevel drmach_s1p_pci_t *pci;
81801708Sstevel
81811708Sstevel if (schizo->csr_basepa == 0x0UL)
81821708Sstevel return;
81831708Sstevel
81841708Sstevel schizo->regs[iter].csr =
81851708Sstevel lddphysio(schizo->csr_basepa + SCHIZO_CB_CSR_OFFSET);
81861708Sstevel schizo->regs[iter].errctrl =
81871708Sstevel lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRCTRL_OFFSET);
81881708Sstevel schizo->regs[iter].errlog =
81891708Sstevel lddphysio(schizo->csr_basepa + SCHIZO_CB_ERRLOG_OFFSET);
81901708Sstevel
81911708Sstevel for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
81921708Sstevel pci = &schizo->pci[i];
81931708Sstevel if (pci->dip != NULL && pci->csr_basepa != 0x0UL) {
81941708Sstevel pci->regs[iter].slot_intr_state_diag =
81951708Sstevel lddphysio(pci->csr_basepa +
81961708Sstevel COMMON_IB_SLOT_INTR_STATE_DIAG_REG);
81971708Sstevel
81981708Sstevel pci->regs[iter].obio_intr_state_diag =
81991708Sstevel lddphysio(pci->csr_basepa +
82001708Sstevel COMMON_IB_OBIO_INTR_STATE_DIAG_REG);
82011708Sstevel
82021708Sstevel drmach_s1p_intr_map_reg_save(pci, iter);
82031708Sstevel }
82041708Sstevel }
82051708Sstevel }
82061708Sstevel
82071708Sstevel /*
82081708Sstevel * Called post-suspend and pre-resume to snapshot the suspend state
82091708Sstevel * of slot1 AXQs and Schizos.
82101708Sstevel */
82111708Sstevel static void
drmach_slot1_pause_update(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)82121708Sstevel drmach_slot1_pause_update(drmach_slot1_pause_t **slot1_paused,
82131708Sstevel drmach_sr_iter_t iter)
82141708Sstevel {
82151708Sstevel int i, j;
82161708Sstevel drmach_slot1_pause_t *slot1;
82171708Sstevel
82181708Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) {
82191708Sstevel if ((slot1 = slot1_paused[i]) == NULL)
82201708Sstevel continue;
82211708Sstevel
82221708Sstevel drmach_s1p_axq_update(&slot1->axq, iter);
82231708Sstevel for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++)
82241708Sstevel drmach_s1p_schizo_update(&slot1->schizo[j], iter);
82251708Sstevel }
82261708Sstevel }
82271708Sstevel
82281708Sstevel /*
82291708Sstevel * Starcat hPCI Schizo devices.
82301708Sstevel *
82311708Sstevel * The name field is overloaded. NULL means the slot (interrupt concentrator
82321708Sstevel * bus) is not used. intr_mask is a bit mask representing the 4 possible
82331708Sstevel * interrupts per slot, on if valid (rio does not use interrupt lines 0, 1).
82341708Sstevel */
82351708Sstevel static struct {
82361708Sstevel char *name;
82371708Sstevel uint8_t intr_mask;
82381708Sstevel } drmach_schz_slot_intr[][DRMACH_SCHIZO_PCI_LEAF_MAX] = {
82391708Sstevel /* Schizo 0 */ /* Schizo 1 */
82401708Sstevel {{"C3V0", 0xf}, {"C3V1", 0xf}}, /* slot 0 */
82411708Sstevel {{"C5V0", 0xf}, {"C5V1", 0xf}}, /* slot 1 */
82421708Sstevel {{"rio", 0xc}, {NULL, 0x0}}, /* slot 2 */
82431708Sstevel {{NULL, 0x0}, {NULL, 0x0}}, /* slot 3 */
82441708Sstevel {{"sbbc", 0xf}, {NULL, 0x0}}, /* slot 4 */
82451708Sstevel {{NULL, 0x0}, {NULL, 0x0}}, /* slot 5 */
82461708Sstevel {{NULL, 0x0}, {NULL, 0x0}}, /* slot 6 */
82471708Sstevel {{NULL, 0x0}, {NULL, 0x0}} /* slot 7 */
82481708Sstevel };
82491708Sstevel
82501708Sstevel /*
82511708Sstevel * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.4.4
82521708Sstevel * "Interrupt Registers", Table 22-69, page 306.
82531708Sstevel */
82541708Sstevel static char *
drmach_schz_internal_ino2str(int ino)82551708Sstevel drmach_schz_internal_ino2str(int ino)
82561708Sstevel {
82571708Sstevel int intr;
82581708Sstevel
82591708Sstevel ASSERT(ino >= 0x30 && ino <= 0x37);
82601708Sstevel
82611708Sstevel intr = ino & 0x7;
82621708Sstevel switch (intr) {
82631708Sstevel case (0x0): return ("Uncorrectable ECC error");
82641708Sstevel case (0x1): return ("Correctable ECC error");
82651708Sstevel case (0x2): return ("PCI Bus A Error");
82661708Sstevel case (0x3): return ("PCI Bus B Error");
82671708Sstevel case (0x4): return ("Safari Bus Error");
82681708Sstevel default: return ("Reserved");
82691708Sstevel }
82701708Sstevel }
82711708Sstevel
82721708Sstevel #define DRMACH_INTR_MASK_SHIFT(ino) ((ino) << 1)
82731708Sstevel
82741708Sstevel static void
drmach_s1p_decode_slot_intr(int exp,int unum,drmach_s1p_pci_t * pci,int ino,drmach_sr_iter_t iter)82751708Sstevel drmach_s1p_decode_slot_intr(int exp, int unum, drmach_s1p_pci_t *pci,
82761708Sstevel int ino, drmach_sr_iter_t iter)
82771708Sstevel {
82781708Sstevel uint8_t intr_mask;
82791708Sstevel char *slot_devname;
82801708Sstevel char namebuf[OBP_MAXDRVNAME];
82811708Sstevel int slot, intr_line, slot_valid, intr_valid;
82821708Sstevel
82831708Sstevel ASSERT(ino >= 0 && ino <= 0x1f);
82841708Sstevel ASSERT((pci->regs[iter].slot_intr_state_diag &
82851708Sstevel (COMMON_CLEAR_INTR_REG_MASK << DRMACH_INTR_MASK_SHIFT(ino))) !=
82861708Sstevel COMMON_CLEAR_INTR_REG_IDLE);
82871708Sstevel
82881708Sstevel slot = (ino >> 2) & 0x7;
82891708Sstevel intr_line = ino & 0x3;
82901708Sstevel
82911708Sstevel slot_devname = drmach_schz_slot_intr[slot][unum].name;
82921708Sstevel slot_valid = (slot_devname == NULL) ? 0 : 1;
82931708Sstevel if (!slot_valid) {
829411311SSurya.Prakki@Sun.COM (void) snprintf(namebuf, sizeof (namebuf), "slot %d (INVALID)",
829511311SSurya.Prakki@Sun.COM slot);
82961708Sstevel slot_devname = namebuf;
82971708Sstevel }
82981708Sstevel
82991708Sstevel intr_mask = drmach_schz_slot_intr[slot][unum].intr_mask;
83001708Sstevel intr_valid = (1 << intr_line) & intr_mask;
83011708Sstevel
83021708Sstevel prom_printf("IO%d/P%d PCI slot interrupt: ino=0x%x, source device=%s, "
83031708Sstevel "interrupt line=%d%s\n", exp, unum, ino, slot_devname, intr_line,
83041708Sstevel (slot_valid && !intr_valid) ? " (INVALID)" : "");
83051708Sstevel }
83061708Sstevel
83071708Sstevel /*
83081708Sstevel * Log interrupt source device info for all valid, pending interrupts
83091708Sstevel * on each Schizo PCI leaf. Called if Schizo has logged a Safari bus
83101708Sstevel * error in the error ctrl reg.
83111708Sstevel */
83121708Sstevel static void
drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t * schizo,int exp,int unum,drmach_sr_iter_t iter)83131708Sstevel drmach_s1p_schizo_log_intr(drmach_s1p_schizo_t *schizo, int exp,
83141708Sstevel int unum, drmach_sr_iter_t iter)
83151708Sstevel {
83161708Sstevel uint64_t reg;
83171708Sstevel int i, n, ino;
83181708Sstevel drmach_s1p_pci_t *pci;
83191708Sstevel
83201708Sstevel ASSERT(exp >= 0 && exp < STARCAT_BDSET_MAX);
83211708Sstevel ASSERT(unum < STARCAT_SLOT1_IO_MAX);
83221708Sstevel
83231708Sstevel /*
83241708Sstevel * Check the saved interrupt mapping registers. If interrupt is valid,
83251708Sstevel * map the ino to the Schizo source device and check that the pci
83261708Sstevel * slot and interrupt line are valid.
83271708Sstevel */
83281708Sstevel for (i = 0; i < DRMACH_SCHIZO_PCI_LEAF_MAX; i++) {
83291708Sstevel pci = &schizo->pci[i];
83301708Sstevel for (n = 0; n < pci->regs[iter].nmap_regs; n++) {
83311708Sstevel reg = pci->regs[iter].intr_map_regs[n];
83321708Sstevel if (reg & COMMON_INTR_MAP_REG_VALID) {
83331708Sstevel ino = reg & COMMON_INTR_MAP_REG_INO;
83341708Sstevel
83351708Sstevel if (ino <= 0x1f) {
83361708Sstevel /*
83371708Sstevel * PCI slot interrupt
83381708Sstevel */
83391708Sstevel drmach_s1p_decode_slot_intr(exp, unum,
83401708Sstevel pci, ino, iter);
83411708Sstevel } else if (ino <= 0x2f) {
83421708Sstevel /*
83431708Sstevel * OBIO interrupt
83441708Sstevel */
83451708Sstevel prom_printf("IO%d/P%d OBIO interrupt: "
83461708Sstevel "ino=0x%x\n", exp, unum, ino);
83471708Sstevel } else if (ino <= 0x37) {
83481708Sstevel /*
83491708Sstevel * Internal interrupt
83501708Sstevel */
83511708Sstevel prom_printf("IO%d/P%d Internal "
83521708Sstevel "interrupt: ino=0x%x (%s)\n",
83531708Sstevel exp, unum, ino,
83541708Sstevel drmach_schz_internal_ino2str(ino));
83551708Sstevel } else {
83561708Sstevel /*
83571708Sstevel * NewLink interrupt
83581708Sstevel */
83591708Sstevel prom_printf("IO%d/P%d NewLink "
83601708Sstevel "interrupt: ino=0x%x\n", exp,
83611708Sstevel unum, ino);
83621708Sstevel }
83631708Sstevel
83641708Sstevel DRMACH_PR("drmach_s1p_schizo_log_intr: "
83651708Sstevel "exp=%d, schizo=%d, pci_leaf=%c, "
83661708Sstevel "ino=0x%x, intr_map_reg=0x%lx\n",
83671708Sstevel exp, unum, (i == 0) ? 'A' : 'B', ino, reg);
83681708Sstevel }
83691708Sstevel }
83701708Sstevel }
83711708Sstevel }
83721708Sstevel
83731708Sstevel /*
83741708Sstevel * See Schizo Specification, Revision 51 (May 23, 2001), Section 22.2.4
83751708Sstevel * "Safari Error Control/Log Registers", Table 22-11, page 248.
83761708Sstevel */
83771708Sstevel #define DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR (0x1ull << 4)
83781708Sstevel
83791708Sstevel /*
83801708Sstevel * Check for possible error indicators prior to resuming the
83811708Sstevel * AXQ driver, which will de-assert slot1 AXQ_DOMCTRL_PAUSE.
83821708Sstevel */
83831708Sstevel static void
drmach_slot1_pause_verify(drmach_slot1_pause_t ** slot1_paused,drmach_sr_iter_t iter)83841708Sstevel drmach_slot1_pause_verify(drmach_slot1_pause_t **slot1_paused,
83851708Sstevel drmach_sr_iter_t iter)
83861708Sstevel {
83871708Sstevel int i, j;
83881708Sstevel int errflag = 0;
83891708Sstevel drmach_slot1_pause_t *slot1;
83901708Sstevel
83911708Sstevel /*
83921708Sstevel * Check for logged schizo bus error and pending interrupts.
83931708Sstevel */
83941708Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) {
83951708Sstevel if ((slot1 = slot1_paused[i]) == NULL)
83961708Sstevel continue;
83971708Sstevel
83981708Sstevel for (j = 0; j < STARCAT_SLOT1_IO_MAX; j++) {
83991708Sstevel if (slot1->schizo[j].csr_basepa == 0x0UL)
84001708Sstevel continue;
84011708Sstevel
84021708Sstevel if (slot1->schizo[j].regs[iter].errlog &
84031708Sstevel DRMACH_SCHIZO_SAFARI_UNMAPPED_ERR) {
84041708Sstevel if (!errflag) {
84051708Sstevel prom_printf("DR WARNING: interrupt "
84061708Sstevel "attempt detected during "
84071708Sstevel "copy-rename (%s):\n",
84081708Sstevel (iter == DRMACH_POST_SUSPEND) ?
84091708Sstevel "post suspend" : "pre resume");
84101708Sstevel ++errflag;
84111708Sstevel }
84121708Sstevel drmach_s1p_schizo_log_intr(&slot1->schizo[j],
84131708Sstevel i, j, iter);
84141708Sstevel }
84151708Sstevel }
84161708Sstevel }
84171708Sstevel
84181708Sstevel /*
84191708Sstevel * Check for changes in axq l2_io_q performance counters (2nd pass only)
84201708Sstevel */
84211708Sstevel if (iter == DRMACH_PRE_RESUME) {
84221708Sstevel for (i = 0; i < STARCAT_BDSET_MAX; i++) {
84231708Sstevel if ((slot1 = slot1_paused[i]) == NULL)
84241708Sstevel continue;
84251708Sstevel
84261708Sstevel if (slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND] !=
84271708Sstevel slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]) {
84281708Sstevel prom_printf("DR WARNING: IO transactions "
84291708Sstevel "detected on IO%d during copy-rename: "
84301708Sstevel "AXQ l2_io_q performance counter "
84311708Sstevel "start=%d, end=%d\n", i,
84321708Sstevel slot1->axq.pic_l2_io_q[DRMACH_POST_SUSPEND],
84331708Sstevel slot1->axq.pic_l2_io_q[DRMACH_PRE_RESUME]);
84341708Sstevel }
84351708Sstevel }
84361708Sstevel }
84371708Sstevel }
84381708Sstevel
84391708Sstevel struct drmach_sr_list {
84401708Sstevel dev_info_t *dip;
84411708Sstevel struct drmach_sr_list *next;
84421708Sstevel struct drmach_sr_list *prev;
84431708Sstevel };
84441708Sstevel
84451708Sstevel static struct drmach_sr_ordered {
84461708Sstevel char *name;
84471708Sstevel struct drmach_sr_list *ring;
84481708Sstevel } drmach_sr_ordered[] = {
84491708Sstevel { "iosram", NULL },
84501708Sstevel { "address-extender-queue", NULL },
84511708Sstevel { NULL, NULL }, /* terminator -- required */
84521708Sstevel };
84531708Sstevel
84541708Sstevel static void
drmach_sr_insert(struct drmach_sr_list ** lp,dev_info_t * dip)84551708Sstevel drmach_sr_insert(struct drmach_sr_list **lp, dev_info_t *dip)
84561708Sstevel {
84571708Sstevel struct drmach_sr_list *np;
84581708Sstevel
845911311SSurya.Prakki@Sun.COM DRMACH_PR("drmach_sr_insert: adding dip %p\n", (void *)dip);
84601708Sstevel
84611708Sstevel np = (struct drmach_sr_list *)kmem_alloc(
846211066Srafael.vanoni@sun.com sizeof (struct drmach_sr_list), KM_SLEEP);
84631708Sstevel
84641708Sstevel ndi_hold_devi(dip);
84651708Sstevel np->dip = dip;
84661708Sstevel
84671708Sstevel if (*lp == NULL) {
84681708Sstevel /* establish list */
84691708Sstevel *lp = np->next = np->prev = np;
84701708Sstevel } else {
84711708Sstevel /* place new node behind head node on ring list */
84721708Sstevel np->prev = (*lp)->prev;
84731708Sstevel np->next = *lp;
84741708Sstevel np->prev->next = np;
84751708Sstevel np->next->prev = np;
84761708Sstevel }
84771708Sstevel }
84781708Sstevel
84791708Sstevel static void
drmach_sr_delete(struct drmach_sr_list ** lp,dev_info_t * dip)84801708Sstevel drmach_sr_delete(struct drmach_sr_list **lp, dev_info_t *dip)
84811708Sstevel {
848211311SSurya.Prakki@Sun.COM DRMACH_PR("drmach_sr_delete: searching for dip %p\n", (void *)dip);
84831708Sstevel
84841708Sstevel if (*lp) {
84851708Sstevel struct drmach_sr_list *xp;
84861708Sstevel
84871708Sstevel /* start search with mostly likely node */
84881708Sstevel xp = (*lp)->prev;
84891708Sstevel do {
84901708Sstevel if (xp->dip == dip) {
84911708Sstevel xp->prev->next = xp->next;
84921708Sstevel xp->next->prev = xp->prev;
84931708Sstevel
84941708Sstevel if (xp == *lp)
84951708Sstevel *lp = xp->next;
84961708Sstevel if (xp == *lp)
84971708Sstevel *lp = NULL;
84981708Sstevel xp->dip = NULL;
84991708Sstevel ndi_rele_devi(dip);
85001708Sstevel kmem_free(xp, sizeof (*xp));
85011708Sstevel
85021708Sstevel DRMACH_PR("drmach_sr_delete:"
850311311SSurya.Prakki@Sun.COM " disposed sr node for dip %p",
850411311SSurya.Prakki@Sun.COM (void *)dip);
85051708Sstevel return;
85061708Sstevel }
85071708Sstevel
85081708Sstevel DRMACH_PR("drmach_sr_delete: still searching\n");
85091708Sstevel
85101708Sstevel xp = xp->prev;
85111708Sstevel } while (xp != (*lp)->prev);
85121708Sstevel }
85131708Sstevel
85141708Sstevel /* every dip should be found during resume */
851511311SSurya.Prakki@Sun.COM DRMACH_PR("ERROR: drmach_sr_delete: can't find dip %p", (void *)dip);
85161708Sstevel }
85171708Sstevel
85181708Sstevel int
drmach_verify_sr(dev_info_t * dip,int sflag)85191708Sstevel drmach_verify_sr(dev_info_t *dip, int sflag)
85201708Sstevel {
85211708Sstevel int rv;
85221708Sstevel int len;
85231708Sstevel char name[OBP_MAXDRVNAME];
85241708Sstevel
85251708Sstevel if (drmach_slot1_pause_debug) {
85261708Sstevel if (sflag && drmach_slot1_pause_init) {
85271708Sstevel drmach_slot1_pause_free(drmach_slot1_paused);
85281708Sstevel drmach_slot1_pause_init = 0;
85291708Sstevel } else if (!sflag && !drmach_slot1_pause_init) {
85301708Sstevel /* schedule init for next suspend */
85311708Sstevel drmach_slot1_pause_init = 1;
85321708Sstevel }
85331708Sstevel }
85341708Sstevel
85351708Sstevel rv = ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
853611066Srafael.vanoni@sun.com "name", &len);
85371708Sstevel if (rv == DDI_PROP_SUCCESS) {
85381708Sstevel int portid;
85391708Sstevel uint64_t reg;
85401708Sstevel struct drmach_sr_ordered *op;
85411708Sstevel
85421708Sstevel rv = ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
854311066Srafael.vanoni@sun.com DDI_PROP_DONTPASS, "name", (caddr_t)name, &len);
85441708Sstevel
85451708Sstevel if (rv != DDI_PROP_SUCCESS)
85461708Sstevel return (0);
85471708Sstevel
85481708Sstevel if (drmach_slot1_pause_debug && sflag &&
85491708Sstevel drmach_is_slot1_pause_axq(dip, name, &portid, ®)) {
85501708Sstevel drmach_slot1_pause_add_axq(dip, name, portid, reg,
85511708Sstevel drmach_slot1_paused);
85521708Sstevel }
85531708Sstevel
85541708Sstevel for (op = drmach_sr_ordered; op->name; op++) {
85551708Sstevel if (strncmp(op->name, name, strlen(op->name)) == 0) {
85561708Sstevel if (sflag)
85571708Sstevel drmach_sr_insert(&op->ring, dip);
85581708Sstevel else
85591708Sstevel drmach_sr_delete(&op->ring, dip);
85601708Sstevel return (1);
85611708Sstevel }
85621708Sstevel }
85631708Sstevel }
85641708Sstevel
85651708Sstevel return (0);
85661708Sstevel }
85671708Sstevel
85681708Sstevel static void
drmach_sr_dip(dev_info_t * dip,int suspend)85691708Sstevel drmach_sr_dip(dev_info_t *dip, int suspend)
85701708Sstevel {
85711708Sstevel int rv;
85721708Sstevel major_t maj;
85731708Sstevel char *name, *name_addr, *aka;
85741708Sstevel
85751708Sstevel if ((name = ddi_get_name(dip)) == NULL)
85761708Sstevel name = "<null name>";
85771708Sstevel else if ((maj = ddi_name_to_major(name)) != -1)
85781708Sstevel aka = ddi_major_to_name(maj);
85791708Sstevel else
85801708Sstevel aka = "<unknown>";
85811708Sstevel
85821708Sstevel if ((name_addr = ddi_get_name_addr(dip)) == NULL)
85831708Sstevel name_addr = "<null>";
85841708Sstevel
85851708Sstevel prom_printf("\t%s %s@%s (aka %s)\n",
858611066Srafael.vanoni@sun.com suspend ? "suspending" : "resuming",
858711066Srafael.vanoni@sun.com name, name_addr, aka);
85881708Sstevel
85891708Sstevel if (suspend) {
85901708Sstevel rv = devi_detach(dip, DDI_SUSPEND);
85911708Sstevel } else {
85921708Sstevel rv = devi_attach(dip, DDI_RESUME);
85931708Sstevel }
85941708Sstevel
85951708Sstevel if (rv != DDI_SUCCESS) {
85961708Sstevel prom_printf("\tFAILED to %s %s@%s\n",
859711066Srafael.vanoni@sun.com suspend ? "suspend" : "resume",
859811066Srafael.vanoni@sun.com name, name_addr);
85991708Sstevel }
86001708Sstevel }
86011708Sstevel
86021708Sstevel void
drmach_suspend_last()86031708Sstevel drmach_suspend_last()
86041708Sstevel {
86051708Sstevel struct drmach_sr_ordered *op;
86061708Sstevel
86071708Sstevel if (drmach_slot1_pause_debug)
86081708Sstevel drmach_slot1_pause_add_io(drmach_slot1_paused);
86091708Sstevel
86101708Sstevel /*
86111708Sstevel * The ordering array declares the strict sequence in which
86121708Sstevel * the named drivers are to suspended. Each element in
86131708Sstevel * the array may have a double-linked ring list of driver
86141708Sstevel * instances (dip) in the order in which they were presented
86151708Sstevel * to drmach_verify_sr. If present, walk the list in the
86161708Sstevel * forward direction to suspend each instance.
86171708Sstevel */
86181708Sstevel for (op = drmach_sr_ordered; op->name; op++) {
86191708Sstevel if (op->ring) {
86201708Sstevel struct drmach_sr_list *rp;
86211708Sstevel
86221708Sstevel rp = op->ring;
86231708Sstevel do {
86241708Sstevel drmach_sr_dip(rp->dip, 1);
86251708Sstevel rp = rp->next;
86261708Sstevel } while (rp != op->ring);
86271708Sstevel }
86281708Sstevel }
86291708Sstevel
86301708Sstevel if (drmach_slot1_pause_debug) {
86311708Sstevel drmach_slot1_pause_update(drmach_slot1_paused,
86321708Sstevel DRMACH_POST_SUSPEND);
86331708Sstevel drmach_slot1_pause_verify(drmach_slot1_paused,
86341708Sstevel DRMACH_POST_SUSPEND);
86351708Sstevel }
86361708Sstevel }
86371708Sstevel
86381708Sstevel void
drmach_resume_first()86391708Sstevel drmach_resume_first()
86401708Sstevel {
86411708Sstevel struct drmach_sr_ordered *op = drmach_sr_ordered +
864211066Srafael.vanoni@sun.com (sizeof (drmach_sr_ordered) / sizeof (drmach_sr_ordered[0]));
86431708Sstevel
86441708Sstevel if (drmach_slot1_pause_debug) {
86451708Sstevel drmach_slot1_pause_update(drmach_slot1_paused,
86461708Sstevel DRMACH_PRE_RESUME);
86471708Sstevel drmach_slot1_pause_verify(drmach_slot1_paused,
86481708Sstevel DRMACH_PRE_RESUME);
86491708Sstevel }
86501708Sstevel
86511708Sstevel op -= 1; /* point at terminating element */
86521708Sstevel
86531708Sstevel /*
86541708Sstevel * walk ordering array and rings backwards to resume dips
86551708Sstevel * in reverse order in which they were suspended
86561708Sstevel */
86571708Sstevel while (--op >= drmach_sr_ordered) {
86581708Sstevel if (op->ring) {
86591708Sstevel struct drmach_sr_list *rp;
86601708Sstevel
86611708Sstevel rp = op->ring->prev;
86621708Sstevel do {
86631708Sstevel drmach_sr_dip(rp->dip, 0);
86641708Sstevel rp = rp->prev;
86651708Sstevel } while (rp != op->ring->prev);
86661708Sstevel }
86671708Sstevel }
86681708Sstevel }
86691708Sstevel
86701708Sstevel /*
86711708Sstevel * Log a DR sysevent.
86721708Sstevel * Return value: 0 success, non-zero failure.
86731708Sstevel */
86741708Sstevel int
drmach_log_sysevent(int board,char * hint,int flag,int verbose)86751708Sstevel drmach_log_sysevent(int board, char *hint, int flag, int verbose)
86761708Sstevel {
86771708Sstevel sysevent_t *ev;
86781708Sstevel sysevent_id_t eid;
86791708Sstevel int rv, km_flag;
86801708Sstevel sysevent_value_t evnt_val;
86811708Sstevel sysevent_attr_list_t *evnt_attr_list = NULL;
86821708Sstevel char attach_pnt[MAXNAMELEN];
86831708Sstevel
86841708Sstevel km_flag = (flag == SE_SLEEP) ? KM_SLEEP : KM_NOSLEEP;
86851708Sstevel attach_pnt[0] = '\0';
86861708Sstevel if (drmach_board_name(board, attach_pnt, MAXNAMELEN)) {
86871708Sstevel rv = -1;
86881708Sstevel goto logexit;
86891708Sstevel }
86901708Sstevel if (verbose)
86911708Sstevel DRMACH_PR("drmach_log_sysevent: %s %s, flag: %d, verbose: %d\n",
869211066Srafael.vanoni@sun.com attach_pnt, hint, flag, verbose);
86931708Sstevel
86941708Sstevel if ((ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE,
869511066Srafael.vanoni@sun.com SUNW_KERN_PUB"dr", km_flag)) == NULL) {
86961708Sstevel rv = -2;
86971708Sstevel goto logexit;
86981708Sstevel }
86991708Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
87001708Sstevel evnt_val.value.sv_string = attach_pnt;
87011708Sstevel if ((rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID,
870211066Srafael.vanoni@sun.com &evnt_val, km_flag)) != 0)
87031708Sstevel goto logexit;
87041708Sstevel
87051708Sstevel evnt_val.value_type = SE_DATA_TYPE_STRING;
87061708Sstevel evnt_val.value.sv_string = hint;
87071708Sstevel if ((rv = sysevent_add_attr(&evnt_attr_list, DR_HINT,
870811066Srafael.vanoni@sun.com &evnt_val, km_flag)) != 0) {
87091708Sstevel sysevent_free_attr(evnt_attr_list);
87101708Sstevel goto logexit;
87111708Sstevel }
87121708Sstevel
87131708Sstevel (void) sysevent_attach_attributes(ev, evnt_attr_list);
87141708Sstevel
87151708Sstevel /*
87161708Sstevel * Log the event but do not sleep waiting for its
87171708Sstevel * delivery. This provides insulation from syseventd.
87181708Sstevel */
87191708Sstevel rv = log_sysevent(ev, SE_NOSLEEP, &eid);
87201708Sstevel
87211708Sstevel logexit:
87221708Sstevel if (ev)
87231708Sstevel sysevent_free(ev);
87241708Sstevel if ((rv != 0) && verbose)
87251708Sstevel cmn_err(CE_WARN,
872611066Srafael.vanoni@sun.com "drmach_log_sysevent failed (rv %d) for %s %s\n",
872711066Srafael.vanoni@sun.com rv, attach_pnt, hint);
87281708Sstevel
87291708Sstevel return (rv);
87301708Sstevel }
87311708Sstevel
87321708Sstevel /*
87331708Sstevel * Initialize the mem_slice portion of a claim/unconfig/unclaim mailbox message.
87341708Sstevel * Only the valid entries are modified, so the array should be zeroed out
87351708Sstevel * initially.
87361708Sstevel */
87371708Sstevel static void
drmach_msg_memslice_init(dr_memslice_t slice_arr[])87381708Sstevel drmach_msg_memslice_init(dr_memslice_t slice_arr[]) {
87391708Sstevel int i;
87401708Sstevel char c;
87411708Sstevel
87421708Sstevel ASSERT(mutex_owned(&drmach_slice_table_lock));
87431708Sstevel
87441708Sstevel for (i = 0; i < AXQ_MAX_EXP; i++) {
87451708Sstevel c = drmach_slice_table[i];
87461708Sstevel
87471708Sstevel if (c & 0x20) {
87481708Sstevel slice_arr[i].valid = 1;
87491708Sstevel slice_arr[i].slice = c & 0x1f;
87501708Sstevel }
87511708Sstevel }
87521708Sstevel }
87531708Sstevel
87541708Sstevel /*
87551708Sstevel * Initialize the mem_regs portion of a claim/unconfig/unclaim mailbox message.
87561708Sstevel * Only the valid entries are modified, so the array should be zeroed out
87571708Sstevel * initially.
87581708Sstevel */
87591708Sstevel static void
drmach_msg_memregs_init(dr_memregs_t regs_arr[])87601708Sstevel drmach_msg_memregs_init(dr_memregs_t regs_arr[]) {
87611708Sstevel int rv, exp, mcnum, bank;
87621708Sstevel uint64_t madr;
87631708Sstevel drmachid_t id;
87641708Sstevel drmach_board_t *bp;
87651708Sstevel drmach_mem_t *mp;
87661708Sstevel dr_memregs_t *memregs;
87671708Sstevel
87681708Sstevel /* CONSTCOND */
87691708Sstevel ASSERT(DRMACH_MC_NBANKS == (PMBANKS_PER_PORT * LMBANKS_PER_PMBANK));
87701708Sstevel
87711708Sstevel for (exp = 0; exp < 18; exp++) {
87721708Sstevel rv = drmach_array_get(drmach_boards,
87731708Sstevel DRMACH_EXPSLOT2BNUM(exp, 0), &id);
87741708Sstevel ASSERT(rv == 0); /* should never be out of bounds */
87751708Sstevel if (id == NULL) {
87761708Sstevel continue;
87771708Sstevel }
87781708Sstevel
87791708Sstevel memregs = ®s_arr[exp];
87801708Sstevel bp = (drmach_board_t *)id;
87811708Sstevel for (mp = bp->mem; mp != NULL; mp = mp->next) {
87821708Sstevel mcnum = mp->dev.portid & 0x3;
87831708Sstevel for (bank = 0; bank < DRMACH_MC_NBANKS; bank++) {
87841708Sstevel drmach_mem_read_madr(mp, bank, &madr);
87851708Sstevel if (madr & DRMACH_MC_VALID_MASK) {
87861708Sstevel DRMACH_PR("%d.%d.%d.madr = 0x%lx\n",
878711066Srafael.vanoni@sun.com exp, mcnum, bank, madr);
87881708Sstevel memregs->madr[mcnum][bank].hi =
87891708Sstevel DRMACH_U64_TO_MCREGHI(madr);
87901708Sstevel memregs->madr[mcnum][bank].lo =
87911708Sstevel DRMACH_U64_TO_MCREGLO(madr);
87921708Sstevel }
87931708Sstevel }
87941708Sstevel }
87951708Sstevel }
87961708Sstevel }
87971708Sstevel
87981708Sstevel /*
87991708Sstevel * Do not allow physical address range modification if either board on this
88001708Sstevel * expander has processors in NULL LPA mode (CBASE=CBND=NULL).
88011708Sstevel *
88021708Sstevel * A side effect of NULL proc LPA mode in Starcat SSM is that local reads will
88031708Sstevel * install the cache line as owned/dirty as a result of the RTSR transaction.
88041708Sstevel * See section 5.2.3 of the Safari spec. All processors will read the bus sync
88051708Sstevel * list before the rename after flushing local caches. When copy-rename
88061708Sstevel * requires changing the physical address ranges (i.e. smaller memory target),
88071708Sstevel * the bus sync list contains physical addresses that will not exist after the
88081708Sstevel * rename. If these cache lines are owned due to a RTSR, a system error can
88091708Sstevel * occur following the rename when these cache lines are evicted and a writeback
88101708Sstevel * is attempted.
88111708Sstevel *
88121708Sstevel * Incoming parameter represents either the copy-rename source or a candidate
88131708Sstevel * target memory board. On Starcat, only slot0 boards may have memory.
88141708Sstevel */
88151708Sstevel int
drmach_allow_memrange_modify(drmachid_t s0id)88161708Sstevel drmach_allow_memrange_modify(drmachid_t s0id)
88171708Sstevel {
88181708Sstevel drmach_board_t *s0bp, *s1bp;
88191708Sstevel drmachid_t s1id;
88201708Sstevel int rv;
88211708Sstevel
88221708Sstevel s0bp = s0id;
88231708Sstevel
88241708Sstevel ASSERT(DRMACH_IS_BOARD_ID(s0id));
88251708Sstevel ASSERT(DRMACH_BNUM2SLOT(s0bp->bnum) == 0);
88261708Sstevel
88271708Sstevel if (s0bp->flags & DRMACH_NULL_PROC_LPA) {
88281708Sstevel /*
88291708Sstevel * This is reason enough to fail the request, no need
88301708Sstevel * to check the device list for cpus.
88311708Sstevel */
88321708Sstevel return (0);
88331708Sstevel }
88341708Sstevel
88351708Sstevel /*
88361708Sstevel * Check for MCPU board on the same expander.
88371708Sstevel *
88381708Sstevel * The board flag DRMACH_NULL_PROC_LPA can be set for all board
88391708Sstevel * types, as it is derived at from the POST gdcd board flag
88401708Sstevel * L1SSFLG_THIS_L1_NULL_PROC_LPA, which can be set (and should be
88411708Sstevel * ignored) for boards with no processors. Since NULL proc LPA
88421708Sstevel * applies only to processors, we walk the devices array to detect
88431708Sstevel * MCPUs.
88441708Sstevel */
88451708Sstevel rv = drmach_array_get(drmach_boards, s0bp->bnum + 1, &s1id);
88461708Sstevel s1bp = s1id;
88471708Sstevel if (rv == 0 && s1bp != NULL) {
88481708Sstevel
88491708Sstevel ASSERT(DRMACH_IS_BOARD_ID(s1id));
88501708Sstevel ASSERT(DRMACH_BNUM2SLOT(s1bp->bnum) == 1);
88511708Sstevel ASSERT(DRMACH_BNUM2EXP(s0bp->bnum) ==
88521708Sstevel DRMACH_BNUM2EXP(s1bp->bnum));
88531708Sstevel
88541708Sstevel if ((s1bp->flags & DRMACH_NULL_PROC_LPA) &&
88551708Sstevel s1bp->devices != NULL) {
88561708Sstevel int d_idx;
88571708Sstevel drmachid_t d_id;
88581708Sstevel
88591708Sstevel rv = drmach_array_first(s1bp->devices, &d_idx, &d_id);
88601708Sstevel while (rv == 0) {
88611708Sstevel if (DRMACH_IS_CPU_ID(d_id)) {
88621708Sstevel /*
88631708Sstevel * Fail MCPU in NULL LPA mode.
88641708Sstevel */
88651708Sstevel return (0);
88661708Sstevel }
88671708Sstevel
88681708Sstevel rv = drmach_array_next(s1bp->devices, &d_idx,
88691708Sstevel &d_id);
88701708Sstevel }
88711708Sstevel }
88721708Sstevel }
88731708Sstevel
88741708Sstevel return (1);
88751708Sstevel }
8876