129949e86Sstevel /* 229949e86Sstevel * CDDL HEADER START 329949e86Sstevel * 429949e86Sstevel * The contents of this file are subject to the terms of the 529949e86Sstevel * Common Development and Distribution License (the "License"). 629949e86Sstevel * You may not use this file except in compliance with the License. 729949e86Sstevel * 829949e86Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 929949e86Sstevel * or http://www.opensolaris.org/os/licensing. 1029949e86Sstevel * See the License for the specific language governing permissions 1129949e86Sstevel * and limitations under the License. 1229949e86Sstevel * 1329949e86Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1429949e86Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1529949e86Sstevel * If applicable, add the following below this CDDL HEADER, with the 1629949e86Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1729949e86Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1829949e86Sstevel * 1929949e86Sstevel * CDDL HEADER END 2029949e86Sstevel */ 2129949e86Sstevel 2229949e86Sstevel /* 2307d06da5SSurya Prakki * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2429949e86Sstevel * Use is subject to license terms. 25*89b43686SBayard Bell * Copyright (c) 2011 Bayard G. Bell. All rights reserved. 2629949e86Sstevel */ 2729949e86Sstevel 2829949e86Sstevel 2929949e86Sstevel #include <sys/types.h> 3029949e86Sstevel #include <sys/conf.h> 3129949e86Sstevel #include <sys/ddi.h> 3229949e86Sstevel #include <sys/sunddi.h> 3329949e86Sstevel #include <sys/ddi_impldefs.h> 3429949e86Sstevel #include <sys/obpdefs.h> 3529949e86Sstevel #include <sys/cmn_err.h> 3629949e86Sstevel #include <sys/errno.h> 3729949e86Sstevel #include <sys/kmem.h> 3829949e86Sstevel #include <sys/debug.h> 3929949e86Sstevel #include <sys/sysmacros.h> 4029949e86Sstevel #include <sys/machsystm.h> 4129949e86Sstevel #include <vm/hat_sfmmu.h> 4229949e86Sstevel #include <sys/autoconf.h> 4329949e86Sstevel #include <sys/open.h> 4429949e86Sstevel #include <sys/stat.h> 4529949e86Sstevel #include <sys/modctl.h> 4629949e86Sstevel #include <sys/fhc.h> 4729949e86Sstevel #include <sys/ac.h> 4829949e86Sstevel #include <sys/cpu_module.h> 4929949e86Sstevel #include <sys/x_call.h> 5029949e86Sstevel #include <sys/fpu/fpusystm.h> 5129949e86Sstevel #include <sys/lgrp.h> 5229949e86Sstevel 5329949e86Sstevel /* Useful debugging Stuff */ 5429949e86Sstevel #include <sys/nexusdebug.h> 5529949e86Sstevel 5629949e86Sstevel /* 5729949e86Sstevel * Function prototypes 5829949e86Sstevel */ 5929949e86Sstevel 6029949e86Sstevel static int ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 6129949e86Sstevel void **result); 6229949e86Sstevel static int ac_attach(dev_info_t *, ddi_attach_cmd_t); 6329949e86Sstevel static int ac_detach(dev_info_t *, ddi_detach_cmd_t); 6429949e86Sstevel static int ac_open(dev_t *, int, int, cred_t *); 6529949e86Sstevel static int ac_close(dev_t, int, int, cred_t *); 6629949e86Sstevel static int ac_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 6729949e86Sstevel 6829949e86Sstevel static void ac_add_kstats(struct ac_soft_state *); 6929949e86Sstevel static void ac_del_kstats(struct ac_soft_state *); 7029949e86Sstevel static int ac_misc_kstat_update(kstat_t *, int); 7129949e86Sstevel static void ac_add_picN_kstats(dev_info_t *dip); 7229949e86Sstevel static int ac_counters_kstat_update(kstat_t *, int); 7329949e86Sstevel static void ac_get_memory_status(struct ac_soft_state *, enum ac_bank_id); 7429949e86Sstevel static void ac_eval_memory_status(struct ac_soft_state *, enum ac_bank_id); 7529949e86Sstevel static void ac_ecache_flush(uint64_t, uint64_t); 7629949e86Sstevel static int ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag); 7729949e86Sstevel static int ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag); 7829949e86Sstevel static int ac_reset_timeout(int rw); 7929949e86Sstevel static void ac_timeout(void *); 8029949e86Sstevel static int ac_enter_transition(void); 8129949e86Sstevel static void ac_exit_transition(void); 8229949e86Sstevel 8329949e86Sstevel 8429949e86Sstevel int ac_add_memory(ac_cfga_pkt_t *); 8529949e86Sstevel int ac_del_memory(ac_cfga_pkt_t *); 8629949e86Sstevel int ac_mem_stat(ac_cfga_pkt_t *, int); 8729949e86Sstevel int ac_mem_test_start(ac_cfga_pkt_t *, int); 8829949e86Sstevel int ac_mem_test_stop(ac_cfga_pkt_t *, int); 8929949e86Sstevel int ac_mem_test_read(ac_cfga_pkt_t *, int); 9029949e86Sstevel int ac_mem_test_write(ac_cfga_pkt_t *, int); 9129949e86Sstevel void ac_mem_test_stop_on_close(uint_t, uint_t); 9229949e86Sstevel /* 9329949e86Sstevel * ac audit message events 9429949e86Sstevel */ 9529949e86Sstevel typedef enum { 9629949e86Sstevel AC_AUDIT_OSTATE_CONFIGURE, 9729949e86Sstevel AC_AUDIT_OSTATE_UNCONFIGURE, 9829949e86Sstevel AC_AUDIT_OSTATE_SUCCEEDED, 9929949e86Sstevel AC_AUDIT_OSTATE_CONFIGURE_FAILED, 10029949e86Sstevel AC_AUDIT_OSTATE_UNCONFIGURE_FAILED 10129949e86Sstevel } ac_audit_evt_t; 10229949e86Sstevel static void ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt); 10329949e86Sstevel static char *ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event); 10429949e86Sstevel 10529949e86Sstevel /* The memory ioctl interface version of this driver. */ 10629949e86Sstevel static ac_mem_version_t ac_mem_version = AC_MEM_ADMIN_VERSION; 10729949e86Sstevel 10829949e86Sstevel static int ac_mem_exercise(ac_cfga_pkt_t *, int); 10929949e86Sstevel 11029949e86Sstevel /* 11129949e86Sstevel * Configuration data structures 11229949e86Sstevel */ 11329949e86Sstevel static struct cb_ops ac_cb_ops = { 11429949e86Sstevel ac_open, /* open */ 11529949e86Sstevel ac_close, /* close */ 11629949e86Sstevel nulldev, /* strategy */ 11729949e86Sstevel nulldev, /* print */ 11829949e86Sstevel nodev, /* dump */ 11929949e86Sstevel nulldev, /* read */ 12029949e86Sstevel nulldev, /* write */ 12129949e86Sstevel ac_ioctl, /* ioctl */ 12229949e86Sstevel nodev, /* devmap */ 12329949e86Sstevel nodev, /* mmap */ 12429949e86Sstevel nodev, /* segmap */ 12529949e86Sstevel nochpoll, /* poll */ 12629949e86Sstevel ddi_prop_op, /* cb_prop_op */ 12729949e86Sstevel 0, /* streamtab */ 12829949e86Sstevel D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */ 12929949e86Sstevel CB_REV, /* rev */ 13029949e86Sstevel nodev, /* cb_aread */ 13129949e86Sstevel nodev /* cb_awrite */ 13229949e86Sstevel }; 13329949e86Sstevel 13429949e86Sstevel static struct dev_ops ac_ops = { 13529949e86Sstevel DEVO_REV, /* devo_rev, */ 13629949e86Sstevel 0, /* refcnt */ 13729949e86Sstevel ac_info, /* getinfo */ 13829949e86Sstevel nulldev, /* identify */ 13929949e86Sstevel nulldev, /* probe */ 14029949e86Sstevel ac_attach, /* attach */ 14129949e86Sstevel ac_detach, /* detach */ 14229949e86Sstevel nulldev, /* reset */ 14329949e86Sstevel &ac_cb_ops, /* cb_ops */ 14429949e86Sstevel (struct bus_ops *)0, /* bus_ops */ 14519397407SSherry Moore nulldev, /* power */ 14619397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 14729949e86Sstevel }; 14829949e86Sstevel 14929949e86Sstevel /* 15029949e86Sstevel * Driver globals 15129949e86Sstevel */ 15229949e86Sstevel void *acp; /* ac soft state hook */ 15329949e86Sstevel static kstat_t *ac_picN_ksp[AC_NUM_PICS]; /* performance picN kstats */ 15429949e86Sstevel static int ac_attachcnt = 0; /* number of instances attached */ 15529949e86Sstevel static kmutex_t ac_attachcnt_mutex; /* ac_attachcnt lock - attach/detach */ 15629949e86Sstevel static kmutex_t ac_hot_plug_mode_mutex; 15729949e86Sstevel static timeout_id_t ac_hot_plug_timeout; 15829949e86Sstevel static int ac_hot_plug_timeout_interval = 10; 15929949e86Sstevel 16029949e86Sstevel #define AC_GETSOFTC(I) \ 16129949e86Sstevel ((struct ac_soft_state *)ddi_get_soft_state(acp, (I))) 16229949e86Sstevel 16329949e86Sstevel extern struct mod_ops mod_driverops; 16429949e86Sstevel 16529949e86Sstevel static struct modldrv modldrv = { 16629949e86Sstevel &mod_driverops, /* Type of module. This one is a driver */ 16719397407SSherry Moore "AC Leaf", /* name of module */ 16829949e86Sstevel &ac_ops, /* driver ops */ 16929949e86Sstevel }; 17029949e86Sstevel 17129949e86Sstevel static struct modlinkage modlinkage = { 17229949e86Sstevel MODREV_1, 17329949e86Sstevel (void *)&modldrv, 17429949e86Sstevel NULL 17529949e86Sstevel }; 17629949e86Sstevel 17729949e86Sstevel /* 17829949e86Sstevel * These are the module initialization routines. 17929949e86Sstevel */ 18029949e86Sstevel 18129949e86Sstevel int 18229949e86Sstevel _init(void) 18329949e86Sstevel { 18429949e86Sstevel int error; 18529949e86Sstevel 18629949e86Sstevel if ((error = ddi_soft_state_init(&acp, sizeof (struct ac_soft_state), 18729949e86Sstevel 1)) != 0) 18829949e86Sstevel return (error); 18929949e86Sstevel 19029949e86Sstevel if ((error = mod_install(&modlinkage)) != 0) { 19129949e86Sstevel ddi_soft_state_fini(&acp); 19229949e86Sstevel return (error); 19329949e86Sstevel } 19429949e86Sstevel /* Initialize global mutex */ 19529949e86Sstevel mutex_init(&ac_attachcnt_mutex, NULL, MUTEX_DRIVER, NULL); 19629949e86Sstevel mutex_init(&ac_hot_plug_mode_mutex, NULL, MUTEX_DRIVER, NULL); 19729949e86Sstevel return (0); 19829949e86Sstevel } 19929949e86Sstevel 20029949e86Sstevel int 20129949e86Sstevel _fini(void) 20229949e86Sstevel { 20329949e86Sstevel int error; 20429949e86Sstevel 20529949e86Sstevel if ((error = mod_remove(&modlinkage)) == 0) { 20629949e86Sstevel ddi_soft_state_fini(&acp); 20729949e86Sstevel mutex_destroy(&ac_attachcnt_mutex); 20829949e86Sstevel mutex_destroy(&ac_hot_plug_mode_mutex); 20929949e86Sstevel } 21029949e86Sstevel return (error); 21129949e86Sstevel } 21229949e86Sstevel 21329949e86Sstevel int 21429949e86Sstevel _info(struct modinfo *modinfop) 21529949e86Sstevel { 21629949e86Sstevel return (mod_info(&modlinkage, modinfop)); 21729949e86Sstevel } 21829949e86Sstevel 21929949e86Sstevel /* ARGSUSED */ 22029949e86Sstevel static int 22129949e86Sstevel ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 22229949e86Sstevel { 22329949e86Sstevel dev_t dev; 22429949e86Sstevel int instance; 22529949e86Sstevel 22629949e86Sstevel if (infocmd == DDI_INFO_DEVT2INSTANCE) { 22729949e86Sstevel dev = (dev_t)arg; 22829949e86Sstevel instance = AC_GETINSTANCE(getminor(dev)); 22929949e86Sstevel *result = (void *)(uintptr_t)instance; 23029949e86Sstevel return (DDI_SUCCESS); 23129949e86Sstevel } 23229949e86Sstevel return (DDI_FAILURE); 23329949e86Sstevel } 23429949e86Sstevel 23529949e86Sstevel static int 23629949e86Sstevel ac_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 23729949e86Sstevel { 23829949e86Sstevel int instance; 23929949e86Sstevel struct ac_soft_state *softsp; 24029949e86Sstevel struct bd_list *list = NULL; 24129949e86Sstevel 24229949e86Sstevel switch (cmd) { 24329949e86Sstevel case DDI_ATTACH: 24429949e86Sstevel break; 24529949e86Sstevel 24629949e86Sstevel case DDI_RESUME: 24729949e86Sstevel return (DDI_SUCCESS); 24829949e86Sstevel 24929949e86Sstevel default: 25029949e86Sstevel return (DDI_FAILURE); 25129949e86Sstevel } 25229949e86Sstevel 25329949e86Sstevel instance = ddi_get_instance(devi); 25429949e86Sstevel 25529949e86Sstevel if (ddi_soft_state_zalloc(acp, instance) != DDI_SUCCESS) { 25629949e86Sstevel cmn_err(CE_WARN, "ddi_soft_state_zalloc failed for ac%d", 25729949e86Sstevel instance); 25829949e86Sstevel return (DDI_FAILURE); 25929949e86Sstevel } 26029949e86Sstevel 26129949e86Sstevel softsp = ddi_get_soft_state(acp, instance); 26229949e86Sstevel 26329949e86Sstevel /* Set the dip in the soft state */ 26429949e86Sstevel softsp->dip = devi; 26529949e86Sstevel 26629949e86Sstevel /* Get the board number from this nodes parent */ 26729949e86Sstevel softsp->pdip = ddi_get_parent(softsp->dip); 26829949e86Sstevel if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip, 26929949e86Sstevel DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) { 27029949e86Sstevel cmn_err(CE_WARN, "ac%d: unable to retrieve %s property", 27129949e86Sstevel instance, OBP_BOARDNUM); 27229949e86Sstevel goto bad; 27329949e86Sstevel } 27429949e86Sstevel 27529949e86Sstevel DPRINTF(AC_ATTACH_DEBUG, ("ac%d: devi= 0x%p\n," 27607d06da5SSurya Prakki " softsp=0x%p\n", instance, (void *)devi, (void *)softsp)); 27729949e86Sstevel 27829949e86Sstevel /* map in the registers for this device. */ 27929949e86Sstevel if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0)) { 28029949e86Sstevel cmn_err(CE_WARN, "ac%d: unable to map registers", instance); 28129949e86Sstevel goto bad; 28229949e86Sstevel } 28329949e86Sstevel 28429949e86Sstevel /* Setup the pointers to the hardware registers */ 28529949e86Sstevel softsp->ac_id = (uint32_t *)softsp->ac_base; 28629949e86Sstevel softsp->ac_memctl = (uint64_t *)((char *)softsp->ac_base + 28729949e86Sstevel AC_OFF_MEMCTL); 28829949e86Sstevel softsp->ac_memdecode0 = (uint64_t *)((char *)softsp->ac_base + 28929949e86Sstevel AC_OFF_MEMDEC0); 29029949e86Sstevel softsp->ac_memdecode1 = (uint64_t *)((char *)softsp->ac_base + 29129949e86Sstevel AC_OFF_MEMDEC1); 29229949e86Sstevel softsp->ac_counter = (uint64_t *)((char *)softsp->ac_base + 29329949e86Sstevel AC_OFF_CNTR); 29429949e86Sstevel softsp->ac_mccr = (uint32_t *)((char *)softsp->ac_base + 29529949e86Sstevel AC_OFF_MCCR); 29629949e86Sstevel 29729949e86Sstevel /* nothing to suspend/resume here */ 29829949e86Sstevel (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi, 29929949e86Sstevel "pm-hardware-state", "no-suspend-resume"); 30029949e86Sstevel 30129949e86Sstevel /* setup the the AC counter registers to allow for hotplug. */ 30229949e86Sstevel list = fhc_bdlist_lock(softsp->board); 30329949e86Sstevel 30429949e86Sstevel if (list == NULL) { 30529949e86Sstevel cmn_err(CE_PANIC, "ac%d: Board %d not found in database", 30629949e86Sstevel instance, softsp->board); 30729949e86Sstevel } 30829949e86Sstevel 30929949e86Sstevel /* set the AC rev into the bd list structure */ 31029949e86Sstevel list->sc.ac_compid = *softsp->ac_id; 31129949e86Sstevel 31229949e86Sstevel list->ac_softsp = softsp; 31329949e86Sstevel 31429949e86Sstevel if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) { 31529949e86Sstevel /* Create the minor nodes */ 31629949e86Sstevel if (ddi_create_minor_node(devi, NAME_BANK0, S_IFCHR, 31729949e86Sstevel (AC_PUTINSTANCE(instance) | 0), 31829949e86Sstevel DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) { 31929949e86Sstevel cmn_err(CE_WARN, "ac%d: \"%s\" " 32029949e86Sstevel "ddi_create_minor_node failed", instance, 32129949e86Sstevel NAME_BANK0); 32229949e86Sstevel } 32329949e86Sstevel if (ddi_create_minor_node(devi, NAME_BANK1, S_IFCHR, 32429949e86Sstevel (AC_PUTINSTANCE(instance) | 1), 32529949e86Sstevel DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) { 32629949e86Sstevel cmn_err(CE_WARN, "ac%d: \"%s\" " 32729949e86Sstevel "ddi_create_minor_node failed", instance, 32829949e86Sstevel NAME_BANK0); 32929949e86Sstevel } 33029949e86Sstevel 33129949e86Sstevel /* purge previous fhc pa database entries */ 33229949e86Sstevel fhc_del_memloc(softsp->board); 33329949e86Sstevel 33429949e86Sstevel /* Inherit Memory Bank Status */ 33529949e86Sstevel ac_get_memory_status(softsp, Bank0); 33629949e86Sstevel ac_get_memory_status(softsp, Bank1); 33729949e86Sstevel /* Final Memory Bank Status evaluation and messaging */ 33829949e86Sstevel ac_eval_memory_status(softsp, Bank0); 33929949e86Sstevel ac_eval_memory_status(softsp, Bank1); 34029949e86Sstevel } 34129949e86Sstevel 34229949e86Sstevel fhc_bdlist_unlock(); 34329949e86Sstevel 34429949e86Sstevel /* create the kstats for this device. */ 34529949e86Sstevel ac_add_kstats(softsp); 34629949e86Sstevel 34729949e86Sstevel ddi_report_dev(devi); 34829949e86Sstevel 34929949e86Sstevel return (DDI_SUCCESS); 35029949e86Sstevel 35129949e86Sstevel bad: 35229949e86Sstevel ddi_soft_state_free(acp, instance); 35329949e86Sstevel return (DDI_FAILURE); 35429949e86Sstevel } 35529949e86Sstevel 35629949e86Sstevel /* ARGSUSED */ 35729949e86Sstevel static int 35829949e86Sstevel ac_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 35929949e86Sstevel { 36029949e86Sstevel int instance; 36129949e86Sstevel struct ac_soft_state *softsp; 36229949e86Sstevel struct bd_list *list; 36329949e86Sstevel 36429949e86Sstevel /* get the instance of this devi */ 36529949e86Sstevel instance = ddi_get_instance(devi); 36629949e86Sstevel 36729949e86Sstevel /* get the soft state pointer for this device node */ 36829949e86Sstevel softsp = ddi_get_soft_state(acp, instance); 36929949e86Sstevel 37029949e86Sstevel switch (cmd) { 37129949e86Sstevel case DDI_SUSPEND: 37229949e86Sstevel return (DDI_SUCCESS); 37329949e86Sstevel 37429949e86Sstevel case DDI_DETACH: 37529949e86Sstevel list = fhc_bdlist_lock(softsp->board); 37629949e86Sstevel 37729949e86Sstevel if (fhc_bd_detachable(softsp->board)) 37829949e86Sstevel break; 37929949e86Sstevel else 38029949e86Sstevel fhc_bdlist_unlock(); 38129949e86Sstevel /* FALLTHROUGH */ 38229949e86Sstevel 38329949e86Sstevel default: 38429949e86Sstevel return (DDI_FAILURE); 38529949e86Sstevel } 38629949e86Sstevel 38729949e86Sstevel ASSERT(list->ac_softsp == softsp); 38829949e86Sstevel 38929949e86Sstevel if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) { 39029949e86Sstevel int cpui; 39129949e86Sstevel 39229949e86Sstevel /* 39329949e86Sstevel * Test to see if memory is in use on a CPU/MEM board. 39429949e86Sstevel * In the case of a DR operation this condition 39529949e86Sstevel * will have been assured when the board was unconfigured. 39629949e86Sstevel */ 39729949e86Sstevel if (softsp->bank[Bank0].busy != 0 || 39829949e86Sstevel softsp->bank[Bank0].ostate == SYSC_CFGA_OSTATE_CONFIGURED || 39929949e86Sstevel softsp->bank[Bank1].busy != 0 || 40029949e86Sstevel softsp->bank[Bank1].ostate == SYSC_CFGA_OSTATE_CONFIGURED) { 40129949e86Sstevel fhc_bdlist_unlock(); 40229949e86Sstevel return (DDI_FAILURE); 40329949e86Sstevel } 40429949e86Sstevel /* 40529949e86Sstevel * CPU busy test is done by the DR sequencer before 40629949e86Sstevel * device detach called. 40729949e86Sstevel */ 40829949e86Sstevel 40929949e86Sstevel /* 41029949e86Sstevel * Flush all E-caches to remove references to this 41129949e86Sstevel * board's memory. 41229949e86Sstevel * 41329949e86Sstevel * Do this one CPU at a time to avoid stalls and timeouts 41429949e86Sstevel * due to all CPUs flushing concurrently. 41529949e86Sstevel * xc_one returns silently for non-existant CPUs. 41629949e86Sstevel */ 41729949e86Sstevel for (cpui = 0; cpui < NCPU; cpui++) 41829949e86Sstevel xc_one(cpui, ac_ecache_flush, 0, 0); 41929949e86Sstevel } 42029949e86Sstevel 42129949e86Sstevel list->ac_softsp = NULL; 42229949e86Sstevel 42329949e86Sstevel /* delete the kstat for this driver. */ 42429949e86Sstevel ac_del_kstats(softsp); 42529949e86Sstevel 42629949e86Sstevel /* unmap the registers */ 42729949e86Sstevel ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0); 42829949e86Sstevel 42929949e86Sstevel fhc_bdlist_unlock(); 43029949e86Sstevel 43129949e86Sstevel /* Remove the minor nodes. */ 43229949e86Sstevel ddi_remove_minor_node(devi, NULL); 43329949e86Sstevel 43429949e86Sstevel /* free the soft state structure */ 43529949e86Sstevel ddi_soft_state_free(acp, instance); 43629949e86Sstevel ddi_prop_remove_all(devi); 43729949e86Sstevel 43829949e86Sstevel return (DDI_SUCCESS); 43929949e86Sstevel } 44029949e86Sstevel 44129949e86Sstevel /* ARGSUSED */ 44229949e86Sstevel static int 44329949e86Sstevel ac_open(dev_t *devp, int flag, int otyp, cred_t *credp) 44429949e86Sstevel { 44529949e86Sstevel int instance; 44629949e86Sstevel dev_t dev; 44729949e86Sstevel struct ac_soft_state *softsp; 44829949e86Sstevel struct bd_list *board; 44929949e86Sstevel int vis; 45029949e86Sstevel 45129949e86Sstevel dev = *devp; 45229949e86Sstevel instance = AC_GETINSTANCE(getminor(dev)); 45329949e86Sstevel softsp = AC_GETSOFTC(instance); 45429949e86Sstevel 45529949e86Sstevel /* Is the instance attached? */ 45629949e86Sstevel if (softsp == NULL) { 45729949e86Sstevel #ifdef DEBUG 45829949e86Sstevel cmn_err(CE_WARN, "ac%d device not attached", instance); 45929949e86Sstevel #endif /* DEBUG */ 46029949e86Sstevel return (ENXIO); 46129949e86Sstevel } 46229949e86Sstevel 46329949e86Sstevel /* 46429949e86Sstevel * If the board is not configured, hide the memory APs 46529949e86Sstevel */ 46629949e86Sstevel board = fhc_bdlist_lock(softsp->board); 46729949e86Sstevel vis = (board != NULL) && MEM_BOARD_VISIBLE(board); 46829949e86Sstevel fhc_bdlist_unlock(); 46929949e86Sstevel 47029949e86Sstevel if (!vis) 47129949e86Sstevel return (ENXIO); 47229949e86Sstevel 47329949e86Sstevel /* verify that otyp is appropriate */ 47429949e86Sstevel if (otyp != OTYP_CHR) { 47529949e86Sstevel return (EINVAL); 47629949e86Sstevel } 47729949e86Sstevel 47829949e86Sstevel return (DDI_SUCCESS); 47929949e86Sstevel } 48029949e86Sstevel 48129949e86Sstevel /* ARGSUSED */ 48229949e86Sstevel static int 48329949e86Sstevel ac_close(dev_t devt, int flag, int otyp, cred_t *credp) 48429949e86Sstevel { 48529949e86Sstevel struct ac_soft_state *softsp; 48629949e86Sstevel int instance; 48729949e86Sstevel 48829949e86Sstevel instance = AC_GETINSTANCE(getminor(devt)); 48929949e86Sstevel softsp = AC_GETSOFTC(instance); 49029949e86Sstevel ASSERT(softsp != NULL); 49129949e86Sstevel ac_mem_test_stop_on_close(softsp->board, AC_GETBANK(getminor(devt))); 49229949e86Sstevel return (DDI_SUCCESS); 49329949e86Sstevel } 49429949e86Sstevel 49529949e86Sstevel static int 49629949e86Sstevel ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag) 49729949e86Sstevel { 49829949e86Sstevel #ifdef _MULTI_DATAMODEL 49929949e86Sstevel if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) { 50029949e86Sstevel ac_cfga_cmd32_t ac_cmd32; 50129949e86Sstevel 50229949e86Sstevel if (ddi_copyin((void *)arg, &ac_cmd32, 50329949e86Sstevel sizeof (ac_cfga_cmd32_t), flag) != 0) { 50429949e86Sstevel return (EFAULT); 50529949e86Sstevel } 50629949e86Sstevel pkt->cmd_cfga.force = ac_cmd32.force; 50729949e86Sstevel pkt->cmd_cfga.test = ac_cmd32.test; 50829949e86Sstevel pkt->cmd_cfga.arg = ac_cmd32.arg; 50929949e86Sstevel pkt->cmd_cfga.errtype = ac_cmd32.errtype; 51029949e86Sstevel pkt->cmd_cfga.outputstr = 51129949e86Sstevel (char *)(uintptr_t)ac_cmd32.outputstr; 51229949e86Sstevel pkt->cmd_cfga.private = 51329949e86Sstevel (void *)(uintptr_t)ac_cmd32.private; 51429949e86Sstevel } else 51529949e86Sstevel #endif /* _MULTI_DATAMODEL */ 51629949e86Sstevel if (ddi_copyin((void *)arg, &(pkt->cmd_cfga), 51729949e86Sstevel sizeof (ac_cfga_cmd_t), flag) != 0) { 51829949e86Sstevel return (EFAULT); 51929949e86Sstevel } 52029949e86Sstevel pkt->errbuf = kmem_zalloc(SYSC_OUTPUT_LEN, KM_SLEEP); 52129949e86Sstevel return (0); 52229949e86Sstevel } 52329949e86Sstevel 52429949e86Sstevel static int 52529949e86Sstevel ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag) 52629949e86Sstevel { 52729949e86Sstevel int ret = TRUE; 52829949e86Sstevel 52929949e86Sstevel #ifdef _MULTI_DATAMODEL 53029949e86Sstevel if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) { 53129949e86Sstevel 53229949e86Sstevel if (ddi_copyout(&(pkt->cmd_cfga.errtype), 53329949e86Sstevel (void *)&(((ac_cfga_cmd32_t *)arg)->errtype), 53429949e86Sstevel sizeof (ac_err_t), flag) != 0) { 53529949e86Sstevel ret = FALSE; 53629949e86Sstevel } 53729949e86Sstevel } else 53829949e86Sstevel #endif 53929949e86Sstevel if (ddi_copyout(&(pkt->cmd_cfga.errtype), 54029949e86Sstevel (void *)&(((ac_cfga_cmd_t *)arg)->errtype), 54129949e86Sstevel sizeof (ac_err_t), flag) != 0) { 54229949e86Sstevel ret = FALSE; 54329949e86Sstevel } 54429949e86Sstevel 54529949e86Sstevel if ((ret != FALSE) && ((pkt->cmd_cfga.outputstr != NULL) && 54629949e86Sstevel (ddi_copyout(pkt->errbuf, pkt->cmd_cfga.outputstr, 54729949e86Sstevel SYSC_OUTPUT_LEN, flag) != 0))) { 54829949e86Sstevel ret = FALSE; 54929949e86Sstevel } 55029949e86Sstevel 55129949e86Sstevel kmem_free(pkt->errbuf, SYSC_OUTPUT_LEN); 55229949e86Sstevel return (ret); 55329949e86Sstevel } 55429949e86Sstevel 55529949e86Sstevel /* ARGSUSED */ 55629949e86Sstevel static int 55729949e86Sstevel ac_ioctl( 55829949e86Sstevel dev_t devt, 55929949e86Sstevel int cmd, 56029949e86Sstevel intptr_t arg, 56129949e86Sstevel int flag, 56229949e86Sstevel cred_t *cred_p, 56329949e86Sstevel int *rval_p) 56429949e86Sstevel { 56529949e86Sstevel struct ac_soft_state *softsp; 56629949e86Sstevel ac_cfga_pkt_t cfga_pkt, *pkt; 56729949e86Sstevel int instance; 56829949e86Sstevel int retval; 56929949e86Sstevel 57029949e86Sstevel instance = AC_GETINSTANCE(getminor(devt)); 57129949e86Sstevel softsp = AC_GETSOFTC(instance); 57229949e86Sstevel if (softsp == NULL) { 57329949e86Sstevel #ifdef DEBUG 57429949e86Sstevel cmn_err(CE_NOTE, "ac%d device not attached", instance); 57529949e86Sstevel #endif /* DEBUG */ 57629949e86Sstevel return (ENXIO); 57729949e86Sstevel } 57829949e86Sstevel 57929949e86Sstevel /* 58029949e86Sstevel * Dispose of the easy ones first. 58129949e86Sstevel */ 58229949e86Sstevel switch (cmd) { 58329949e86Sstevel case AC_MEM_ADMIN_VER: 58429949e86Sstevel /* 58529949e86Sstevel * Specify the revision of this ioctl interface driver. 58629949e86Sstevel */ 58729949e86Sstevel if (ddi_copyout(&ac_mem_version, (void *)arg, 58829949e86Sstevel sizeof (ac_mem_version_t), flag) != 0) 58929949e86Sstevel return (EFAULT); 59029949e86Sstevel return (DDI_SUCCESS); 59129949e86Sstevel 59229949e86Sstevel case AC_MEM_CONFIGURE: 59329949e86Sstevel case AC_MEM_UNCONFIGURE: 59429949e86Sstevel case AC_MEM_STAT: 59529949e86Sstevel case AC_MEM_TEST_START: 59629949e86Sstevel case AC_MEM_TEST_STOP: 59729949e86Sstevel case AC_MEM_TEST_READ: 59829949e86Sstevel case AC_MEM_TEST_WRITE: 59929949e86Sstevel case AC_MEM_EXERCISE: 60029949e86Sstevel break; 60129949e86Sstevel 60229949e86Sstevel default: 60329949e86Sstevel return (ENOTTY); 60429949e86Sstevel } 60529949e86Sstevel if (cmd != AC_MEM_STAT && !fpu_exists) { 60629949e86Sstevel return (ENOTSUP); 60729949e86Sstevel } 60829949e86Sstevel 60929949e86Sstevel pkt = &cfga_pkt; 61029949e86Sstevel if ((retval = ac_pkt_init(pkt, arg, flag)) != 0) 61129949e86Sstevel return (retval); 61229949e86Sstevel pkt->softsp = softsp; 61329949e86Sstevel pkt->bank = AC_GETBANK(getminor(devt)); 61429949e86Sstevel 61529949e86Sstevel switch (cmd) { 61629949e86Sstevel case AC_MEM_CONFIGURE: 61729949e86Sstevel if ((flag & FWRITE) == 0) { 61829949e86Sstevel retval = EBADF; 61929949e86Sstevel break; 62029949e86Sstevel } 62129949e86Sstevel 62229949e86Sstevel if (pkt->cmd_cfga.private != NULL) { 62329949e86Sstevel retval = EINVAL; 62429949e86Sstevel break; 62529949e86Sstevel } 62629949e86Sstevel ac_policy_audit_messages(AC_AUDIT_OSTATE_CONFIGURE, pkt); 62729949e86Sstevel retval = ac_add_memory(pkt); 62829949e86Sstevel if (!retval) 62929949e86Sstevel ac_policy_audit_messages( 63029949e86Sstevel AC_AUDIT_OSTATE_SUCCEEDED, pkt); 63129949e86Sstevel else 63229949e86Sstevel ac_policy_audit_messages( 63329949e86Sstevel AC_AUDIT_OSTATE_CONFIGURE_FAILED, pkt); 63429949e86Sstevel break; 63529949e86Sstevel 63629949e86Sstevel case AC_MEM_UNCONFIGURE: 63729949e86Sstevel if ((flag & FWRITE) == 0) { 63829949e86Sstevel retval = EBADF; 63929949e86Sstevel break; 64029949e86Sstevel } 64129949e86Sstevel 64229949e86Sstevel if (pkt->cmd_cfga.private != NULL) { 64329949e86Sstevel retval = EINVAL; 64429949e86Sstevel break; 64529949e86Sstevel } 64629949e86Sstevel ac_policy_audit_messages(AC_AUDIT_OSTATE_UNCONFIGURE, pkt); 64729949e86Sstevel retval = ac_del_memory(pkt); 64829949e86Sstevel if (!retval) { 64929949e86Sstevel ac_policy_audit_messages( 65029949e86Sstevel AC_AUDIT_OSTATE_SUCCEEDED, pkt); 65129949e86Sstevel } else 65229949e86Sstevel ac_policy_audit_messages( 65329949e86Sstevel AC_AUDIT_OSTATE_UNCONFIGURE_FAILED, pkt); 65429949e86Sstevel break; 65529949e86Sstevel 65629949e86Sstevel case AC_MEM_STAT: 65729949e86Sstevel /* 65829949e86Sstevel * Query usage of a bank of memory. 65929949e86Sstevel */ 66029949e86Sstevel retval = ac_mem_stat(pkt, flag); 66129949e86Sstevel break; 66229949e86Sstevel 66329949e86Sstevel case AC_MEM_TEST_START: 66429949e86Sstevel if ((flag & FWRITE) == 0) { 66529949e86Sstevel retval = EBADF; 66629949e86Sstevel break; 66729949e86Sstevel } 66829949e86Sstevel 66929949e86Sstevel retval = ac_mem_test_start(pkt, flag); 67029949e86Sstevel break; 67129949e86Sstevel 67229949e86Sstevel case AC_MEM_TEST_STOP: 67329949e86Sstevel if ((flag & FWRITE) == 0) { 67429949e86Sstevel retval = EBADF; 67529949e86Sstevel break; 67629949e86Sstevel } 67729949e86Sstevel 67829949e86Sstevel retval = ac_mem_test_stop(pkt, flag); 67929949e86Sstevel break; 68029949e86Sstevel 68129949e86Sstevel case AC_MEM_TEST_READ: 68229949e86Sstevel /* 68329949e86Sstevel * read a 'page' (or less) of memory safely. 68429949e86Sstevel */ 68529949e86Sstevel if ((flag & FWRITE) == 0) { 68629949e86Sstevel retval = EBADF; 68729949e86Sstevel break; 68829949e86Sstevel } 68929949e86Sstevel 69029949e86Sstevel retval = ac_mem_test_read(pkt, flag); 69129949e86Sstevel break; 69229949e86Sstevel 69329949e86Sstevel case AC_MEM_TEST_WRITE: 69429949e86Sstevel /* 69529949e86Sstevel * write a 'page' (or less) of memory safely. 69629949e86Sstevel */ 69729949e86Sstevel if ((flag & FWRITE) == 0) { 69829949e86Sstevel retval = EBADF; 69929949e86Sstevel break; 70029949e86Sstevel } 70129949e86Sstevel 70229949e86Sstevel retval = ac_mem_test_write(pkt, flag); 70329949e86Sstevel break; 70429949e86Sstevel 70529949e86Sstevel case AC_MEM_EXERCISE: 70629949e86Sstevel retval = ac_mem_exercise(pkt, flag); 70729949e86Sstevel break; 70829949e86Sstevel 70929949e86Sstevel default: 71029949e86Sstevel ASSERT(0); 71129949e86Sstevel retval = ENOTTY; 71229949e86Sstevel break; 71329949e86Sstevel } 71429949e86Sstevel 71529949e86Sstevel if (ac_pkt_fini(pkt, arg, flag) != TRUE) 71629949e86Sstevel retval = EFAULT; 71729949e86Sstevel 71829949e86Sstevel return (retval); 71929949e86Sstevel } 72029949e86Sstevel 72129949e86Sstevel static void 72229949e86Sstevel ac_add_kstats(struct ac_soft_state *softsp) 72329949e86Sstevel { 72429949e86Sstevel struct kstat *ac_ksp, *ac_counters_ksp; 72529949e86Sstevel struct ac_kstat *ac_named_ksp; 72629949e86Sstevel struct kstat_named *ac_counters_named_data; 72729949e86Sstevel 72829949e86Sstevel /* 72929949e86Sstevel * create the unix-misc kstat for address controller 73029949e86Sstevel * using the board number as the instance. 73129949e86Sstevel */ 73229949e86Sstevel if ((ac_ksp = kstat_create("unix", softsp->board, 73329949e86Sstevel AC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED, 73429949e86Sstevel sizeof (struct ac_kstat) / sizeof (kstat_named_t), 73529949e86Sstevel KSTAT_FLAG_PERSISTENT)) == NULL) { 73629949e86Sstevel cmn_err(CE_WARN, "ac%d: kstat_create failed", 73729949e86Sstevel ddi_get_instance(softsp->dip)); 73829949e86Sstevel return; 73929949e86Sstevel } 74029949e86Sstevel 74129949e86Sstevel ac_named_ksp = (struct ac_kstat *)(ac_ksp->ks_data); 74229949e86Sstevel 74329949e86Sstevel /* initialize the named kstats */ 74429949e86Sstevel kstat_named_init(&ac_named_ksp->ac_memctl, 74529949e86Sstevel MEMCTL_KSTAT_NAMED, 74629949e86Sstevel KSTAT_DATA_UINT64); 74729949e86Sstevel 74829949e86Sstevel kstat_named_init(&ac_named_ksp->ac_memdecode0, 74929949e86Sstevel MEMDECODE0_KSTAT_NAMED, 75029949e86Sstevel KSTAT_DATA_UINT64); 75129949e86Sstevel 75229949e86Sstevel kstat_named_init(&ac_named_ksp->ac_memdecode1, 75329949e86Sstevel MEMDECODE1_KSTAT_NAMED, 75429949e86Sstevel KSTAT_DATA_UINT64); 75529949e86Sstevel 75629949e86Sstevel kstat_named_init(&ac_named_ksp->ac_mccr, 75729949e86Sstevel MCCR_KSTAT_NAMED, 75829949e86Sstevel KSTAT_DATA_UINT32); 75929949e86Sstevel 76029949e86Sstevel kstat_named_init(&ac_named_ksp->ac_counter, 76129949e86Sstevel CNTR_KSTAT_NAMED, 76229949e86Sstevel KSTAT_DATA_UINT64); 76329949e86Sstevel 76429949e86Sstevel kstat_named_init(&ac_named_ksp->ac_bank0_status, 76529949e86Sstevel BANK_0_KSTAT_NAMED, 76629949e86Sstevel KSTAT_DATA_CHAR); 76729949e86Sstevel 76829949e86Sstevel kstat_named_init(&ac_named_ksp->ac_bank1_status, 76929949e86Sstevel BANK_1_KSTAT_NAMED, 77029949e86Sstevel KSTAT_DATA_CHAR); 77129949e86Sstevel 77229949e86Sstevel ac_ksp->ks_update = ac_misc_kstat_update; 77329949e86Sstevel ac_ksp->ks_private = (void *)softsp; 77429949e86Sstevel softsp->ac_ksp = ac_ksp; 77529949e86Sstevel kstat_install(ac_ksp); 77629949e86Sstevel 77729949e86Sstevel /* 77829949e86Sstevel * Create the picN kstats if we are the first instance 77929949e86Sstevel * to attach. We use ac_attachcnt as a count of how 78029949e86Sstevel * many instances have attached. This is protected by 78129949e86Sstevel * a mutex. 78229949e86Sstevel */ 78329949e86Sstevel mutex_enter(&ac_attachcnt_mutex); 78429949e86Sstevel if (ac_attachcnt == 0) 78529949e86Sstevel ac_add_picN_kstats(softsp->dip); 78629949e86Sstevel 78729949e86Sstevel ac_attachcnt ++; 78829949e86Sstevel mutex_exit(&ac_attachcnt_mutex); 78929949e86Sstevel 79029949e86Sstevel /* 79129949e86Sstevel * Create the "counter" kstat for each AC instance. 79229949e86Sstevel * This provides access to the %pcr and %pic 79329949e86Sstevel * registers for that instance. 79429949e86Sstevel * 79529949e86Sstevel * The size of this kstat is AC_NUM_PICS + 1 for %pcr 79629949e86Sstevel */ 79729949e86Sstevel if ((ac_counters_ksp = kstat_create("ac", 79829949e86Sstevel ddi_get_instance(softsp->dip), "counters", 79929949e86Sstevel "bus", KSTAT_TYPE_NAMED, AC_NUM_PICS + 1, 80029949e86Sstevel KSTAT_FLAG_WRITABLE)) == NULL) { 80129949e86Sstevel 80229949e86Sstevel cmn_err(CE_WARN, "ac%d counters: kstat_create failed", 80329949e86Sstevel ddi_get_instance(softsp->dip)); 80429949e86Sstevel return; 80529949e86Sstevel } 80629949e86Sstevel ac_counters_named_data = 80729949e86Sstevel (struct kstat_named *)(ac_counters_ksp->ks_data); 80829949e86Sstevel 80929949e86Sstevel /* initialize the named kstats */ 81029949e86Sstevel kstat_named_init(&ac_counters_named_data[0], 81129949e86Sstevel "pcr", KSTAT_DATA_UINT64); 81229949e86Sstevel 81329949e86Sstevel kstat_named_init(&ac_counters_named_data[1], 81429949e86Sstevel "pic0", KSTAT_DATA_UINT64); 81529949e86Sstevel 81629949e86Sstevel kstat_named_init(&ac_counters_named_data[2], 81729949e86Sstevel "pic1", KSTAT_DATA_UINT64); 81829949e86Sstevel 81929949e86Sstevel ac_counters_ksp->ks_update = ac_counters_kstat_update; 82029949e86Sstevel ac_counters_ksp->ks_private = (void *)softsp; 82129949e86Sstevel kstat_install(ac_counters_ksp); 82229949e86Sstevel 82329949e86Sstevel /* update the sofstate */ 82429949e86Sstevel softsp->ac_counters_ksp = ac_counters_ksp; 82529949e86Sstevel } 82629949e86Sstevel 82729949e86Sstevel /* 82829949e86Sstevel * called from ac_add_kstats() to create a kstat for each %pic 82929949e86Sstevel * that the AC supports. These (read-only) kstats export the 83029949e86Sstevel * event names and %pcr masks that each %pic supports. 83129949e86Sstevel * 83229949e86Sstevel * if we fail to create any of these kstats we must remove any 83329949e86Sstevel * that we have already created and return; 83429949e86Sstevel * 83529949e86Sstevel * NOTE: because all AC's use the same events we only need to 83629949e86Sstevel * create the picN kstats once. All instances can use 83729949e86Sstevel * the same picN kstats. 83829949e86Sstevel * 83929949e86Sstevel * The flexibility exists to allow each device specify it's 84029949e86Sstevel * own events by creating picN kstats with the instance number 84129949e86Sstevel * set to ddi_get_instance(softsp->dip). 84229949e86Sstevel * 84329949e86Sstevel * When searching for a picN kstat for a device you should 84429949e86Sstevel * first search for a picN kstat using the instance number 84529949e86Sstevel * of the device you are interested in. If that fails you 84629949e86Sstevel * should use the first picN kstat found for that device. 84729949e86Sstevel */ 84829949e86Sstevel static void 84929949e86Sstevel ac_add_picN_kstats(dev_info_t *dip) 85029949e86Sstevel { 85129949e86Sstevel typedef struct ac_event_mask { 85229949e86Sstevel char *event_name; 85329949e86Sstevel uint64_t pcr_mask; 85429949e86Sstevel } ac_event_mask_t; 85529949e86Sstevel 85629949e86Sstevel /* 85729949e86Sstevel * AC Performance Events. 85829949e86Sstevel * 85929949e86Sstevel * We declare an array of event-names and event-masks. 86029949e86Sstevel */ 86129949e86Sstevel ac_event_mask_t ac_events_arr[] = { 86229949e86Sstevel {"mem_bank0_rds", 0x1}, {"mem_bank0_wrs", 0x2}, 86329949e86Sstevel {"mem_bank0_stall", 0x3}, {"mem_bank1_rds", 0x4}, 86429949e86Sstevel {"mem_bank1_wrs", 0x5}, {"mem_bank1_stall", 0x6}, 86529949e86Sstevel {"clock_cycles", 0x7}, {"addr_pkts", 0x8}, 86629949e86Sstevel {"data_pkts", 0x9}, {"flow_ctl_cyc", 0xa}, 86729949e86Sstevel {"fast_arb_pkts", 0xb}, {"bus_cont_cyc", 0xc}, 86829949e86Sstevel {"data_bus_can", 0xd}, {"ac_addr_pkts", 0xe}, 86929949e86Sstevel {"ac_data_pkts", 0xf}, {"rts_pkts", 0x10}, 87029949e86Sstevel {"rtsa_pkts", 0x11}, {"rto_pkts", 0x12}, 87129949e86Sstevel {"rs_pkts", 0x13}, {"wb_pkts", 0x14}, 87229949e86Sstevel {"ws_pkts", 0x15}, {"rio_pkts", 0x16}, 87329949e86Sstevel {"rbio_pkts", 0x17}, {"wio_pkts", 0x18}, 87429949e86Sstevel {"wbio_pkts", 0x19}, {"upa_a_rds_m", 0x1a}, 87529949e86Sstevel {"upa_a_rdo_v", 0x1b}, {"upa_b_rds_m", 0x1c}, 87629949e86Sstevel {"upa_b_rdo_v", 0x1d}, {"upa_a_preqs_fr", 0x20}, 87729949e86Sstevel {"upa_a_sreqs_to", 0x21}, {"upa_a_preqs_to", 0x22}, 87829949e86Sstevel {"upa_a_rds_fr", 0x23}, {"upa_a_rdsa_fr", 0x24}, 87929949e86Sstevel {"upa_a_rdo_fr", 0x25}, {"upa_a_rdd_fr", 0x26}, 88029949e86Sstevel {"upa_a_rio_rbio", 0x27}, {"upa_a_wio_wbio", 0x28}, 88129949e86Sstevel {"upa_a_cpb_to", 0x29}, {"upa_a_inv_to", 0x2a}, 88229949e86Sstevel {"upa_a_hits_buff", 0x2b}, {"upa_a_wb", 0x2c}, 88329949e86Sstevel {"upa_a_wi", 0x2d}, {"upa_b_preqs_fr", 0x30}, 88429949e86Sstevel {"upa_b_sreqs_to", 0x31}, {"upa_b_preqs_to", 0x32}, 88529949e86Sstevel {"upa_b_rds_fr", 0x33}, {"upa_b_rdsa_fr", 0x34}, 88629949e86Sstevel {"upa_b_rdo_fr", 0x35}, {"upa_b_rdd_fr", 0x36}, 88729949e86Sstevel {"upa_b_rio_rbio", 0x37}, {"upa_b_wio_wbio", 0x38}, 88829949e86Sstevel {"upa_b_cpb_to", 0x39}, {"upa_b_inv_to", 0x3a}, 88929949e86Sstevel {"upa_b_hits_buff", 0x3b}, {"upa_b_wb", 0x3c}, 89029949e86Sstevel {"upa_b_wi", 0x3d} 89129949e86Sstevel }; 89229949e86Sstevel 89329949e86Sstevel #define AC_NUM_EVENTS sizeof (ac_events_arr) / sizeof (ac_events_arr[0]) 89429949e86Sstevel 89529949e86Sstevel /* 89629949e86Sstevel * array of clear masks for each pic. 89729949e86Sstevel * These masks are used to clear the %pcr bits for 89829949e86Sstevel * each pic. 89929949e86Sstevel */ 90029949e86Sstevel ac_event_mask_t ac_clear_pic[AC_NUM_PICS] = { 90129949e86Sstevel /* pic0 */ 90229949e86Sstevel {"clear_pic", (uint64_t)~(0x3f)}, 90329949e86Sstevel /* pic1 */ 90429949e86Sstevel {"clear_pic", (uint64_t)~(0x3f << 8)} 90529949e86Sstevel }; 90629949e86Sstevel 90729949e86Sstevel struct kstat_named *ac_pic_named_data; 90829949e86Sstevel int event, pic; 90929949e86Sstevel char pic_name[30]; 91029949e86Sstevel int instance = ddi_get_instance(dip); 91129949e86Sstevel int pic_shift = 0; 91229949e86Sstevel 91329949e86Sstevel for (pic = 0; pic < AC_NUM_PICS; pic++) { 91429949e86Sstevel /* 91529949e86Sstevel * create the picN kstat. The size of this kstat is 91629949e86Sstevel * AC_NUM_EVENTS + 1 for the clear_event_mask 91729949e86Sstevel */ 91829949e86Sstevel (void) sprintf(pic_name, "pic%d", pic); /* pic0, pic1 ... */ 91929949e86Sstevel if ((ac_picN_ksp[pic] = kstat_create("ac", 92029949e86Sstevel instance, pic_name, "bus", KSTAT_TYPE_NAMED, 92129949e86Sstevel AC_NUM_EVENTS + 1, NULL)) == NULL) { 92229949e86Sstevel 92329949e86Sstevel cmn_err(CE_WARN, "ac %s: kstat_create failed", 92429949e86Sstevel pic_name); 92529949e86Sstevel 92629949e86Sstevel /* remove pic0 kstat if pic1 create fails */ 92729949e86Sstevel if (pic == 1) { 92829949e86Sstevel kstat_delete(ac_picN_ksp[0]); 92929949e86Sstevel ac_picN_ksp[0] = NULL; 93029949e86Sstevel } 93129949e86Sstevel return; 93229949e86Sstevel } 93329949e86Sstevel ac_pic_named_data = 93429949e86Sstevel (struct kstat_named *)(ac_picN_ksp[pic]->ks_data); 93529949e86Sstevel 93629949e86Sstevel /* 93729949e86Sstevel * when we are storing pcr_masks we need to shift bits 93829949e86Sstevel * left by 8 for pic1 events. 93929949e86Sstevel */ 94029949e86Sstevel if (pic == 1) 94129949e86Sstevel pic_shift = 8; 94229949e86Sstevel 94329949e86Sstevel /* 94429949e86Sstevel * for each picN event we need to write a kstat record 94529949e86Sstevel * (name = EVENT, value.ui64 = PCR_MASK) 94629949e86Sstevel */ 94729949e86Sstevel for (event = 0; event < AC_NUM_EVENTS; event ++) { 94829949e86Sstevel 94929949e86Sstevel /* pcr_mask */ 95029949e86Sstevel ac_pic_named_data[event].value.ui64 = 95129949e86Sstevel ac_events_arr[event].pcr_mask << pic_shift; 95229949e86Sstevel 95329949e86Sstevel /* event-name */ 95429949e86Sstevel kstat_named_init(&ac_pic_named_data[event], 95529949e86Sstevel ac_events_arr[event].event_name, 95629949e86Sstevel KSTAT_DATA_UINT64); 95729949e86Sstevel } 95829949e86Sstevel 95929949e86Sstevel /* 96029949e86Sstevel * we add the clear_pic event and mask as the last 96129949e86Sstevel * record in the kstat 96229949e86Sstevel */ 96329949e86Sstevel /* pcr mask */ 96429949e86Sstevel ac_pic_named_data[AC_NUM_EVENTS].value.ui64 = 96529949e86Sstevel ac_clear_pic[pic].pcr_mask; 96629949e86Sstevel 96729949e86Sstevel /* event-name */ 96829949e86Sstevel kstat_named_init(&ac_pic_named_data[AC_NUM_EVENTS], 96929949e86Sstevel ac_clear_pic[pic].event_name, 97029949e86Sstevel KSTAT_DATA_UINT64); 97129949e86Sstevel 97229949e86Sstevel kstat_install(ac_picN_ksp[pic]); 97329949e86Sstevel } 97429949e86Sstevel } 97529949e86Sstevel 97629949e86Sstevel 97729949e86Sstevel static void 97829949e86Sstevel ac_del_kstats(struct ac_soft_state *softsp) 97929949e86Sstevel { 98029949e86Sstevel struct kstat *ac_ksp; 98129949e86Sstevel int pic; 98229949e86Sstevel 98329949e86Sstevel /* remove "misc" kstat */ 98429949e86Sstevel ac_ksp = softsp->ac_ksp; 98529949e86Sstevel softsp->ac_ksp = NULL; 98629949e86Sstevel if (ac_ksp != NULL) { 98729949e86Sstevel ASSERT(ac_ksp->ks_private == (void *)softsp); 98829949e86Sstevel kstat_delete(ac_ksp); 98929949e86Sstevel } 99029949e86Sstevel 99129949e86Sstevel /* remove "bus" kstat */ 99229949e86Sstevel ac_ksp = softsp->ac_counters_ksp; 99329949e86Sstevel softsp->ac_counters_ksp = NULL; 99429949e86Sstevel if (ac_ksp != NULL) { 99529949e86Sstevel ASSERT(ac_ksp->ks_private == (void *)softsp); 99629949e86Sstevel kstat_delete(ac_ksp); 99729949e86Sstevel } 99829949e86Sstevel 99929949e86Sstevel /* 100029949e86Sstevel * if we are the last instance to detach we need to 100129949e86Sstevel * remove the picN kstats. We use ac_attachcnt as a 100229949e86Sstevel * count of how many instances are still attached. This 100329949e86Sstevel * is protected by a mutex. 100429949e86Sstevel */ 100529949e86Sstevel mutex_enter(&ac_attachcnt_mutex); 100629949e86Sstevel ac_attachcnt --; 100729949e86Sstevel if (ac_attachcnt == 0) { 100829949e86Sstevel for (pic = 0; pic < AC_NUM_PICS; pic++) { 100929949e86Sstevel if (ac_picN_ksp[pic] != (kstat_t *)NULL) { 101029949e86Sstevel kstat_delete(ac_picN_ksp[pic]); 101129949e86Sstevel ac_picN_ksp[pic] = NULL; 101229949e86Sstevel } 101329949e86Sstevel } 101429949e86Sstevel } 101529949e86Sstevel mutex_exit(&ac_attachcnt_mutex); 101629949e86Sstevel } 101729949e86Sstevel 101829949e86Sstevel static enum ac_bank_status 101929949e86Sstevel ac_kstat_stat(sysc_cfga_rstate_t rst, sysc_cfga_ostate_t ost) 102029949e86Sstevel { 102129949e86Sstevel switch (rst) { 102229949e86Sstevel case SYSC_CFGA_RSTATE_EMPTY: 102329949e86Sstevel return (StNoMem); 102429949e86Sstevel case SYSC_CFGA_RSTATE_DISCONNECTED: 102529949e86Sstevel return (StBad); 102629949e86Sstevel case SYSC_CFGA_RSTATE_CONNECTED: 102729949e86Sstevel switch (ost) { 102829949e86Sstevel case SYSC_CFGA_OSTATE_UNCONFIGURED: 102929949e86Sstevel return (StSpare); 103029949e86Sstevel case SYSC_CFGA_OSTATE_CONFIGURED: 103129949e86Sstevel return (StActive); 103229949e86Sstevel default: 103329949e86Sstevel return (StUnknown); 103429949e86Sstevel } 103529949e86Sstevel default: 103629949e86Sstevel return (StUnknown); 103729949e86Sstevel } 103829949e86Sstevel } 103929949e86Sstevel 104029949e86Sstevel static enum ac_bank_condition 104129949e86Sstevel ac_kstat_cond(sysc_cfga_cond_t cond) 104229949e86Sstevel { 104329949e86Sstevel switch (cond) { 104429949e86Sstevel case SYSC_CFGA_COND_UNKNOWN: 104529949e86Sstevel return (ConUnknown); 104629949e86Sstevel case SYSC_CFGA_COND_OK: 104729949e86Sstevel return (ConOK); 104829949e86Sstevel case SYSC_CFGA_COND_FAILING: 104929949e86Sstevel return (ConFailing); 105029949e86Sstevel case SYSC_CFGA_COND_FAILED: 105129949e86Sstevel return (ConFailed); 105229949e86Sstevel case SYSC_CFGA_COND_UNUSABLE: 105329949e86Sstevel return (ConBad); 105429949e86Sstevel default: 105529949e86Sstevel return (ConUnknown); 105629949e86Sstevel } 105729949e86Sstevel } 105829949e86Sstevel 105929949e86Sstevel static int 106029949e86Sstevel ac_misc_kstat_update(kstat_t *ksp, int rw) 106129949e86Sstevel { 106229949e86Sstevel struct ac_kstat *acksp; 106329949e86Sstevel struct ac_soft_state *softsp; 106429949e86Sstevel 106529949e86Sstevel acksp = (struct ac_kstat *)ksp->ks_data; 106629949e86Sstevel softsp = (struct ac_soft_state *)ksp->ks_private; 106729949e86Sstevel /* Need the NULL check in case kstat is about to be deleted. */ 106829949e86Sstevel ASSERT(softsp->ac_ksp == NULL || ksp == softsp->ac_ksp); 106929949e86Sstevel 107029949e86Sstevel /* this is a read-only kstat. Bail out on a write */ 107129949e86Sstevel if (rw == KSTAT_WRITE) { 107229949e86Sstevel return (EACCES); 107329949e86Sstevel } else { 107429949e86Sstevel /* 107529949e86Sstevel * copy the current state of the hardware into the 107629949e86Sstevel * kstat structure. 107729949e86Sstevel */ 107829949e86Sstevel acksp->ac_memctl.value.ui64 = *softsp->ac_memctl; 107929949e86Sstevel acksp->ac_memdecode0.value.ui64 = *softsp->ac_memdecode0; 108029949e86Sstevel acksp->ac_memdecode1.value.ui64 = *softsp->ac_memdecode1; 108129949e86Sstevel acksp->ac_mccr.value.ui32 = *softsp->ac_mccr; 108229949e86Sstevel acksp->ac_counter.value.ui64 = *softsp->ac_counter; 108329949e86Sstevel acksp->ac_bank0_status.value.c[0] = 108429949e86Sstevel ac_kstat_stat(softsp->bank[0].rstate, 108529949e86Sstevel softsp->bank[0].ostate); 108629949e86Sstevel acksp->ac_bank0_status.value.c[1] = 108729949e86Sstevel ac_kstat_cond(softsp->bank[0].condition); 108829949e86Sstevel acksp->ac_bank1_status.value.c[0] = 108929949e86Sstevel ac_kstat_stat(softsp->bank[1].rstate, 109029949e86Sstevel softsp->bank[1].ostate); 109129949e86Sstevel acksp->ac_bank1_status.value.c[1] = 109229949e86Sstevel ac_kstat_cond(softsp->bank[1].condition); 109329949e86Sstevel } 109429949e86Sstevel return (0); 109529949e86Sstevel } 109629949e86Sstevel 109729949e86Sstevel static int 109829949e86Sstevel ac_counters_kstat_update(kstat_t *ksp, int rw) 109929949e86Sstevel { 110029949e86Sstevel struct kstat_named *ac_counters_data; 110129949e86Sstevel struct ac_soft_state *softsp; 110229949e86Sstevel uint64_t pic_register; 110329949e86Sstevel 110429949e86Sstevel ac_counters_data = (struct kstat_named *)ksp->ks_data; 110529949e86Sstevel softsp = (struct ac_soft_state *)ksp->ks_private; 110629949e86Sstevel 110729949e86Sstevel /* 110829949e86Sstevel * We need to start/restart the ac_timeout that will 110929949e86Sstevel * return the AC counters to hot-plug mode after the 111029949e86Sstevel * ac_hot_plug_timeout_interval has expired. We tell 111129949e86Sstevel * ac_reset_timeout() whether this is a kstat_read or a 111229949e86Sstevel * kstat_write call. If this fails we reject the kstat 111329949e86Sstevel * operation. 111429949e86Sstevel */ 111529949e86Sstevel if (ac_reset_timeout(rw) != 0) 111629949e86Sstevel return (-1); 111729949e86Sstevel 111829949e86Sstevel 111929949e86Sstevel if (rw == KSTAT_WRITE) { 112029949e86Sstevel /* 112129949e86Sstevel * Write the %pcr value to the softsp->ac_mccr. 112229949e86Sstevel * This interface does not support writing to the 112329949e86Sstevel * %pic. 112429949e86Sstevel */ 112529949e86Sstevel *softsp->ac_mccr = 112629949e86Sstevel (uint32_t)ac_counters_data[0].value.ui64; 112729949e86Sstevel } else { 112829949e86Sstevel /* 112929949e86Sstevel * Read %pcr and %pic register values and write them 113029949e86Sstevel * into counters kstat. 113129949e86Sstevel */ 113229949e86Sstevel 113329949e86Sstevel /* pcr */ 113429949e86Sstevel ac_counters_data[0].value.ui64 = *softsp->ac_mccr; 113529949e86Sstevel 113629949e86Sstevel pic_register = *softsp->ac_counter; 113729949e86Sstevel /* 113829949e86Sstevel * ac pic register: 113929949e86Sstevel * (63:32) = pic1 114029949e86Sstevel * (31:00) = pic0 114129949e86Sstevel */ 114229949e86Sstevel 114329949e86Sstevel /* pic0 */ 114429949e86Sstevel ac_counters_data[1].value.ui64 = 114529949e86Sstevel AC_COUNTER_TO_PIC0(pic_register); 114629949e86Sstevel /* pic1 */ 114729949e86Sstevel ac_counters_data[2].value.ui64 = 114829949e86Sstevel AC_COUNTER_TO_PIC1(pic_register); 114929949e86Sstevel } 115029949e86Sstevel return (0); 115129949e86Sstevel } 115229949e86Sstevel 115329949e86Sstevel /* 115429949e86Sstevel * Decode the memory state given to us and plug it into the soft state 115529949e86Sstevel */ 115629949e86Sstevel static void 115729949e86Sstevel ac_get_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id) 115829949e86Sstevel { 115929949e86Sstevel char *property = (id == Bank0) ? AC_BANK0_STATUS : AC_BANK1_STATUS; 116029949e86Sstevel char *propval; 116129949e86Sstevel int proplen; 116229949e86Sstevel uint64_t memdec = (id == Bank0) ? 116329949e86Sstevel *(softsp->ac_memdecode0) : *(softsp->ac_memdecode1); 116429949e86Sstevel uint_t grp_size; 116529949e86Sstevel 116629949e86Sstevel softsp->bank[id].busy = 0; 116729949e86Sstevel softsp->bank[id].status_change = ddi_get_time(); 116829949e86Sstevel 116929949e86Sstevel if (GRP_SIZE_IS_SET(memdec)) { 117029949e86Sstevel grp_size = GRP_SPANMB(memdec); 117129949e86Sstevel 117229949e86Sstevel /* determine the memory bank size (in MB) */ 117329949e86Sstevel softsp->bank[id].real_size = softsp->bank[id].use_size = 117429949e86Sstevel (id == Bank0) ? (grp_size / INTLV0(*softsp->ac_memctl)) : 117529949e86Sstevel (grp_size / INTLV1(*softsp->ac_memctl)); 117629949e86Sstevel } else { 117729949e86Sstevel softsp->bank[id].real_size = softsp->bank[id].use_size = 0; 117829949e86Sstevel } 117929949e86Sstevel 118029949e86Sstevel /* 118129949e86Sstevel * decode the memory bank property. set condition based 118229949e86Sstevel * on the values. 118329949e86Sstevel */ 118429949e86Sstevel if (ddi_prop_op(DDI_DEV_T_ANY, softsp->dip, PROP_LEN_AND_VAL_ALLOC, 118529949e86Sstevel DDI_PROP_DONTPASS, property, (caddr_t)&propval, &proplen) == 118629949e86Sstevel DDI_PROP_SUCCESS) { 118729949e86Sstevel if (strcmp(propval, AC_BANK_NOMEM) == 0) { 118829949e86Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY; 118929949e86Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; 119029949e86Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN; 119129949e86Sstevel } else if (strcmp(propval, AC_BANK_OK) == 0) { 119229949e86Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED; 119329949e86Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED; 119429949e86Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_OK; 119529949e86Sstevel } else if (strcmp(propval, AC_BANK_SPARE) == 0) { 119629949e86Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED; 119729949e86Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; 119829949e86Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN; 119929949e86Sstevel } else if (strcmp(propval, AC_BANK_FAILED) == 0) { 120029949e86Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED; 120129949e86Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; 120229949e86Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE; 120329949e86Sstevel } else { 120429949e86Sstevel cmn_err(CE_WARN, "ac%d: board %d, bank %d: " 120529949e86Sstevel "unknown %smemory state [%s]", 120629949e86Sstevel ddi_get_instance(softsp->dip), softsp->board, id, 120729949e86Sstevel (memdec & AC_MEM_VALID) ? "connected " : "", 120829949e86Sstevel propval); 120929949e86Sstevel if (memdec & AC_MEM_VALID) { 121029949e86Sstevel softsp->bank[id].rstate = 121129949e86Sstevel SYSC_CFGA_RSTATE_CONNECTED; 121229949e86Sstevel softsp->bank[id].ostate = 121329949e86Sstevel SYSC_CFGA_OSTATE_CONFIGURED; 121429949e86Sstevel softsp->bank[id].condition = 121529949e86Sstevel SYSC_CFGA_COND_OK; 121629949e86Sstevel } else { 121729949e86Sstevel softsp->bank[id].rstate = 121829949e86Sstevel SYSC_CFGA_RSTATE_DISCONNECTED; 121929949e86Sstevel softsp->bank[id].ostate = 122029949e86Sstevel SYSC_CFGA_OSTATE_UNCONFIGURED; 122129949e86Sstevel softsp->bank[id].condition = 122229949e86Sstevel SYSC_CFGA_COND_UNUSABLE; 122329949e86Sstevel } 122429949e86Sstevel } 122529949e86Sstevel 122629949e86Sstevel kmem_free(propval, proplen); 122729949e86Sstevel } else { 122829949e86Sstevel /* we don't have the property, deduce the state of memory */ 122929949e86Sstevel if (memdec & AC_MEM_VALID) { 123029949e86Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED; 123129949e86Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED; 123229949e86Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_OK; 123329949e86Sstevel } else { 123429949e86Sstevel /* could be an i/o board... */ 123529949e86Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY; 123629949e86Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; 123729949e86Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN; 123829949e86Sstevel } 123929949e86Sstevel } 124029949e86Sstevel 124129949e86Sstevel /* we assume that all other bank statuses are NOT valid */ 124229949e86Sstevel if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) { 124329949e86Sstevel if ((memdec & AC_MEM_VALID) != 0) { 124429949e86Sstevel uint64_t base_pa; 124529949e86Sstevel 124629949e86Sstevel ASSERT((*softsp->ac_memctl & AC_CSR_REFEN) != 0); 124729949e86Sstevel /* register existence in the memloc database */ 124829949e86Sstevel base_pa = GRP_REALBASE(memdec); 124929949e86Sstevel fhc_add_memloc(softsp->board, base_pa, grp_size); 125029949e86Sstevel } 125129949e86Sstevel } 125229949e86Sstevel } 125329949e86Sstevel 125429949e86Sstevel static void 125529949e86Sstevel ac_eval_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id) 125629949e86Sstevel { 125729949e86Sstevel uint64_t memdec = (id == Bank0) ? 125829949e86Sstevel *(softsp->ac_memdecode0) : *(softsp->ac_memdecode1); 125929949e86Sstevel uint64_t base_pa; 126029949e86Sstevel 126129949e86Sstevel /* 126229949e86Sstevel * Downgrade the status of any bank that did not get 126329949e86Sstevel * programmed. 126429949e86Sstevel */ 126529949e86Sstevel if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED && 126629949e86Sstevel softsp->bank[id].ostate == SYSC_CFGA_OSTATE_UNCONFIGURED && 126729949e86Sstevel (memdec & AC_MEM_VALID) == 0) { 126829949e86Sstevel cmn_err(CE_WARN, "ac%d: board %d, bank %d: " 126929949e86Sstevel "spare memory bank not valid - it was ", 127029949e86Sstevel ddi_get_instance(softsp->dip), softsp->board, id); 127129949e86Sstevel cmn_err(CE_WARN, "misconfigured by the system " 127229949e86Sstevel "firmware. Disabling..."); 127329949e86Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED; 127429949e86Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; 127529949e86Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE; 127629949e86Sstevel } 127729949e86Sstevel /* 127829949e86Sstevel * Log a message about good banks. 127929949e86Sstevel */ 128029949e86Sstevel if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) { 128129949e86Sstevel ASSERT((memdec & AC_MEM_VALID) != 0); 128229949e86Sstevel base_pa = GRP_REALBASE(memdec); 128329949e86Sstevel 128429949e86Sstevel cmn_err(CE_CONT, "?ac%d board %d bank %d: " 128529949e86Sstevel "base 0x%" PRIx64 " size %dmb rstate %d " 128629949e86Sstevel "ostate %d condition %d\n", 128729949e86Sstevel ddi_get_instance(softsp->dip), 128829949e86Sstevel softsp->board, id, base_pa, softsp->bank[id].real_size, 128929949e86Sstevel softsp->bank[id].rstate, softsp->bank[id].ostate, 129029949e86Sstevel softsp->bank[id].condition); 129129949e86Sstevel } 129229949e86Sstevel } 129329949e86Sstevel 129429949e86Sstevel /*ARGSUSED*/ 129529949e86Sstevel static void 129629949e86Sstevel ac_ecache_flush(uint64_t a, uint64_t b) 129729949e86Sstevel { 129829949e86Sstevel cpu_flush_ecache(); 129929949e86Sstevel } 130029949e86Sstevel 130129949e86Sstevel static char * 130229949e86Sstevel ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event) 130329949e86Sstevel { 130429949e86Sstevel char *type_str; 130529949e86Sstevel 130629949e86Sstevel switch (ostate) { 130729949e86Sstevel case SYSC_CFGA_OSTATE_UNCONFIGURED: 130829949e86Sstevel switch (event) { 130929949e86Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE: 131029949e86Sstevel type_str = "unconfiguring"; 131129949e86Sstevel break; 131229949e86Sstevel case AC_AUDIT_OSTATE_SUCCEEDED: 131329949e86Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED: 131429949e86Sstevel type_str = "unconfigured"; 131529949e86Sstevel break; 131629949e86Sstevel default: 131729949e86Sstevel type_str = "unconfigure?"; 131829949e86Sstevel break; 131929949e86Sstevel } 132029949e86Sstevel break; 132129949e86Sstevel case SYSC_CFGA_OSTATE_CONFIGURED: 132229949e86Sstevel switch (event) { 132329949e86Sstevel case AC_AUDIT_OSTATE_CONFIGURE: 132429949e86Sstevel type_str = "configuring"; 132529949e86Sstevel break; 132629949e86Sstevel case AC_AUDIT_OSTATE_SUCCEEDED: 132729949e86Sstevel case AC_AUDIT_OSTATE_CONFIGURE_FAILED: 132829949e86Sstevel type_str = "configured"; 132929949e86Sstevel break; 133029949e86Sstevel default: 133129949e86Sstevel type_str = "configure?"; 133229949e86Sstevel break; 133329949e86Sstevel } 133429949e86Sstevel break; 133529949e86Sstevel 133629949e86Sstevel default: 133729949e86Sstevel type_str = "undefined occupant state"; 133829949e86Sstevel break; 133929949e86Sstevel } 134029949e86Sstevel return (type_str); 134129949e86Sstevel } 134229949e86Sstevel 134329949e86Sstevel static void 134429949e86Sstevel ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt) 134529949e86Sstevel { 134629949e86Sstevel struct ac_soft_state *softsp = pkt->softsp; 134729949e86Sstevel 134829949e86Sstevel switch (event) { 134929949e86Sstevel case AC_AUDIT_OSTATE_CONFIGURE: 135029949e86Sstevel cmn_err(CE_NOTE, 135129949e86Sstevel "%s memory bank %d in slot %d", 135229949e86Sstevel ac_ostate_typestr(SYSC_CFGA_OSTATE_CONFIGURED, 135329949e86Sstevel event), pkt->bank, 135429949e86Sstevel softsp->board); 135529949e86Sstevel break; 135629949e86Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE: 135729949e86Sstevel cmn_err(CE_NOTE, 135829949e86Sstevel "%s memory bank %d in slot %d", 135929949e86Sstevel ac_ostate_typestr( 136029949e86Sstevel SYSC_CFGA_OSTATE_UNCONFIGURED, 136129949e86Sstevel event), pkt->bank, 136229949e86Sstevel softsp->board); 136329949e86Sstevel break; 136429949e86Sstevel case AC_AUDIT_OSTATE_SUCCEEDED: 136529949e86Sstevel cmn_err(CE_NOTE, 136629949e86Sstevel "memory bank %d in slot %d is %s", 136729949e86Sstevel pkt->bank, softsp->board, 136829949e86Sstevel ac_ostate_typestr( 136929949e86Sstevel softsp->bank[pkt->bank].ostate, 137029949e86Sstevel event)); 137129949e86Sstevel break; 137229949e86Sstevel case AC_AUDIT_OSTATE_CONFIGURE_FAILED: 137329949e86Sstevel cmn_err(CE_NOTE, 137429949e86Sstevel "memory bank %d in slot %d not %s", 137529949e86Sstevel pkt->bank, 137629949e86Sstevel softsp->board, 137729949e86Sstevel ac_ostate_typestr( 137829949e86Sstevel SYSC_CFGA_OSTATE_CONFIGURED, 137929949e86Sstevel event)); 138029949e86Sstevel break; 138129949e86Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED: 138229949e86Sstevel cmn_err(CE_NOTE, 138329949e86Sstevel "memory bank %d in slot %d not %s", 138429949e86Sstevel pkt->bank, 138529949e86Sstevel softsp->board, 138629949e86Sstevel ac_ostate_typestr( 138729949e86Sstevel SYSC_CFGA_OSTATE_UNCONFIGURED, 138829949e86Sstevel event)); 138929949e86Sstevel break; 139029949e86Sstevel default: 139129949e86Sstevel cmn_err(CE_NOTE, 139229949e86Sstevel "unknown audit of memory bank %d in slot %d", 139329949e86Sstevel pkt->bank, softsp->board); 139429949e86Sstevel break; 139529949e86Sstevel } 139629949e86Sstevel } 139729949e86Sstevel 139829949e86Sstevel #include <vm/page.h> 139929949e86Sstevel #include <vm/hat.h> 140029949e86Sstevel 140129949e86Sstevel static int 140229949e86Sstevel ac_mem_exercise(ac_cfga_pkt_t *pkt, int flag) 140329949e86Sstevel { 140429949e86Sstevel struct ac_mem_info *mem_info; 140529949e86Sstevel pfn_t base; 140629949e86Sstevel pgcnt_t npgs; 140729949e86Sstevel 140829949e86Sstevel mem_info = &pkt->softsp->bank[pkt->bank]; 140929949e86Sstevel if (mem_info->rstate == SYSC_CFGA_RSTATE_CONNECTED) { 141029949e86Sstevel uint64_t base_pa, bank_size; 141129949e86Sstevel uint64_t decode; 141229949e86Sstevel 141329949e86Sstevel decode = (pkt->bank == Bank0) ? 141429949e86Sstevel *pkt->softsp->ac_memdecode0 : *pkt->softsp->ac_memdecode1; 141529949e86Sstevel base_pa = GRP_REALBASE(decode); 141629949e86Sstevel bank_size = GRP_UK2SPAN(decode); 141729949e86Sstevel 141829949e86Sstevel base = base_pa >> PAGESHIFT; 141929949e86Sstevel npgs = bank_size >> PAGESHIFT; 142029949e86Sstevel } else { 142129949e86Sstevel base = 0; 142229949e86Sstevel npgs = 0; 142329949e86Sstevel } 142429949e86Sstevel switch (pkt->cmd_cfga.arg) { 142529949e86Sstevel case AC_MEMX_RELOCATE_ALL: { 142629949e86Sstevel pfn_t pfn, pglim; 142729949e86Sstevel struct ac_memx_relocate_stats rstat; 142829949e86Sstevel 142929949e86Sstevel if (npgs == 0 || 143029949e86Sstevel mem_info->ostate != SYSC_CFGA_OSTATE_CONFIGURED) { 143129949e86Sstevel return (EINVAL); 143229949e86Sstevel } 143329949e86Sstevel if (mem_info->busy != FALSE) { 143429949e86Sstevel return (EBUSY); 143529949e86Sstevel } 143629949e86Sstevel bzero(&rstat, sizeof (rstat)); 143729949e86Sstevel rstat.base = (uint_t)base; 143829949e86Sstevel rstat.npgs = (uint_t)npgs; 143929949e86Sstevel pglim = base + npgs; 144029949e86Sstevel for (pfn = base; pfn < pglim; pfn++) { 144129949e86Sstevel page_t *pp, *pp_repl; 144229949e86Sstevel 144329949e86Sstevel retry: 144429949e86Sstevel pp = page_numtopp_nolock(pfn); 144529949e86Sstevel if (pp != NULL) { 144629949e86Sstevel if (!page_trylock(pp, SE_EXCL)) { 144729949e86Sstevel pp = NULL; 144829949e86Sstevel rstat.nolock++; 144929949e86Sstevel } 145029949e86Sstevel if (pp != NULL && page_pptonum(pp) != pfn) { 145129949e86Sstevel page_unlock(pp); 145229949e86Sstevel goto retry; 145329949e86Sstevel } 145429949e86Sstevel } else { 145529949e86Sstevel rstat.nopaget++; 145629949e86Sstevel } 145729949e86Sstevel if (pp != NULL && PP_ISFREE(pp)) { 145829949e86Sstevel page_unlock(pp); 145929949e86Sstevel rstat.isfree++; 146029949e86Sstevel pp = NULL; 146129949e86Sstevel } 146229949e86Sstevel if (pp != NULL) { 146329949e86Sstevel spgcnt_t npgs; 146429949e86Sstevel int result; 146529949e86Sstevel 146629949e86Sstevel pp_repl = NULL; 146729949e86Sstevel result = page_relocate(&pp, &pp_repl, 1, 1, 146829949e86Sstevel &npgs, NULL); 146929949e86Sstevel if (result == 0) { 147029949e86Sstevel while (npgs-- > 0) { 147129949e86Sstevel page_t *tpp; 147229949e86Sstevel 147329949e86Sstevel ASSERT(pp_repl != NULL); 147429949e86Sstevel tpp = pp_repl; 147529949e86Sstevel page_sub(&pp_repl, tpp); 147629949e86Sstevel page_unlock(tpp); 147729949e86Sstevel } 147829949e86Sstevel 147929949e86Sstevel rstat.reloc++; 148029949e86Sstevel } else { 148129949e86Sstevel page_unlock(pp); 148229949e86Sstevel rstat.noreloc++; 148329949e86Sstevel } 148429949e86Sstevel } 148529949e86Sstevel } 148629949e86Sstevel if (pkt->cmd_cfga.private != NULL && ddi_copyout(&rstat, 148729949e86Sstevel pkt->cmd_cfga.private, sizeof (rstat), flag) != 0) 148829949e86Sstevel return (EFAULT); 148929949e86Sstevel return (DDI_SUCCESS); 149029949e86Sstevel } 149129949e86Sstevel 149229949e86Sstevel default: 149329949e86Sstevel return (EINVAL); 149429949e86Sstevel } 149529949e86Sstevel } 149629949e86Sstevel 149729949e86Sstevel static int 149829949e86Sstevel ac_reset_timeout(int rw) 149929949e86Sstevel { 150029949e86Sstevel mutex_enter(&ac_hot_plug_mode_mutex); 150129949e86Sstevel 150229949e86Sstevel if ((ac_hot_plug_timeout == (timeout_id_t)NULL) && 150329949e86Sstevel (rw == KSTAT_READ)) { 150429949e86Sstevel /* 150529949e86Sstevel * We are in hot-plug mode. A kstat_read is not 150629949e86Sstevel * going to affect this. return 0 to allow the 150729949e86Sstevel * kstat_read to continue. 150829949e86Sstevel */ 150929949e86Sstevel mutex_exit(&ac_hot_plug_mode_mutex); 151029949e86Sstevel return (0); 151129949e86Sstevel 151229949e86Sstevel } else if ((ac_hot_plug_timeout == (timeout_id_t)NULL) && 151329949e86Sstevel (rw == KSTAT_WRITE)) { 151429949e86Sstevel /* 151529949e86Sstevel * There are no pending timeouts and we have received a 151629949e86Sstevel * kstat_write request so we must be transitioning 151729949e86Sstevel * from "hot-plug" mode to non "hot-plug" mode. 151829949e86Sstevel * Try to lock all boards before allowing the kstat_write. 151929949e86Sstevel */ 152029949e86Sstevel if (ac_enter_transition() == TRUE) 152129949e86Sstevel fhc_bdlist_unlock(); 152229949e86Sstevel else { 152329949e86Sstevel /* cannot lock boards so fail */ 152429949e86Sstevel mutex_exit(&ac_hot_plug_mode_mutex); 152529949e86Sstevel return (-1); 152629949e86Sstevel } 152729949e86Sstevel 152829949e86Sstevel /* 152929949e86Sstevel * We need to display a Warning about hot-plugging any 153029949e86Sstevel * boards. This message is only needed when we are 153129949e86Sstevel * transitioning out of "hot-plug" mode. 153229949e86Sstevel */ 153329949e86Sstevel cmn_err(CE_WARN, "This machine is being taken out of " 153429949e86Sstevel "hot-plug mode."); 153529949e86Sstevel cmn_err(CE_CONT, "Do not attempt to hot-plug boards " 153629949e86Sstevel "or power supplies in this system until further notice."); 153729949e86Sstevel 153829949e86Sstevel } else if (ac_hot_plug_timeout != (timeout_id_t)NULL) { 153929949e86Sstevel /* 154029949e86Sstevel * There is a pending timeout so we must already be 154129949e86Sstevel * in non "hot-plug" mode. It doesn't matter if the 154229949e86Sstevel * kstat request is a read or a write. 154329949e86Sstevel * 154429949e86Sstevel * We need to cancel the existing timeout. 154529949e86Sstevel */ 154629949e86Sstevel (void) untimeout(ac_hot_plug_timeout); 154729949e86Sstevel ac_hot_plug_timeout = NULL; 154829949e86Sstevel } 154929949e86Sstevel 155029949e86Sstevel /* 155129949e86Sstevel * create a new timeout. 155229949e86Sstevel */ 155329949e86Sstevel ac_hot_plug_timeout = timeout(ac_timeout, NULL, 155429949e86Sstevel drv_usectohz(ac_hot_plug_timeout_interval * 1000000)); 155529949e86Sstevel 155629949e86Sstevel mutex_exit(&ac_hot_plug_mode_mutex); 155729949e86Sstevel return (0); 155829949e86Sstevel } 155929949e86Sstevel 156029949e86Sstevel static void 156129949e86Sstevel ac_timeout(void *arg) 156229949e86Sstevel { 156329949e86Sstevel struct ac_soft_state *softsp; 156429949e86Sstevel fhc_bd_t *board; 156529949e86Sstevel 156629949e86Sstevel #ifdef lint 156729949e86Sstevel arg = arg; 156829949e86Sstevel #endif /* lint */ 156929949e86Sstevel 157029949e86Sstevel ac_hot_plug_timeout = (timeout_id_t)NULL; 157129949e86Sstevel 157229949e86Sstevel (void) fhc_bdlist_lock(-1); 157329949e86Sstevel 157429949e86Sstevel /* 157529949e86Sstevel * Foreach ac in the board list we need to 157629949e86Sstevel * re-program the pcr into "hot-plug" mode. 157729949e86Sstevel * We also program the pic register with the 157829949e86Sstevel * bus pause timing 157929949e86Sstevel */ 158029949e86Sstevel board = fhc_bd_first(); 158129949e86Sstevel while (board != NULL) { 158229949e86Sstevel softsp = board->ac_softsp; 158329949e86Sstevel if (softsp == NULL) { 158429949e86Sstevel /* 158529949e86Sstevel * This board must not have an AC. 158629949e86Sstevel * Skip it and move on. 158729949e86Sstevel */ 158829949e86Sstevel board = fhc_bd_next(board); 158929949e86Sstevel continue; 159029949e86Sstevel } 159129949e86Sstevel /* program the pcr into hot-plug mode */ 159229949e86Sstevel *softsp->ac_mccr = AC_CLEAR_PCR(*softsp->ac_mccr); 159329949e86Sstevel *softsp->ac_mccr = AC_SET_HOT_PLUG(*softsp->ac_mccr); 159429949e86Sstevel 159529949e86Sstevel /* program the pic with the bus pause time value */ 159629949e86Sstevel *softsp->ac_counter = AC_SET_PIC_BUS_PAUSE(softsp->board); 159729949e86Sstevel 159829949e86Sstevel /* get the next board */ 159929949e86Sstevel board = fhc_bd_next(board); 160029949e86Sstevel } 160129949e86Sstevel 160229949e86Sstevel ac_exit_transition(); 160329949e86Sstevel 160429949e86Sstevel fhc_bdlist_unlock(); 160529949e86Sstevel 160629949e86Sstevel /* 160729949e86Sstevel * It is now safe to start hot-plugging again. We need 160829949e86Sstevel * to display a message. 160929949e86Sstevel */ 161029949e86Sstevel cmn_err(CE_NOTE, "This machine is now in hot-plug mode."); 161129949e86Sstevel cmn_err(CE_CONT, "Board and power supply hot-plug operations " 161229949e86Sstevel "can be resumed."); 161329949e86Sstevel } 161429949e86Sstevel 161529949e86Sstevel /* 161629949e86Sstevel * This function will acquire the lock and set the in_transition 161729949e86Sstevel * bit for all the slots. If the slots are being used, 161829949e86Sstevel * we return FALSE; else set in_transition and return TRUE. 161929949e86Sstevel */ 162029949e86Sstevel static int 162129949e86Sstevel ac_enter_transition(void) 162229949e86Sstevel { 162329949e86Sstevel fhc_bd_t *list; 162429949e86Sstevel sysc_cfga_stat_t *sysc_stat_lk; 162529949e86Sstevel 162629949e86Sstevel /* mutex lock the structure */ 162729949e86Sstevel (void) fhc_bdlist_lock(-1); 162829949e86Sstevel 162929949e86Sstevel list = fhc_bd_clock(); 163029949e86Sstevel 163129949e86Sstevel /* change the in_transition bit */ 163229949e86Sstevel sysc_stat_lk = &list->sc; 163329949e86Sstevel if (sysc_stat_lk->in_transition == TRUE) { 163429949e86Sstevel fhc_bdlist_unlock(); 163529949e86Sstevel return (FALSE); 163629949e86Sstevel } else { 163729949e86Sstevel sysc_stat_lk->in_transition = TRUE; 163829949e86Sstevel return (TRUE); 163929949e86Sstevel } 164029949e86Sstevel } 164129949e86Sstevel 164229949e86Sstevel /* 164329949e86Sstevel * clear the in_transition bit for all the slots. 164429949e86Sstevel */ 164529949e86Sstevel static void 164629949e86Sstevel ac_exit_transition(void) 164729949e86Sstevel { 164829949e86Sstevel fhc_bd_t *list; 164929949e86Sstevel sysc_cfga_stat_t *sysc_stat_lk; 165029949e86Sstevel 165129949e86Sstevel ASSERT(fhc_bdlist_locked()); 165229949e86Sstevel 165329949e86Sstevel list = fhc_bd_clock(); 165429949e86Sstevel 165529949e86Sstevel sysc_stat_lk = &list->sc; 165629949e86Sstevel ASSERT(sysc_stat_lk->in_transition == TRUE); 165729949e86Sstevel sysc_stat_lk->in_transition = FALSE; 165829949e86Sstevel } 1659