11341Sstevel /*
21341Sstevel * CDDL HEADER START
31341Sstevel *
41341Sstevel * The contents of this file are subject to the terms of the
51341Sstevel * Common Development and Distribution License (the "License").
61341Sstevel * You may not use this file except in compliance with the License.
71341Sstevel *
81341Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91341Sstevel * or http://www.opensolaris.org/os/licensing.
101341Sstevel * See the License for the specific language governing permissions
111341Sstevel * and limitations under the License.
121341Sstevel *
131341Sstevel * When distributing Covered Code, include this CDDL HEADER in each
141341Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151341Sstevel * If applicable, add the following below this CDDL HEADER, with the
161341Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
171341Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
181341Sstevel *
191341Sstevel * CDDL HEADER END
201341Sstevel */
211341Sstevel
221341Sstevel /*
23*11311SSurya.Prakki@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
241341Sstevel * Use is subject to license terms.
251341Sstevel */
261341Sstevel
271341Sstevel
281341Sstevel #include <sys/types.h>
291341Sstevel #include <sys/conf.h>
301341Sstevel #include <sys/ddi.h>
311341Sstevel #include <sys/sunddi.h>
321341Sstevel #include <sys/ddi_impldefs.h>
331341Sstevel #include <sys/obpdefs.h>
341341Sstevel #include <sys/cmn_err.h>
351341Sstevel #include <sys/errno.h>
361341Sstevel #include <sys/kmem.h>
371341Sstevel #include <sys/debug.h>
381341Sstevel #include <sys/sysmacros.h>
391341Sstevel #include <sys/machsystm.h>
401341Sstevel #include <vm/hat_sfmmu.h>
411341Sstevel #include <sys/autoconf.h>
421341Sstevel #include <sys/open.h>
431341Sstevel #include <sys/stat.h>
441341Sstevel #include <sys/modctl.h>
451341Sstevel #include <sys/fhc.h>
461341Sstevel #include <sys/ac.h>
471341Sstevel #include <sys/cpu_module.h>
481341Sstevel #include <sys/x_call.h>
491341Sstevel #include <sys/fpu/fpusystm.h>
501341Sstevel #include <sys/lgrp.h>
511341Sstevel
521341Sstevel /* Useful debugging Stuff */
531341Sstevel #include <sys/nexusdebug.h>
541341Sstevel
551341Sstevel /*
561341Sstevel * Function prototypes
571341Sstevel */
581341Sstevel
591341Sstevel static int ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
601341Sstevel void **result);
611341Sstevel static int ac_attach(dev_info_t *, ddi_attach_cmd_t);
621341Sstevel static int ac_detach(dev_info_t *, ddi_detach_cmd_t);
631341Sstevel static int ac_open(dev_t *, int, int, cred_t *);
641341Sstevel static int ac_close(dev_t, int, int, cred_t *);
651341Sstevel static int ac_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
661341Sstevel
671341Sstevel static void ac_add_kstats(struct ac_soft_state *);
681341Sstevel static void ac_del_kstats(struct ac_soft_state *);
691341Sstevel static int ac_misc_kstat_update(kstat_t *, int);
701341Sstevel static void ac_add_picN_kstats(dev_info_t *dip);
711341Sstevel static int ac_counters_kstat_update(kstat_t *, int);
721341Sstevel static void ac_get_memory_status(struct ac_soft_state *, enum ac_bank_id);
731341Sstevel static void ac_eval_memory_status(struct ac_soft_state *, enum ac_bank_id);
741341Sstevel static void ac_ecache_flush(uint64_t, uint64_t);
751341Sstevel static int ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag);
761341Sstevel static int ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag);
771341Sstevel static int ac_reset_timeout(int rw);
781341Sstevel static void ac_timeout(void *);
791341Sstevel static int ac_enter_transition(void);
801341Sstevel static void ac_exit_transition(void);
811341Sstevel
821341Sstevel
831341Sstevel int ac_add_memory(ac_cfga_pkt_t *);
841341Sstevel int ac_del_memory(ac_cfga_pkt_t *);
851341Sstevel int ac_mem_stat(ac_cfga_pkt_t *, int);
861341Sstevel int ac_mem_test_start(ac_cfga_pkt_t *, int);
871341Sstevel int ac_mem_test_stop(ac_cfga_pkt_t *, int);
881341Sstevel int ac_mem_test_read(ac_cfga_pkt_t *, int);
891341Sstevel int ac_mem_test_write(ac_cfga_pkt_t *, int);
901341Sstevel void ac_mem_test_stop_on_close(uint_t, uint_t);
911341Sstevel /*
921341Sstevel * ac audit message events
931341Sstevel */
941341Sstevel typedef enum {
951341Sstevel AC_AUDIT_OSTATE_CONFIGURE,
961341Sstevel AC_AUDIT_OSTATE_UNCONFIGURE,
971341Sstevel AC_AUDIT_OSTATE_SUCCEEDED,
981341Sstevel AC_AUDIT_OSTATE_CONFIGURE_FAILED,
991341Sstevel AC_AUDIT_OSTATE_UNCONFIGURE_FAILED
1001341Sstevel } ac_audit_evt_t;
1011341Sstevel static void ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt);
1021341Sstevel static char *ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event);
1031341Sstevel
1041341Sstevel /* The memory ioctl interface version of this driver. */
1051341Sstevel static ac_mem_version_t ac_mem_version = AC_MEM_ADMIN_VERSION;
1061341Sstevel
1071341Sstevel static int ac_mem_exercise(ac_cfga_pkt_t *, int);
1081341Sstevel
1091341Sstevel /*
1101341Sstevel * Configuration data structures
1111341Sstevel */
1121341Sstevel static struct cb_ops ac_cb_ops = {
1131341Sstevel ac_open, /* open */
1141341Sstevel ac_close, /* close */
1151341Sstevel nulldev, /* strategy */
1161341Sstevel nulldev, /* print */
1171341Sstevel nodev, /* dump */
1181341Sstevel nulldev, /* read */
1191341Sstevel nulldev, /* write */
1201341Sstevel ac_ioctl, /* ioctl */
1211341Sstevel nodev, /* devmap */
1221341Sstevel nodev, /* mmap */
1231341Sstevel nodev, /* segmap */
1241341Sstevel nochpoll, /* poll */
1251341Sstevel ddi_prop_op, /* cb_prop_op */
1261341Sstevel 0, /* streamtab */
1271341Sstevel D_MP | D_NEW | D_HOTPLUG, /* Driver compatibility flag */
1281341Sstevel CB_REV, /* rev */
1291341Sstevel nodev, /* cb_aread */
1301341Sstevel nodev /* cb_awrite */
1311341Sstevel };
1321341Sstevel
1331341Sstevel static struct dev_ops ac_ops = {
1341341Sstevel DEVO_REV, /* devo_rev, */
1351341Sstevel 0, /* refcnt */
1361341Sstevel ac_info, /* getinfo */
1371341Sstevel nulldev, /* identify */
1381341Sstevel nulldev, /* probe */
1391341Sstevel ac_attach, /* attach */
1401341Sstevel ac_detach, /* detach */
1411341Sstevel nulldev, /* reset */
1421341Sstevel &ac_cb_ops, /* cb_ops */
1431341Sstevel (struct bus_ops *)0, /* bus_ops */
1447656SSherry.Moore@Sun.COM nulldev, /* power */
1457656SSherry.Moore@Sun.COM ddi_quiesce_not_needed, /* quiesce */
1461341Sstevel };
1471341Sstevel
1481341Sstevel /*
1491341Sstevel * Driver globals
1501341Sstevel */
1511341Sstevel void *acp; /* ac soft state hook */
1521341Sstevel static kstat_t *ac_picN_ksp[AC_NUM_PICS]; /* performance picN kstats */
1531341Sstevel static int ac_attachcnt = 0; /* number of instances attached */
1541341Sstevel static kmutex_t ac_attachcnt_mutex; /* ac_attachcnt lock - attach/detach */
1551341Sstevel static kmutex_t ac_hot_plug_mode_mutex;
1561341Sstevel static timeout_id_t ac_hot_plug_timeout;
1571341Sstevel static int ac_hot_plug_timeout_interval = 10;
1581341Sstevel
1591341Sstevel #define AC_GETSOFTC(I) \
1601341Sstevel ((struct ac_soft_state *)ddi_get_soft_state(acp, (I)))
1611341Sstevel
1621341Sstevel extern struct mod_ops mod_driverops;
1631341Sstevel
1641341Sstevel static struct modldrv modldrv = {
1651341Sstevel &mod_driverops, /* Type of module. This one is a driver */
1667656SSherry.Moore@Sun.COM "AC Leaf", /* name of module */
1671341Sstevel &ac_ops, /* driver ops */
1681341Sstevel };
1691341Sstevel
1701341Sstevel static struct modlinkage modlinkage = {
1711341Sstevel MODREV_1,
1721341Sstevel (void *)&modldrv,
1731341Sstevel NULL
1741341Sstevel };
1751341Sstevel
1761341Sstevel #ifndef lint
1771366Spetede char _depends_on[] = "drv/fhc";
1781341Sstevel #endif /* lint */
1791341Sstevel
1801341Sstevel /*
1811341Sstevel * These are the module initialization routines.
1821341Sstevel */
1831341Sstevel
1841341Sstevel int
_init(void)1851341Sstevel _init(void)
1861341Sstevel {
1871341Sstevel int error;
1881341Sstevel
1891341Sstevel if ((error = ddi_soft_state_init(&acp, sizeof (struct ac_soft_state),
1901341Sstevel 1)) != 0)
1911341Sstevel return (error);
1921341Sstevel
1931341Sstevel if ((error = mod_install(&modlinkage)) != 0) {
1941341Sstevel ddi_soft_state_fini(&acp);
1951341Sstevel return (error);
1961341Sstevel }
1971341Sstevel /* Initialize global mutex */
1981341Sstevel mutex_init(&ac_attachcnt_mutex, NULL, MUTEX_DRIVER, NULL);
1991341Sstevel mutex_init(&ac_hot_plug_mode_mutex, NULL, MUTEX_DRIVER, NULL);
2001341Sstevel return (0);
2011341Sstevel }
2021341Sstevel
2031341Sstevel int
_fini(void)2041341Sstevel _fini(void)
2051341Sstevel {
2061341Sstevel int error;
2071341Sstevel
2081341Sstevel if ((error = mod_remove(&modlinkage)) == 0) {
2091341Sstevel ddi_soft_state_fini(&acp);
2101341Sstevel mutex_destroy(&ac_attachcnt_mutex);
2111341Sstevel mutex_destroy(&ac_hot_plug_mode_mutex);
2121341Sstevel }
2131341Sstevel return (error);
2141341Sstevel }
2151341Sstevel
2161341Sstevel int
_info(struct modinfo * modinfop)2171341Sstevel _info(struct modinfo *modinfop)
2181341Sstevel {
2191341Sstevel return (mod_info(&modlinkage, modinfop));
2201341Sstevel }
2211341Sstevel
2221341Sstevel /* ARGSUSED */
2231341Sstevel static int
ac_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)2241341Sstevel ac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
2251341Sstevel {
2261341Sstevel dev_t dev;
2271341Sstevel int instance;
2281341Sstevel
2291341Sstevel if (infocmd == DDI_INFO_DEVT2INSTANCE) {
2301341Sstevel dev = (dev_t)arg;
2311341Sstevel instance = AC_GETINSTANCE(getminor(dev));
2321341Sstevel *result = (void *)(uintptr_t)instance;
2331341Sstevel return (DDI_SUCCESS);
2341341Sstevel }
2351341Sstevel return (DDI_FAILURE);
2361341Sstevel }
2371341Sstevel
2381341Sstevel static int
ac_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)2391341Sstevel ac_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
2401341Sstevel {
2411341Sstevel int instance;
2421341Sstevel struct ac_soft_state *softsp;
2431341Sstevel struct bd_list *list = NULL;
2441341Sstevel
2451341Sstevel switch (cmd) {
2461341Sstevel case DDI_ATTACH:
2471341Sstevel break;
2481341Sstevel
2491341Sstevel case DDI_RESUME:
2501341Sstevel return (DDI_SUCCESS);
2511341Sstevel
2521341Sstevel default:
2531341Sstevel return (DDI_FAILURE);
2541341Sstevel }
2551341Sstevel
2561341Sstevel instance = ddi_get_instance(devi);
2571341Sstevel
2581341Sstevel if (ddi_soft_state_zalloc(acp, instance) != DDI_SUCCESS) {
2591341Sstevel cmn_err(CE_WARN, "ddi_soft_state_zalloc failed for ac%d",
2607656SSherry.Moore@Sun.COM instance);
2611341Sstevel return (DDI_FAILURE);
2621341Sstevel }
2631341Sstevel
2641341Sstevel softsp = ddi_get_soft_state(acp, instance);
2651341Sstevel
2661341Sstevel /* Set the dip in the soft state */
2671341Sstevel softsp->dip = devi;
2681341Sstevel
2691341Sstevel /* Get the board number from this nodes parent */
2701341Sstevel softsp->pdip = ddi_get_parent(softsp->dip);
2711341Sstevel if ((softsp->board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->pdip,
2721341Sstevel DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
2731341Sstevel cmn_err(CE_WARN, "ac%d: unable to retrieve %s property",
2747656SSherry.Moore@Sun.COM instance, OBP_BOARDNUM);
2751341Sstevel goto bad;
2761341Sstevel }
2771341Sstevel
2781341Sstevel DPRINTF(AC_ATTACH_DEBUG, ("ac%d: devi= 0x%p\n,"
279*11311SSurya.Prakki@Sun.COM " softsp=0x%p\n", instance, (void *)devi, (void *)softsp));
2801341Sstevel
2811341Sstevel /* map in the registers for this device. */
2821341Sstevel if (ddi_map_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0)) {
2831341Sstevel cmn_err(CE_WARN, "ac%d: unable to map registers", instance);
2841341Sstevel goto bad;
2851341Sstevel }
2861341Sstevel
2871341Sstevel /* Setup the pointers to the hardware registers */
2881341Sstevel softsp->ac_id = (uint32_t *)softsp->ac_base;
2891341Sstevel softsp->ac_memctl = (uint64_t *)((char *)softsp->ac_base +
2907656SSherry.Moore@Sun.COM AC_OFF_MEMCTL);
2911341Sstevel softsp->ac_memdecode0 = (uint64_t *)((char *)softsp->ac_base +
2927656SSherry.Moore@Sun.COM AC_OFF_MEMDEC0);
2931341Sstevel softsp->ac_memdecode1 = (uint64_t *)((char *)softsp->ac_base +
2947656SSherry.Moore@Sun.COM AC_OFF_MEMDEC1);
2951341Sstevel softsp->ac_counter = (uint64_t *)((char *)softsp->ac_base +
2967656SSherry.Moore@Sun.COM AC_OFF_CNTR);
2971341Sstevel softsp->ac_mccr = (uint32_t *)((char *)softsp->ac_base +
2987656SSherry.Moore@Sun.COM AC_OFF_MCCR);
2991341Sstevel
3001341Sstevel /* nothing to suspend/resume here */
3011341Sstevel (void) ddi_prop_update_string(DDI_DEV_T_NONE, devi,
3027656SSherry.Moore@Sun.COM "pm-hardware-state", "no-suspend-resume");
3031341Sstevel
3041341Sstevel /* setup the the AC counter registers to allow for hotplug. */
3051341Sstevel list = fhc_bdlist_lock(softsp->board);
3061341Sstevel
3071341Sstevel if (list == NULL) {
3081341Sstevel cmn_err(CE_PANIC, "ac%d: Board %d not found in database",
3097656SSherry.Moore@Sun.COM instance, softsp->board);
3101341Sstevel }
3111341Sstevel
3121341Sstevel /* set the AC rev into the bd list structure */
3131341Sstevel list->sc.ac_compid = *softsp->ac_id;
3141341Sstevel
3151341Sstevel list->ac_softsp = softsp;
3161341Sstevel
3171341Sstevel if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) {
3181341Sstevel /* Create the minor nodes */
3191341Sstevel if (ddi_create_minor_node(devi, NAME_BANK0, S_IFCHR,
3201341Sstevel (AC_PUTINSTANCE(instance) | 0),
3211341Sstevel DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
3221341Sstevel cmn_err(CE_WARN, "ac%d: \"%s\" "
3231341Sstevel "ddi_create_minor_node failed", instance,
3241341Sstevel NAME_BANK0);
3251341Sstevel }
3261341Sstevel if (ddi_create_minor_node(devi, NAME_BANK1, S_IFCHR,
3271341Sstevel (AC_PUTINSTANCE(instance) | 1),
3281341Sstevel DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
3291341Sstevel cmn_err(CE_WARN, "ac%d: \"%s\" "
3301341Sstevel "ddi_create_minor_node failed", instance,
3311341Sstevel NAME_BANK0);
3321341Sstevel }
3331341Sstevel
3341341Sstevel /* purge previous fhc pa database entries */
3351341Sstevel fhc_del_memloc(softsp->board);
3361341Sstevel
3371341Sstevel /* Inherit Memory Bank Status */
3381341Sstevel ac_get_memory_status(softsp, Bank0);
3391341Sstevel ac_get_memory_status(softsp, Bank1);
3401341Sstevel /* Final Memory Bank Status evaluation and messaging */
3411341Sstevel ac_eval_memory_status(softsp, Bank0);
3421341Sstevel ac_eval_memory_status(softsp, Bank1);
3431341Sstevel }
3441341Sstevel
3451341Sstevel fhc_bdlist_unlock();
3461341Sstevel
3471341Sstevel /* create the kstats for this device. */
3481341Sstevel ac_add_kstats(softsp);
3491341Sstevel
3501341Sstevel ddi_report_dev(devi);
3511341Sstevel
3521341Sstevel return (DDI_SUCCESS);
3531341Sstevel
3541341Sstevel bad:
3551341Sstevel ddi_soft_state_free(acp, instance);
3561341Sstevel return (DDI_FAILURE);
3571341Sstevel }
3581341Sstevel
3591341Sstevel /* ARGSUSED */
3601341Sstevel static int
ac_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)3611341Sstevel ac_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3621341Sstevel {
3631341Sstevel int instance;
3641341Sstevel struct ac_soft_state *softsp;
3651341Sstevel struct bd_list *list;
3661341Sstevel
3671341Sstevel /* get the instance of this devi */
3681341Sstevel instance = ddi_get_instance(devi);
3691341Sstevel
3701341Sstevel /* get the soft state pointer for this device node */
3711341Sstevel softsp = ddi_get_soft_state(acp, instance);
3721341Sstevel
3731341Sstevel switch (cmd) {
3741341Sstevel case DDI_SUSPEND:
3751341Sstevel return (DDI_SUCCESS);
3761341Sstevel
3771341Sstevel case DDI_DETACH:
3781341Sstevel list = fhc_bdlist_lock(softsp->board);
3791341Sstevel
3801341Sstevel if (fhc_bd_detachable(softsp->board))
3811341Sstevel break;
3821341Sstevel else
3831341Sstevel fhc_bdlist_unlock();
3841341Sstevel /* FALLTHROUGH */
3851341Sstevel
3861341Sstevel default:
3871341Sstevel return (DDI_FAILURE);
3881341Sstevel }
3891341Sstevel
3901341Sstevel ASSERT(list->ac_softsp == softsp);
3911341Sstevel
3921341Sstevel if (list->sc.type == CPU_BOARD || list->sc.type == MEM_BOARD) {
3931341Sstevel int cpui;
3941341Sstevel
3951341Sstevel /*
3961341Sstevel * Test to see if memory is in use on a CPU/MEM board.
3971341Sstevel * In the case of a DR operation this condition
3981341Sstevel * will have been assured when the board was unconfigured.
3991341Sstevel */
4001341Sstevel if (softsp->bank[Bank0].busy != 0 ||
4011341Sstevel softsp->bank[Bank0].ostate == SYSC_CFGA_OSTATE_CONFIGURED ||
4021341Sstevel softsp->bank[Bank1].busy != 0 ||
4031341Sstevel softsp->bank[Bank1].ostate == SYSC_CFGA_OSTATE_CONFIGURED) {
4041341Sstevel fhc_bdlist_unlock();
4051341Sstevel return (DDI_FAILURE);
4061341Sstevel }
4071341Sstevel /*
4081341Sstevel * CPU busy test is done by the DR sequencer before
4091341Sstevel * device detach called.
4101341Sstevel */
4111341Sstevel
4121341Sstevel /*
4131341Sstevel * Flush all E-caches to remove references to this
4141341Sstevel * board's memory.
4151341Sstevel *
4161341Sstevel * Do this one CPU at a time to avoid stalls and timeouts
4171341Sstevel * due to all CPUs flushing concurrently.
4181341Sstevel * xc_one returns silently for non-existant CPUs.
4191341Sstevel */
4201341Sstevel for (cpui = 0; cpui < NCPU; cpui++)
4211341Sstevel xc_one(cpui, ac_ecache_flush, 0, 0);
4221341Sstevel }
4231341Sstevel
4241341Sstevel list->ac_softsp = NULL;
4251341Sstevel
4261341Sstevel /* delete the kstat for this driver. */
4271341Sstevel ac_del_kstats(softsp);
4281341Sstevel
4291341Sstevel /* unmap the registers */
4301341Sstevel ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->ac_base, 0, 0);
4311341Sstevel
4321341Sstevel fhc_bdlist_unlock();
4331341Sstevel
4341341Sstevel /* Remove the minor nodes. */
4351341Sstevel ddi_remove_minor_node(devi, NULL);
4361341Sstevel
4371341Sstevel /* free the soft state structure */
4381341Sstevel ddi_soft_state_free(acp, instance);
4391341Sstevel ddi_prop_remove_all(devi);
4401341Sstevel
4411341Sstevel return (DDI_SUCCESS);
4421341Sstevel }
4431341Sstevel
4441341Sstevel /* ARGSUSED */
4451341Sstevel static int
ac_open(dev_t * devp,int flag,int otyp,cred_t * credp)4461341Sstevel ac_open(dev_t *devp, int flag, int otyp, cred_t *credp)
4471341Sstevel {
4481341Sstevel int instance;
4491341Sstevel dev_t dev;
4501341Sstevel struct ac_soft_state *softsp;
4511341Sstevel struct bd_list *board;
4521341Sstevel int vis;
4531341Sstevel
4541341Sstevel dev = *devp;
4551341Sstevel instance = AC_GETINSTANCE(getminor(dev));
4561341Sstevel softsp = AC_GETSOFTC(instance);
4571341Sstevel
4581341Sstevel /* Is the instance attached? */
4591341Sstevel if (softsp == NULL) {
4601341Sstevel #ifdef DEBUG
4611341Sstevel cmn_err(CE_WARN, "ac%d device not attached", instance);
4621341Sstevel #endif /* DEBUG */
4631341Sstevel return (ENXIO);
4641341Sstevel }
4651341Sstevel
4661341Sstevel /*
4671341Sstevel * If the board is not configured, hide the memory APs
4681341Sstevel */
4691341Sstevel board = fhc_bdlist_lock(softsp->board);
4701341Sstevel vis = (board != NULL) && MEM_BOARD_VISIBLE(board);
4711341Sstevel fhc_bdlist_unlock();
4721341Sstevel
4731341Sstevel if (!vis)
4741341Sstevel return (ENXIO);
4751341Sstevel
4761341Sstevel /* verify that otyp is appropriate */
4771341Sstevel if (otyp != OTYP_CHR) {
4781341Sstevel return (EINVAL);
4791341Sstevel }
4801341Sstevel
4811341Sstevel return (DDI_SUCCESS);
4821341Sstevel }
4831341Sstevel
4841341Sstevel /* ARGSUSED */
4851341Sstevel static int
ac_close(dev_t devt,int flag,int otyp,cred_t * credp)4861341Sstevel ac_close(dev_t devt, int flag, int otyp, cred_t *credp)
4871341Sstevel {
4881341Sstevel struct ac_soft_state *softsp;
4891341Sstevel int instance;
4901341Sstevel
4911341Sstevel instance = AC_GETINSTANCE(getminor(devt));
4921341Sstevel softsp = AC_GETSOFTC(instance);
4931341Sstevel ASSERT(softsp != NULL);
4941341Sstevel ac_mem_test_stop_on_close(softsp->board, AC_GETBANK(getminor(devt)));
4951341Sstevel return (DDI_SUCCESS);
4961341Sstevel }
4971341Sstevel
4981341Sstevel static int
ac_pkt_init(ac_cfga_pkt_t * pkt,intptr_t arg,int flag)4991341Sstevel ac_pkt_init(ac_cfga_pkt_t *pkt, intptr_t arg, int flag)
5001341Sstevel {
5011341Sstevel #ifdef _MULTI_DATAMODEL
5021341Sstevel if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
5031341Sstevel ac_cfga_cmd32_t ac_cmd32;
5041341Sstevel
5051341Sstevel if (ddi_copyin((void *)arg, &ac_cmd32,
5067656SSherry.Moore@Sun.COM sizeof (ac_cfga_cmd32_t), flag) != 0) {
5071341Sstevel return (EFAULT);
5081341Sstevel }
5091341Sstevel pkt->cmd_cfga.force = ac_cmd32.force;
5101341Sstevel pkt->cmd_cfga.test = ac_cmd32.test;
5111341Sstevel pkt->cmd_cfga.arg = ac_cmd32.arg;
5121341Sstevel pkt->cmd_cfga.errtype = ac_cmd32.errtype;
5131341Sstevel pkt->cmd_cfga.outputstr =
5147656SSherry.Moore@Sun.COM (char *)(uintptr_t)ac_cmd32.outputstr;
5151341Sstevel pkt->cmd_cfga.private =
5167656SSherry.Moore@Sun.COM (void *)(uintptr_t)ac_cmd32.private;
5171341Sstevel } else
5181341Sstevel #endif /* _MULTI_DATAMODEL */
5191341Sstevel if (ddi_copyin((void *)arg, &(pkt->cmd_cfga),
5207656SSherry.Moore@Sun.COM sizeof (ac_cfga_cmd_t), flag) != 0) {
5211341Sstevel return (EFAULT);
5221341Sstevel }
5231341Sstevel pkt->errbuf = kmem_zalloc(SYSC_OUTPUT_LEN, KM_SLEEP);
5241341Sstevel return (0);
5251341Sstevel }
5261341Sstevel
5271341Sstevel static int
ac_pkt_fini(ac_cfga_pkt_t * pkt,intptr_t arg,int flag)5281341Sstevel ac_pkt_fini(ac_cfga_pkt_t *pkt, intptr_t arg, int flag)
5291341Sstevel {
5301341Sstevel int ret = TRUE;
5311341Sstevel
5321341Sstevel #ifdef _MULTI_DATAMODEL
5331341Sstevel if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
5341341Sstevel
5351341Sstevel if (ddi_copyout(&(pkt->cmd_cfga.errtype),
5367656SSherry.Moore@Sun.COM (void *)&(((ac_cfga_cmd32_t *)arg)->errtype),
5377656SSherry.Moore@Sun.COM sizeof (ac_err_t), flag) != 0) {
5381341Sstevel ret = FALSE;
5391341Sstevel }
5401341Sstevel } else
5411341Sstevel #endif
5421341Sstevel if (ddi_copyout(&(pkt->cmd_cfga.errtype),
5437656SSherry.Moore@Sun.COM (void *)&(((ac_cfga_cmd_t *)arg)->errtype),
5447656SSherry.Moore@Sun.COM sizeof (ac_err_t), flag) != 0) {
5451341Sstevel ret = FALSE;
5461341Sstevel }
5471341Sstevel
5481341Sstevel if ((ret != FALSE) && ((pkt->cmd_cfga.outputstr != NULL) &&
5497656SSherry.Moore@Sun.COM (ddi_copyout(pkt->errbuf, pkt->cmd_cfga.outputstr,
5507656SSherry.Moore@Sun.COM SYSC_OUTPUT_LEN, flag) != 0))) {
5511341Sstevel ret = FALSE;
5521341Sstevel }
5531341Sstevel
5541341Sstevel kmem_free(pkt->errbuf, SYSC_OUTPUT_LEN);
5551341Sstevel return (ret);
5561341Sstevel }
5571341Sstevel
5581341Sstevel /* ARGSUSED */
5591341Sstevel static int
ac_ioctl(dev_t devt,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)5601341Sstevel ac_ioctl(
5611341Sstevel dev_t devt,
5621341Sstevel int cmd,
5631341Sstevel intptr_t arg,
5641341Sstevel int flag,
5651341Sstevel cred_t *cred_p,
5661341Sstevel int *rval_p)
5671341Sstevel {
5681341Sstevel struct ac_soft_state *softsp;
5691341Sstevel ac_cfga_pkt_t cfga_pkt, *pkt;
5701341Sstevel int instance;
5711341Sstevel int retval;
5721341Sstevel
5731341Sstevel instance = AC_GETINSTANCE(getminor(devt));
5741341Sstevel softsp = AC_GETSOFTC(instance);
5751341Sstevel if (softsp == NULL) {
5761341Sstevel #ifdef DEBUG
5771341Sstevel cmn_err(CE_NOTE, "ac%d device not attached", instance);
5781341Sstevel #endif /* DEBUG */
5791341Sstevel return (ENXIO);
5801341Sstevel }
5811341Sstevel
5821341Sstevel /*
5831341Sstevel * Dispose of the easy ones first.
5841341Sstevel */
5851341Sstevel switch (cmd) {
5861341Sstevel case AC_MEM_ADMIN_VER:
5871341Sstevel /*
5881341Sstevel * Specify the revision of this ioctl interface driver.
5891341Sstevel */
5901341Sstevel if (ddi_copyout(&ac_mem_version, (void *)arg,
5911341Sstevel sizeof (ac_mem_version_t), flag) != 0)
5921341Sstevel return (EFAULT);
5931341Sstevel return (DDI_SUCCESS);
5941341Sstevel
5951341Sstevel case AC_MEM_CONFIGURE:
5961341Sstevel case AC_MEM_UNCONFIGURE:
5971341Sstevel case AC_MEM_STAT:
5981341Sstevel case AC_MEM_TEST_START:
5991341Sstevel case AC_MEM_TEST_STOP:
6001341Sstevel case AC_MEM_TEST_READ:
6011341Sstevel case AC_MEM_TEST_WRITE:
6021341Sstevel case AC_MEM_EXERCISE:
6031341Sstevel break;
6041341Sstevel
6051341Sstevel default:
6061341Sstevel return (ENOTTY);
6071341Sstevel }
6081341Sstevel if (cmd != AC_MEM_STAT && !fpu_exists) {
6091341Sstevel return (ENOTSUP);
6101341Sstevel }
6111341Sstevel
6121341Sstevel pkt = &cfga_pkt;
6131341Sstevel if ((retval = ac_pkt_init(pkt, arg, flag)) != 0)
6141341Sstevel return (retval);
6151341Sstevel pkt->softsp = softsp;
6161341Sstevel pkt->bank = AC_GETBANK(getminor(devt));
6171341Sstevel
6181341Sstevel switch (cmd) {
6191341Sstevel case AC_MEM_CONFIGURE:
6201341Sstevel if ((flag & FWRITE) == 0) {
6211341Sstevel retval = EBADF;
6221341Sstevel break;
6231341Sstevel }
6241341Sstevel
6251341Sstevel if (pkt->cmd_cfga.private != NULL) {
6261341Sstevel retval = EINVAL;
6271341Sstevel break;
6281341Sstevel }
6291341Sstevel ac_policy_audit_messages(AC_AUDIT_OSTATE_CONFIGURE, pkt);
6301341Sstevel retval = ac_add_memory(pkt);
6311341Sstevel if (!retval)
6321341Sstevel ac_policy_audit_messages(
6337656SSherry.Moore@Sun.COM AC_AUDIT_OSTATE_SUCCEEDED, pkt);
6341341Sstevel else
6351341Sstevel ac_policy_audit_messages(
6367656SSherry.Moore@Sun.COM AC_AUDIT_OSTATE_CONFIGURE_FAILED, pkt);
6371341Sstevel break;
6381341Sstevel
6391341Sstevel case AC_MEM_UNCONFIGURE:
6401341Sstevel if ((flag & FWRITE) == 0) {
6411341Sstevel retval = EBADF;
6421341Sstevel break;
6431341Sstevel }
6441341Sstevel
6451341Sstevel if (pkt->cmd_cfga.private != NULL) {
6461341Sstevel retval = EINVAL;
6471341Sstevel break;
6481341Sstevel }
6491341Sstevel ac_policy_audit_messages(AC_AUDIT_OSTATE_UNCONFIGURE, pkt);
6501341Sstevel retval = ac_del_memory(pkt);
6511341Sstevel if (!retval) {
6521341Sstevel ac_policy_audit_messages(
6537656SSherry.Moore@Sun.COM AC_AUDIT_OSTATE_SUCCEEDED, pkt);
6541341Sstevel } else
6551341Sstevel ac_policy_audit_messages(
6567656SSherry.Moore@Sun.COM AC_AUDIT_OSTATE_UNCONFIGURE_FAILED, pkt);
6571341Sstevel break;
6581341Sstevel
6591341Sstevel case AC_MEM_STAT:
6601341Sstevel /*
6611341Sstevel * Query usage of a bank of memory.
6621341Sstevel */
6631341Sstevel retval = ac_mem_stat(pkt, flag);
6641341Sstevel break;
6651341Sstevel
6661341Sstevel case AC_MEM_TEST_START:
6671341Sstevel if ((flag & FWRITE) == 0) {
6681341Sstevel retval = EBADF;
6691341Sstevel break;
6701341Sstevel }
6711341Sstevel
6721341Sstevel retval = ac_mem_test_start(pkt, flag);
6731341Sstevel break;
6741341Sstevel
6751341Sstevel case AC_MEM_TEST_STOP:
6761341Sstevel if ((flag & FWRITE) == 0) {
6771341Sstevel retval = EBADF;
6781341Sstevel break;
6791341Sstevel }
6801341Sstevel
6811341Sstevel retval = ac_mem_test_stop(pkt, flag);
6821341Sstevel break;
6831341Sstevel
6841341Sstevel case AC_MEM_TEST_READ:
6851341Sstevel /*
6861341Sstevel * read a 'page' (or less) of memory safely.
6871341Sstevel */
6881341Sstevel if ((flag & FWRITE) == 0) {
6891341Sstevel retval = EBADF;
6901341Sstevel break;
6911341Sstevel }
6921341Sstevel
6931341Sstevel retval = ac_mem_test_read(pkt, flag);
6941341Sstevel break;
6951341Sstevel
6961341Sstevel case AC_MEM_TEST_WRITE:
6971341Sstevel /*
6981341Sstevel * write a 'page' (or less) of memory safely.
6991341Sstevel */
7001341Sstevel if ((flag & FWRITE) == 0) {
7011341Sstevel retval = EBADF;
7021341Sstevel break;
7031341Sstevel }
7041341Sstevel
7051341Sstevel retval = ac_mem_test_write(pkt, flag);
7061341Sstevel break;
7071341Sstevel
7081341Sstevel case AC_MEM_EXERCISE:
7091341Sstevel retval = ac_mem_exercise(pkt, flag);
7101341Sstevel break;
7111341Sstevel
7121341Sstevel default:
7131341Sstevel ASSERT(0);
7141341Sstevel retval = ENOTTY;
7151341Sstevel break;
7161341Sstevel }
7171341Sstevel
7181341Sstevel if (ac_pkt_fini(pkt, arg, flag) != TRUE)
7191341Sstevel retval = EFAULT;
7201341Sstevel
7211341Sstevel return (retval);
7221341Sstevel }
7231341Sstevel
7241341Sstevel static void
ac_add_kstats(struct ac_soft_state * softsp)7251341Sstevel ac_add_kstats(struct ac_soft_state *softsp)
7261341Sstevel {
7271341Sstevel struct kstat *ac_ksp, *ac_counters_ksp;
7281341Sstevel struct ac_kstat *ac_named_ksp;
7291341Sstevel struct kstat_named *ac_counters_named_data;
7301341Sstevel
7311341Sstevel /*
7321341Sstevel * create the unix-misc kstat for address controller
7331341Sstevel * using the board number as the instance.
7341341Sstevel */
7351341Sstevel if ((ac_ksp = kstat_create("unix", softsp->board,
7361341Sstevel AC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
7371341Sstevel sizeof (struct ac_kstat) / sizeof (kstat_named_t),
7381341Sstevel KSTAT_FLAG_PERSISTENT)) == NULL) {
7391341Sstevel cmn_err(CE_WARN, "ac%d: kstat_create failed",
7407656SSherry.Moore@Sun.COM ddi_get_instance(softsp->dip));
7411341Sstevel return;
7421341Sstevel }
7431341Sstevel
7441341Sstevel ac_named_ksp = (struct ac_kstat *)(ac_ksp->ks_data);
7451341Sstevel
7461341Sstevel /* initialize the named kstats */
7471341Sstevel kstat_named_init(&ac_named_ksp->ac_memctl,
7487656SSherry.Moore@Sun.COM MEMCTL_KSTAT_NAMED,
7497656SSherry.Moore@Sun.COM KSTAT_DATA_UINT64);
7501341Sstevel
7511341Sstevel kstat_named_init(&ac_named_ksp->ac_memdecode0,
7527656SSherry.Moore@Sun.COM MEMDECODE0_KSTAT_NAMED,
7537656SSherry.Moore@Sun.COM KSTAT_DATA_UINT64);
7541341Sstevel
7551341Sstevel kstat_named_init(&ac_named_ksp->ac_memdecode1,
7567656SSherry.Moore@Sun.COM MEMDECODE1_KSTAT_NAMED,
7577656SSherry.Moore@Sun.COM KSTAT_DATA_UINT64);
7581341Sstevel
7591341Sstevel kstat_named_init(&ac_named_ksp->ac_mccr,
7607656SSherry.Moore@Sun.COM MCCR_KSTAT_NAMED,
7617656SSherry.Moore@Sun.COM KSTAT_DATA_UINT32);
7621341Sstevel
7631341Sstevel kstat_named_init(&ac_named_ksp->ac_counter,
7647656SSherry.Moore@Sun.COM CNTR_KSTAT_NAMED,
7657656SSherry.Moore@Sun.COM KSTAT_DATA_UINT64);
7661341Sstevel
7671341Sstevel kstat_named_init(&ac_named_ksp->ac_bank0_status,
7687656SSherry.Moore@Sun.COM BANK_0_KSTAT_NAMED,
7697656SSherry.Moore@Sun.COM KSTAT_DATA_CHAR);
7701341Sstevel
7711341Sstevel kstat_named_init(&ac_named_ksp->ac_bank1_status,
7727656SSherry.Moore@Sun.COM BANK_1_KSTAT_NAMED,
7737656SSherry.Moore@Sun.COM KSTAT_DATA_CHAR);
7741341Sstevel
7751341Sstevel ac_ksp->ks_update = ac_misc_kstat_update;
7761341Sstevel ac_ksp->ks_private = (void *)softsp;
7771341Sstevel softsp->ac_ksp = ac_ksp;
7781341Sstevel kstat_install(ac_ksp);
7791341Sstevel
7801341Sstevel /*
7811341Sstevel * Create the picN kstats if we are the first instance
7821341Sstevel * to attach. We use ac_attachcnt as a count of how
7831341Sstevel * many instances have attached. This is protected by
7841341Sstevel * a mutex.
7851341Sstevel */
7861341Sstevel mutex_enter(&ac_attachcnt_mutex);
7871341Sstevel if (ac_attachcnt == 0)
7881341Sstevel ac_add_picN_kstats(softsp->dip);
7891341Sstevel
7901341Sstevel ac_attachcnt ++;
7911341Sstevel mutex_exit(&ac_attachcnt_mutex);
7921341Sstevel
7931341Sstevel /*
7941341Sstevel * Create the "counter" kstat for each AC instance.
7951341Sstevel * This provides access to the %pcr and %pic
7961341Sstevel * registers for that instance.
7971341Sstevel *
7981341Sstevel * The size of this kstat is AC_NUM_PICS + 1 for %pcr
7991341Sstevel */
8001341Sstevel if ((ac_counters_ksp = kstat_create("ac",
8017656SSherry.Moore@Sun.COM ddi_get_instance(softsp->dip), "counters",
8027656SSherry.Moore@Sun.COM "bus", KSTAT_TYPE_NAMED, AC_NUM_PICS + 1,
8037656SSherry.Moore@Sun.COM KSTAT_FLAG_WRITABLE)) == NULL) {
8041341Sstevel
8051341Sstevel cmn_err(CE_WARN, "ac%d counters: kstat_create failed",
8067656SSherry.Moore@Sun.COM ddi_get_instance(softsp->dip));
8071341Sstevel return;
8081341Sstevel }
8091341Sstevel ac_counters_named_data =
8107656SSherry.Moore@Sun.COM (struct kstat_named *)(ac_counters_ksp->ks_data);
8111341Sstevel
8121341Sstevel /* initialize the named kstats */
8131341Sstevel kstat_named_init(&ac_counters_named_data[0],
8147656SSherry.Moore@Sun.COM "pcr", KSTAT_DATA_UINT64);
8151341Sstevel
8161341Sstevel kstat_named_init(&ac_counters_named_data[1],
8177656SSherry.Moore@Sun.COM "pic0", KSTAT_DATA_UINT64);
8181341Sstevel
8191341Sstevel kstat_named_init(&ac_counters_named_data[2],
8207656SSherry.Moore@Sun.COM "pic1", KSTAT_DATA_UINT64);
8211341Sstevel
8221341Sstevel ac_counters_ksp->ks_update = ac_counters_kstat_update;
8231341Sstevel ac_counters_ksp->ks_private = (void *)softsp;
8241341Sstevel kstat_install(ac_counters_ksp);
8251341Sstevel
8261341Sstevel /* update the sofstate */
8271341Sstevel softsp->ac_counters_ksp = ac_counters_ksp;
8281341Sstevel }
8291341Sstevel
8301341Sstevel /*
8311341Sstevel * called from ac_add_kstats() to create a kstat for each %pic
8321341Sstevel * that the AC supports. These (read-only) kstats export the
8331341Sstevel * event names and %pcr masks that each %pic supports.
8341341Sstevel *
8351341Sstevel * if we fail to create any of these kstats we must remove any
8361341Sstevel * that we have already created and return;
8371341Sstevel *
8381341Sstevel * NOTE: because all AC's use the same events we only need to
8391341Sstevel * create the picN kstats once. All instances can use
8401341Sstevel * the same picN kstats.
8411341Sstevel *
8421341Sstevel * The flexibility exists to allow each device specify it's
8431341Sstevel * own events by creating picN kstats with the instance number
8441341Sstevel * set to ddi_get_instance(softsp->dip).
8451341Sstevel *
8461341Sstevel * When searching for a picN kstat for a device you should
8471341Sstevel * first search for a picN kstat using the instance number
8481341Sstevel * of the device you are interested in. If that fails you
8491341Sstevel * should use the first picN kstat found for that device.
8501341Sstevel */
8511341Sstevel static void
ac_add_picN_kstats(dev_info_t * dip)8521341Sstevel ac_add_picN_kstats(dev_info_t *dip)
8531341Sstevel {
8541341Sstevel typedef struct ac_event_mask {
8551341Sstevel char *event_name;
8561341Sstevel uint64_t pcr_mask;
8571341Sstevel } ac_event_mask_t;
8581341Sstevel
8591341Sstevel /*
8601341Sstevel * AC Performance Events.
8611341Sstevel *
8621341Sstevel * We declare an array of event-names and event-masks.
8631341Sstevel */
8641341Sstevel ac_event_mask_t ac_events_arr[] = {
8651341Sstevel {"mem_bank0_rds", 0x1}, {"mem_bank0_wrs", 0x2},
8661341Sstevel {"mem_bank0_stall", 0x3}, {"mem_bank1_rds", 0x4},
8671341Sstevel {"mem_bank1_wrs", 0x5}, {"mem_bank1_stall", 0x6},
8681341Sstevel {"clock_cycles", 0x7}, {"addr_pkts", 0x8},
8691341Sstevel {"data_pkts", 0x9}, {"flow_ctl_cyc", 0xa},
8701341Sstevel {"fast_arb_pkts", 0xb}, {"bus_cont_cyc", 0xc},
8711341Sstevel {"data_bus_can", 0xd}, {"ac_addr_pkts", 0xe},
8721341Sstevel {"ac_data_pkts", 0xf}, {"rts_pkts", 0x10},
8731341Sstevel {"rtsa_pkts", 0x11}, {"rto_pkts", 0x12},
8741341Sstevel {"rs_pkts", 0x13}, {"wb_pkts", 0x14},
8751341Sstevel {"ws_pkts", 0x15}, {"rio_pkts", 0x16},
8761341Sstevel {"rbio_pkts", 0x17}, {"wio_pkts", 0x18},
8771341Sstevel {"wbio_pkts", 0x19}, {"upa_a_rds_m", 0x1a},
8781341Sstevel {"upa_a_rdo_v", 0x1b}, {"upa_b_rds_m", 0x1c},
8791341Sstevel {"upa_b_rdo_v", 0x1d}, {"upa_a_preqs_fr", 0x20},
8801341Sstevel {"upa_a_sreqs_to", 0x21}, {"upa_a_preqs_to", 0x22},
8811341Sstevel {"upa_a_rds_fr", 0x23}, {"upa_a_rdsa_fr", 0x24},
8821341Sstevel {"upa_a_rdo_fr", 0x25}, {"upa_a_rdd_fr", 0x26},
8831341Sstevel {"upa_a_rio_rbio", 0x27}, {"upa_a_wio_wbio", 0x28},
8841341Sstevel {"upa_a_cpb_to", 0x29}, {"upa_a_inv_to", 0x2a},
8851341Sstevel {"upa_a_hits_buff", 0x2b}, {"upa_a_wb", 0x2c},
8861341Sstevel {"upa_a_wi", 0x2d}, {"upa_b_preqs_fr", 0x30},
8871341Sstevel {"upa_b_sreqs_to", 0x31}, {"upa_b_preqs_to", 0x32},
8881341Sstevel {"upa_b_rds_fr", 0x33}, {"upa_b_rdsa_fr", 0x34},
8891341Sstevel {"upa_b_rdo_fr", 0x35}, {"upa_b_rdd_fr", 0x36},
8901341Sstevel {"upa_b_rio_rbio", 0x37}, {"upa_b_wio_wbio", 0x38},
8911341Sstevel {"upa_b_cpb_to", 0x39}, {"upa_b_inv_to", 0x3a},
8921341Sstevel {"upa_b_hits_buff", 0x3b}, {"upa_b_wb", 0x3c},
8931341Sstevel {"upa_b_wi", 0x3d}
8941341Sstevel };
8951341Sstevel
8961341Sstevel #define AC_NUM_EVENTS sizeof (ac_events_arr) / sizeof (ac_events_arr[0])
8971341Sstevel
8981341Sstevel /*
8991341Sstevel * array of clear masks for each pic.
9001341Sstevel * These masks are used to clear the %pcr bits for
9011341Sstevel * each pic.
9021341Sstevel */
9031341Sstevel ac_event_mask_t ac_clear_pic[AC_NUM_PICS] = {
9041341Sstevel /* pic0 */
9051341Sstevel {"clear_pic", (uint64_t)~(0x3f)},
9061341Sstevel /* pic1 */
9071341Sstevel {"clear_pic", (uint64_t)~(0x3f << 8)}
9081341Sstevel };
9091341Sstevel
9101341Sstevel struct kstat_named *ac_pic_named_data;
9111341Sstevel int event, pic;
9121341Sstevel char pic_name[30];
9131341Sstevel int instance = ddi_get_instance(dip);
9141341Sstevel int pic_shift = 0;
9151341Sstevel
9161341Sstevel for (pic = 0; pic < AC_NUM_PICS; pic++) {
9171341Sstevel /*
9181341Sstevel * create the picN kstat. The size of this kstat is
9191341Sstevel * AC_NUM_EVENTS + 1 for the clear_event_mask
9201341Sstevel */
9211341Sstevel (void) sprintf(pic_name, "pic%d", pic); /* pic0, pic1 ... */
9221341Sstevel if ((ac_picN_ksp[pic] = kstat_create("ac",
9237656SSherry.Moore@Sun.COM instance, pic_name, "bus", KSTAT_TYPE_NAMED,
9247656SSherry.Moore@Sun.COM AC_NUM_EVENTS + 1, NULL)) == NULL) {
9251341Sstevel
9261341Sstevel cmn_err(CE_WARN, "ac %s: kstat_create failed",
9277656SSherry.Moore@Sun.COM pic_name);
9281341Sstevel
9291341Sstevel /* remove pic0 kstat if pic1 create fails */
9301341Sstevel if (pic == 1) {
9311341Sstevel kstat_delete(ac_picN_ksp[0]);
9321341Sstevel ac_picN_ksp[0] = NULL;
9331341Sstevel }
9341341Sstevel return;
9351341Sstevel }
9361341Sstevel ac_pic_named_data =
9377656SSherry.Moore@Sun.COM (struct kstat_named *)(ac_picN_ksp[pic]->ks_data);
9381341Sstevel
9391341Sstevel /*
9401341Sstevel * when we are storing pcr_masks we need to shift bits
9411341Sstevel * left by 8 for pic1 events.
9421341Sstevel */
9431341Sstevel if (pic == 1)
9441341Sstevel pic_shift = 8;
9451341Sstevel
9461341Sstevel /*
9471341Sstevel * for each picN event we need to write a kstat record
9481341Sstevel * (name = EVENT, value.ui64 = PCR_MASK)
9491341Sstevel */
9501341Sstevel for (event = 0; event < AC_NUM_EVENTS; event ++) {
9511341Sstevel
9521341Sstevel /* pcr_mask */
9531341Sstevel ac_pic_named_data[event].value.ui64 =
9547656SSherry.Moore@Sun.COM ac_events_arr[event].pcr_mask << pic_shift;
9551341Sstevel
9561341Sstevel /* event-name */
9571341Sstevel kstat_named_init(&ac_pic_named_data[event],
9587656SSherry.Moore@Sun.COM ac_events_arr[event].event_name,
9597656SSherry.Moore@Sun.COM KSTAT_DATA_UINT64);
9601341Sstevel }
9611341Sstevel
9621341Sstevel /*
9631341Sstevel * we add the clear_pic event and mask as the last
9641341Sstevel * record in the kstat
9651341Sstevel */
9661341Sstevel /* pcr mask */
9671341Sstevel ac_pic_named_data[AC_NUM_EVENTS].value.ui64 =
9687656SSherry.Moore@Sun.COM ac_clear_pic[pic].pcr_mask;
9691341Sstevel
9701341Sstevel /* event-name */
9711341Sstevel kstat_named_init(&ac_pic_named_data[AC_NUM_EVENTS],
9727656SSherry.Moore@Sun.COM ac_clear_pic[pic].event_name,
9737656SSherry.Moore@Sun.COM KSTAT_DATA_UINT64);
9741341Sstevel
9751341Sstevel kstat_install(ac_picN_ksp[pic]);
9761341Sstevel }
9771341Sstevel }
9781341Sstevel
9791341Sstevel
9801341Sstevel static void
ac_del_kstats(struct ac_soft_state * softsp)9811341Sstevel ac_del_kstats(struct ac_soft_state *softsp)
9821341Sstevel {
9831341Sstevel struct kstat *ac_ksp;
9841341Sstevel int pic;
9851341Sstevel
9861341Sstevel /* remove "misc" kstat */
9871341Sstevel ac_ksp = softsp->ac_ksp;
9881341Sstevel softsp->ac_ksp = NULL;
9891341Sstevel if (ac_ksp != NULL) {
9901341Sstevel ASSERT(ac_ksp->ks_private == (void *)softsp);
9911341Sstevel kstat_delete(ac_ksp);
9921341Sstevel }
9931341Sstevel
9941341Sstevel /* remove "bus" kstat */
9951341Sstevel ac_ksp = softsp->ac_counters_ksp;
9961341Sstevel softsp->ac_counters_ksp = NULL;
9971341Sstevel if (ac_ksp != NULL) {
9981341Sstevel ASSERT(ac_ksp->ks_private == (void *)softsp);
9991341Sstevel kstat_delete(ac_ksp);
10001341Sstevel }
10011341Sstevel
10021341Sstevel /*
10031341Sstevel * if we are the last instance to detach we need to
10041341Sstevel * remove the picN kstats. We use ac_attachcnt as a
10051341Sstevel * count of how many instances are still attached. This
10061341Sstevel * is protected by a mutex.
10071341Sstevel */
10081341Sstevel mutex_enter(&ac_attachcnt_mutex);
10091341Sstevel ac_attachcnt --;
10101341Sstevel if (ac_attachcnt == 0) {
10111341Sstevel for (pic = 0; pic < AC_NUM_PICS; pic++) {
10121341Sstevel if (ac_picN_ksp[pic] != (kstat_t *)NULL) {
10131341Sstevel kstat_delete(ac_picN_ksp[pic]);
10141341Sstevel ac_picN_ksp[pic] = NULL;
10151341Sstevel }
10161341Sstevel }
10171341Sstevel }
10181341Sstevel mutex_exit(&ac_attachcnt_mutex);
10191341Sstevel }
10201341Sstevel
10211341Sstevel static enum ac_bank_status
ac_kstat_stat(sysc_cfga_rstate_t rst,sysc_cfga_ostate_t ost)10221341Sstevel ac_kstat_stat(sysc_cfga_rstate_t rst, sysc_cfga_ostate_t ost)
10231341Sstevel {
10241341Sstevel switch (rst) {
10251341Sstevel case SYSC_CFGA_RSTATE_EMPTY:
10261341Sstevel return (StNoMem);
10271341Sstevel case SYSC_CFGA_RSTATE_DISCONNECTED:
10281341Sstevel return (StBad);
10291341Sstevel case SYSC_CFGA_RSTATE_CONNECTED:
10301341Sstevel switch (ost) {
10311341Sstevel case SYSC_CFGA_OSTATE_UNCONFIGURED:
10321341Sstevel return (StSpare);
10331341Sstevel case SYSC_CFGA_OSTATE_CONFIGURED:
10341341Sstevel return (StActive);
10351341Sstevel default:
10361341Sstevel return (StUnknown);
10371341Sstevel }
10381341Sstevel default:
10391341Sstevel return (StUnknown);
10401341Sstevel }
10411341Sstevel }
10421341Sstevel
10431341Sstevel static enum ac_bank_condition
ac_kstat_cond(sysc_cfga_cond_t cond)10441341Sstevel ac_kstat_cond(sysc_cfga_cond_t cond)
10451341Sstevel {
10461341Sstevel switch (cond) {
10471341Sstevel case SYSC_CFGA_COND_UNKNOWN:
10481341Sstevel return (ConUnknown);
10491341Sstevel case SYSC_CFGA_COND_OK:
10501341Sstevel return (ConOK);
10511341Sstevel case SYSC_CFGA_COND_FAILING:
10521341Sstevel return (ConFailing);
10531341Sstevel case SYSC_CFGA_COND_FAILED:
10541341Sstevel return (ConFailed);
10551341Sstevel case SYSC_CFGA_COND_UNUSABLE:
10561341Sstevel return (ConBad);
10571341Sstevel default:
10581341Sstevel return (ConUnknown);
10591341Sstevel }
10601341Sstevel }
10611341Sstevel
10621341Sstevel static int
ac_misc_kstat_update(kstat_t * ksp,int rw)10631341Sstevel ac_misc_kstat_update(kstat_t *ksp, int rw)
10641341Sstevel {
10651341Sstevel struct ac_kstat *acksp;
10661341Sstevel struct ac_soft_state *softsp;
10671341Sstevel
10681341Sstevel acksp = (struct ac_kstat *)ksp->ks_data;
10691341Sstevel softsp = (struct ac_soft_state *)ksp->ks_private;
10701341Sstevel /* Need the NULL check in case kstat is about to be deleted. */
10711341Sstevel ASSERT(softsp->ac_ksp == NULL || ksp == softsp->ac_ksp);
10721341Sstevel
10731341Sstevel /* this is a read-only kstat. Bail out on a write */
10741341Sstevel if (rw == KSTAT_WRITE) {
10751341Sstevel return (EACCES);
10761341Sstevel } else {
10771341Sstevel /*
10781341Sstevel * copy the current state of the hardware into the
10791341Sstevel * kstat structure.
10801341Sstevel */
10811341Sstevel acksp->ac_memctl.value.ui64 = *softsp->ac_memctl;
10821341Sstevel acksp->ac_memdecode0.value.ui64 = *softsp->ac_memdecode0;
10831341Sstevel acksp->ac_memdecode1.value.ui64 = *softsp->ac_memdecode1;
10841341Sstevel acksp->ac_mccr.value.ui32 = *softsp->ac_mccr;
10851341Sstevel acksp->ac_counter.value.ui64 = *softsp->ac_counter;
10861341Sstevel acksp->ac_bank0_status.value.c[0] =
10871341Sstevel ac_kstat_stat(softsp->bank[0].rstate,
10881341Sstevel softsp->bank[0].ostate);
10891341Sstevel acksp->ac_bank0_status.value.c[1] =
10901341Sstevel ac_kstat_cond(softsp->bank[0].condition);
10911341Sstevel acksp->ac_bank1_status.value.c[0] =
10921341Sstevel ac_kstat_stat(softsp->bank[1].rstate,
10931341Sstevel softsp->bank[1].ostate);
10941341Sstevel acksp->ac_bank1_status.value.c[1] =
10951341Sstevel ac_kstat_cond(softsp->bank[1].condition);
10961341Sstevel }
10971341Sstevel return (0);
10981341Sstevel }
10991341Sstevel
11001341Sstevel static int
ac_counters_kstat_update(kstat_t * ksp,int rw)11011341Sstevel ac_counters_kstat_update(kstat_t *ksp, int rw)
11021341Sstevel {
11031341Sstevel struct kstat_named *ac_counters_data;
11041341Sstevel struct ac_soft_state *softsp;
11051341Sstevel uint64_t pic_register;
11061341Sstevel
11071341Sstevel ac_counters_data = (struct kstat_named *)ksp->ks_data;
11081341Sstevel softsp = (struct ac_soft_state *)ksp->ks_private;
11091341Sstevel
11101341Sstevel /*
11111341Sstevel * We need to start/restart the ac_timeout that will
11121341Sstevel * return the AC counters to hot-plug mode after the
11131341Sstevel * ac_hot_plug_timeout_interval has expired. We tell
11141341Sstevel * ac_reset_timeout() whether this is a kstat_read or a
11151341Sstevel * kstat_write call. If this fails we reject the kstat
11161341Sstevel * operation.
11171341Sstevel */
11181341Sstevel if (ac_reset_timeout(rw) != 0)
11191341Sstevel return (-1);
11201341Sstevel
11211341Sstevel
11221341Sstevel if (rw == KSTAT_WRITE) {
11231341Sstevel /*
11241341Sstevel * Write the %pcr value to the softsp->ac_mccr.
11251341Sstevel * This interface does not support writing to the
11261341Sstevel * %pic.
11271341Sstevel */
11281341Sstevel *softsp->ac_mccr =
11297656SSherry.Moore@Sun.COM (uint32_t)ac_counters_data[0].value.ui64;
11301341Sstevel } else {
11311341Sstevel /*
11321341Sstevel * Read %pcr and %pic register values and write them
11331341Sstevel * into counters kstat.
11341341Sstevel */
11351341Sstevel
11361341Sstevel /* pcr */
11371341Sstevel ac_counters_data[0].value.ui64 = *softsp->ac_mccr;
11381341Sstevel
11391341Sstevel pic_register = *softsp->ac_counter;
11401341Sstevel /*
11411341Sstevel * ac pic register:
11421341Sstevel * (63:32) = pic1
11431341Sstevel * (31:00) = pic0
11441341Sstevel */
11451341Sstevel
11461341Sstevel /* pic0 */
11471341Sstevel ac_counters_data[1].value.ui64 =
11487656SSherry.Moore@Sun.COM AC_COUNTER_TO_PIC0(pic_register);
11491341Sstevel /* pic1 */
11501341Sstevel ac_counters_data[2].value.ui64 =
11517656SSherry.Moore@Sun.COM AC_COUNTER_TO_PIC1(pic_register);
11521341Sstevel }
11531341Sstevel return (0);
11541341Sstevel }
11551341Sstevel
11561341Sstevel /*
11571341Sstevel * Decode the memory state given to us and plug it into the soft state
11581341Sstevel */
11591341Sstevel static void
ac_get_memory_status(struct ac_soft_state * softsp,enum ac_bank_id id)11601341Sstevel ac_get_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id)
11611341Sstevel {
11621341Sstevel char *property = (id == Bank0) ? AC_BANK0_STATUS : AC_BANK1_STATUS;
11631341Sstevel char *propval;
11641341Sstevel int proplen;
11651341Sstevel uint64_t memdec = (id == Bank0) ?
11667656SSherry.Moore@Sun.COM *(softsp->ac_memdecode0) : *(softsp->ac_memdecode1);
11671341Sstevel uint_t grp_size;
11681341Sstevel
11691341Sstevel softsp->bank[id].busy = 0;
11701341Sstevel softsp->bank[id].status_change = ddi_get_time();
11711341Sstevel
11721341Sstevel if (GRP_SIZE_IS_SET(memdec)) {
11731341Sstevel grp_size = GRP_SPANMB(memdec);
11741341Sstevel
11751341Sstevel /* determine the memory bank size (in MB) */
11761341Sstevel softsp->bank[id].real_size = softsp->bank[id].use_size =
11771341Sstevel (id == Bank0) ? (grp_size / INTLV0(*softsp->ac_memctl)) :
11781341Sstevel (grp_size / INTLV1(*softsp->ac_memctl));
11791341Sstevel } else {
11801341Sstevel softsp->bank[id].real_size = softsp->bank[id].use_size = 0;
11811341Sstevel }
11821341Sstevel
11831341Sstevel /*
11841341Sstevel * decode the memory bank property. set condition based
11851341Sstevel * on the values.
11861341Sstevel */
11871341Sstevel if (ddi_prop_op(DDI_DEV_T_ANY, softsp->dip, PROP_LEN_AND_VAL_ALLOC,
11881341Sstevel DDI_PROP_DONTPASS, property, (caddr_t)&propval, &proplen) ==
11891341Sstevel DDI_PROP_SUCCESS) {
11901341Sstevel if (strcmp(propval, AC_BANK_NOMEM) == 0) {
11911341Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY;
11921341Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
11931341Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
11941341Sstevel } else if (strcmp(propval, AC_BANK_OK) == 0) {
11951341Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
11961341Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
11971341Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_OK;
11981341Sstevel } else if (strcmp(propval, AC_BANK_SPARE) == 0) {
11991341Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
12001341Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12011341Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
12021341Sstevel } else if (strcmp(propval, AC_BANK_FAILED) == 0) {
12031341Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
12041341Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12051341Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE;
12061341Sstevel } else {
12071341Sstevel cmn_err(CE_WARN, "ac%d: board %d, bank %d: "
12081341Sstevel "unknown %smemory state [%s]",
12091341Sstevel ddi_get_instance(softsp->dip), softsp->board, id,
12101341Sstevel (memdec & AC_MEM_VALID) ? "connected " : "",
12111341Sstevel propval);
12121341Sstevel if (memdec & AC_MEM_VALID) {
12131341Sstevel softsp->bank[id].rstate =
12141341Sstevel SYSC_CFGA_RSTATE_CONNECTED;
12151341Sstevel softsp->bank[id].ostate =
12161341Sstevel SYSC_CFGA_OSTATE_CONFIGURED;
12171341Sstevel softsp->bank[id].condition =
12181341Sstevel SYSC_CFGA_COND_OK;
12191341Sstevel } else {
12201341Sstevel softsp->bank[id].rstate =
12211341Sstevel SYSC_CFGA_RSTATE_DISCONNECTED;
12221341Sstevel softsp->bank[id].ostate =
12231341Sstevel SYSC_CFGA_OSTATE_UNCONFIGURED;
12241341Sstevel softsp->bank[id].condition =
12251341Sstevel SYSC_CFGA_COND_UNUSABLE;
12261341Sstevel }
12271341Sstevel }
12281341Sstevel
12291341Sstevel kmem_free(propval, proplen);
12301341Sstevel } else {
12311341Sstevel /* we don't have the property, deduce the state of memory */
12321341Sstevel if (memdec & AC_MEM_VALID) {
12331341Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_CONNECTED;
12341341Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_CONFIGURED;
12351341Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_OK;
12361341Sstevel } else {
12371341Sstevel /* could be an i/o board... */
12381341Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_EMPTY;
12391341Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12401341Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNKNOWN;
12411341Sstevel }
12421341Sstevel }
12431341Sstevel
12441341Sstevel /* we assume that all other bank statuses are NOT valid */
12451341Sstevel if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) {
12461341Sstevel if ((memdec & AC_MEM_VALID) != 0) {
12471341Sstevel uint64_t base_pa;
12481341Sstevel
12491341Sstevel ASSERT((*softsp->ac_memctl & AC_CSR_REFEN) != 0);
12501341Sstevel /* register existence in the memloc database */
12511341Sstevel base_pa = GRP_REALBASE(memdec);
12521341Sstevel fhc_add_memloc(softsp->board, base_pa, grp_size);
12531341Sstevel }
12541341Sstevel }
12551341Sstevel }
12561341Sstevel
12571341Sstevel static void
ac_eval_memory_status(struct ac_soft_state * softsp,enum ac_bank_id id)12581341Sstevel ac_eval_memory_status(struct ac_soft_state *softsp, enum ac_bank_id id)
12591341Sstevel {
12601341Sstevel uint64_t memdec = (id == Bank0) ?
12617656SSherry.Moore@Sun.COM *(softsp->ac_memdecode0) : *(softsp->ac_memdecode1);
12621341Sstevel uint64_t base_pa;
12631341Sstevel
12641341Sstevel /*
12651341Sstevel * Downgrade the status of any bank that did not get
12661341Sstevel * programmed.
12671341Sstevel */
12681341Sstevel if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED &&
12691341Sstevel softsp->bank[id].ostate == SYSC_CFGA_OSTATE_UNCONFIGURED &&
12701341Sstevel (memdec & AC_MEM_VALID) == 0) {
12711341Sstevel cmn_err(CE_WARN, "ac%d: board %d, bank %d: "
12721341Sstevel "spare memory bank not valid - it was ",
12731341Sstevel ddi_get_instance(softsp->dip), softsp->board, id);
12741341Sstevel cmn_err(CE_WARN, "misconfigured by the system "
12751341Sstevel "firmware. Disabling...");
12761341Sstevel softsp->bank[id].rstate = SYSC_CFGA_RSTATE_DISCONNECTED;
12771341Sstevel softsp->bank[id].ostate = SYSC_CFGA_OSTATE_UNCONFIGURED;
12781341Sstevel softsp->bank[id].condition = SYSC_CFGA_COND_UNUSABLE;
12791341Sstevel }
12801341Sstevel /*
12811341Sstevel * Log a message about good banks.
12821341Sstevel */
12831341Sstevel if (softsp->bank[id].rstate == SYSC_CFGA_RSTATE_CONNECTED) {
12841341Sstevel ASSERT((memdec & AC_MEM_VALID) != 0);
12851341Sstevel base_pa = GRP_REALBASE(memdec);
12861341Sstevel
12871341Sstevel cmn_err(CE_CONT, "?ac%d board %d bank %d: "
12881341Sstevel "base 0x%" PRIx64 " size %dmb rstate %d "
12891341Sstevel "ostate %d condition %d\n",
12901341Sstevel ddi_get_instance(softsp->dip),
12911341Sstevel softsp->board, id, base_pa, softsp->bank[id].real_size,
12921341Sstevel softsp->bank[id].rstate, softsp->bank[id].ostate,
12931341Sstevel softsp->bank[id].condition);
12941341Sstevel }
12951341Sstevel }
12961341Sstevel
12971341Sstevel /*ARGSUSED*/
12981341Sstevel static void
ac_ecache_flush(uint64_t a,uint64_t b)12991341Sstevel ac_ecache_flush(uint64_t a, uint64_t b)
13001341Sstevel {
13011341Sstevel cpu_flush_ecache();
13021341Sstevel }
13031341Sstevel
13041341Sstevel static char *
ac_ostate_typestr(sysc_cfga_ostate_t ostate,ac_audit_evt_t event)13051341Sstevel ac_ostate_typestr(sysc_cfga_ostate_t ostate, ac_audit_evt_t event)
13061341Sstevel {
13071341Sstevel char *type_str;
13081341Sstevel
13091341Sstevel switch (ostate) {
13101341Sstevel case SYSC_CFGA_OSTATE_UNCONFIGURED:
13111341Sstevel switch (event) {
13121341Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE:
13131341Sstevel type_str = "unconfiguring";
13141341Sstevel break;
13151341Sstevel case AC_AUDIT_OSTATE_SUCCEEDED:
13161341Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED:
13171341Sstevel type_str = "unconfigured";
13181341Sstevel break;
13191341Sstevel default:
13201341Sstevel type_str = "unconfigure?";
13211341Sstevel break;
13221341Sstevel }
13231341Sstevel break;
13241341Sstevel case SYSC_CFGA_OSTATE_CONFIGURED:
13251341Sstevel switch (event) {
13261341Sstevel case AC_AUDIT_OSTATE_CONFIGURE:
13271341Sstevel type_str = "configuring";
13281341Sstevel break;
13291341Sstevel case AC_AUDIT_OSTATE_SUCCEEDED:
13301341Sstevel case AC_AUDIT_OSTATE_CONFIGURE_FAILED:
13311341Sstevel type_str = "configured";
13321341Sstevel break;
13331341Sstevel default:
13341341Sstevel type_str = "configure?";
13351341Sstevel break;
13361341Sstevel }
13371341Sstevel break;
13381341Sstevel
13391341Sstevel default:
13401341Sstevel type_str = "undefined occupant state";
13411341Sstevel break;
13421341Sstevel }
13431341Sstevel return (type_str);
13441341Sstevel }
13451341Sstevel
13461341Sstevel static void
ac_policy_audit_messages(ac_audit_evt_t event,ac_cfga_pkt_t * pkt)13471341Sstevel ac_policy_audit_messages(ac_audit_evt_t event, ac_cfga_pkt_t *pkt)
13481341Sstevel {
13491341Sstevel struct ac_soft_state *softsp = pkt->softsp;
13501341Sstevel
13511341Sstevel switch (event) {
13521341Sstevel case AC_AUDIT_OSTATE_CONFIGURE:
13531341Sstevel cmn_err(CE_NOTE,
13547656SSherry.Moore@Sun.COM "%s memory bank %d in slot %d",
13557656SSherry.Moore@Sun.COM ac_ostate_typestr(SYSC_CFGA_OSTATE_CONFIGURED,
13567656SSherry.Moore@Sun.COM event), pkt->bank,
13577656SSherry.Moore@Sun.COM softsp->board);
13581341Sstevel break;
13591341Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE:
13601341Sstevel cmn_err(CE_NOTE,
13617656SSherry.Moore@Sun.COM "%s memory bank %d in slot %d",
13627656SSherry.Moore@Sun.COM ac_ostate_typestr(
13637656SSherry.Moore@Sun.COM SYSC_CFGA_OSTATE_UNCONFIGURED,
13647656SSherry.Moore@Sun.COM event), pkt->bank,
13657656SSherry.Moore@Sun.COM softsp->board);
13661341Sstevel break;
13671341Sstevel case AC_AUDIT_OSTATE_SUCCEEDED:
13681341Sstevel cmn_err(CE_NOTE,
13697656SSherry.Moore@Sun.COM "memory bank %d in slot %d is %s",
13707656SSherry.Moore@Sun.COM pkt->bank, softsp->board,
13717656SSherry.Moore@Sun.COM ac_ostate_typestr(
13727656SSherry.Moore@Sun.COM softsp->bank[pkt->bank].ostate,
13737656SSherry.Moore@Sun.COM event));
13741341Sstevel break;
13751341Sstevel case AC_AUDIT_OSTATE_CONFIGURE_FAILED:
13761341Sstevel cmn_err(CE_NOTE,
13771341Sstevel "memory bank %d in slot %d not %s",
13787656SSherry.Moore@Sun.COM pkt->bank,
13797656SSherry.Moore@Sun.COM softsp->board,
13807656SSherry.Moore@Sun.COM ac_ostate_typestr(
13817656SSherry.Moore@Sun.COM SYSC_CFGA_OSTATE_CONFIGURED,
13827656SSherry.Moore@Sun.COM event));
13831341Sstevel break;
13841341Sstevel case AC_AUDIT_OSTATE_UNCONFIGURE_FAILED:
13851341Sstevel cmn_err(CE_NOTE,
13867656SSherry.Moore@Sun.COM "memory bank %d in slot %d not %s",
13877656SSherry.Moore@Sun.COM pkt->bank,
13887656SSherry.Moore@Sun.COM softsp->board,
13897656SSherry.Moore@Sun.COM ac_ostate_typestr(
13907656SSherry.Moore@Sun.COM SYSC_CFGA_OSTATE_UNCONFIGURED,
13917656SSherry.Moore@Sun.COM event));
13921341Sstevel break;
13931341Sstevel default:
13941341Sstevel cmn_err(CE_NOTE,
13957656SSherry.Moore@Sun.COM "unknown audit of memory bank %d in slot %d",
13967656SSherry.Moore@Sun.COM pkt->bank, softsp->board);
13971341Sstevel break;
13981341Sstevel }
13991341Sstevel }
14001341Sstevel
14011341Sstevel #include <vm/page.h>
14021341Sstevel #include <vm/hat.h>
14031341Sstevel
14041341Sstevel static int
ac_mem_exercise(ac_cfga_pkt_t * pkt,int flag)14051341Sstevel ac_mem_exercise(ac_cfga_pkt_t *pkt, int flag)
14061341Sstevel {
14071341Sstevel struct ac_mem_info *mem_info;
14081341Sstevel pfn_t base;
14091341Sstevel pgcnt_t npgs;
14101341Sstevel
14111341Sstevel mem_info = &pkt->softsp->bank[pkt->bank];
14121341Sstevel if (mem_info->rstate == SYSC_CFGA_RSTATE_CONNECTED) {
14131341Sstevel uint64_t base_pa, bank_size;
14141341Sstevel uint64_t decode;
14151341Sstevel
14161341Sstevel decode = (pkt->bank == Bank0) ?
14171341Sstevel *pkt->softsp->ac_memdecode0 : *pkt->softsp->ac_memdecode1;
14181341Sstevel base_pa = GRP_REALBASE(decode);
14191341Sstevel bank_size = GRP_UK2SPAN(decode);
14201341Sstevel
14211341Sstevel base = base_pa >> PAGESHIFT;
14221341Sstevel npgs = bank_size >> PAGESHIFT;
14231341Sstevel } else {
14241341Sstevel base = 0;
14251341Sstevel npgs = 0;
14261341Sstevel }
14271341Sstevel switch (pkt->cmd_cfga.arg) {
14281341Sstevel case AC_MEMX_RELOCATE_ALL: {
14291341Sstevel pfn_t pfn, pglim;
14301341Sstevel struct ac_memx_relocate_stats rstat;
14311341Sstevel
14321341Sstevel if (npgs == 0 ||
14331341Sstevel mem_info->ostate != SYSC_CFGA_OSTATE_CONFIGURED) {
14341341Sstevel return (EINVAL);
14351341Sstevel }
14361341Sstevel if (mem_info->busy != FALSE) {
14371341Sstevel return (EBUSY);
14381341Sstevel }
14391341Sstevel bzero(&rstat, sizeof (rstat));
14401341Sstevel rstat.base = (uint_t)base;
14411341Sstevel rstat.npgs = (uint_t)npgs;
14421341Sstevel pglim = base + npgs;
14431341Sstevel for (pfn = base; pfn < pglim; pfn++) {
14441341Sstevel page_t *pp, *pp_repl;
14451341Sstevel
14461341Sstevel retry:
14471341Sstevel pp = page_numtopp_nolock(pfn);
14481341Sstevel if (pp != NULL) {
14491341Sstevel if (!page_trylock(pp, SE_EXCL)) {
14501341Sstevel pp = NULL;
14511341Sstevel rstat.nolock++;
14521341Sstevel }
14531341Sstevel if (pp != NULL && page_pptonum(pp) != pfn) {
14541341Sstevel page_unlock(pp);
14551341Sstevel goto retry;
14561341Sstevel }
14571341Sstevel } else {
14581341Sstevel rstat.nopaget++;
14591341Sstevel }
14601341Sstevel if (pp != NULL && PP_ISFREE(pp)) {
14611341Sstevel page_unlock(pp);
14621341Sstevel rstat.isfree++;
14631341Sstevel pp = NULL;
14641341Sstevel }
14651341Sstevel if (pp != NULL) {
14661341Sstevel spgcnt_t npgs;
14671341Sstevel int result;
14681341Sstevel
14691341Sstevel pp_repl = NULL;
14701341Sstevel result = page_relocate(&pp, &pp_repl, 1, 1,
14711341Sstevel &npgs, NULL);
14721341Sstevel if (result == 0) {
14731341Sstevel while (npgs-- > 0) {
14741341Sstevel page_t *tpp;
14751341Sstevel
14761341Sstevel ASSERT(pp_repl != NULL);
14771341Sstevel tpp = pp_repl;
14781341Sstevel page_sub(&pp_repl, tpp);
14791341Sstevel page_unlock(tpp);
14801341Sstevel }
14811341Sstevel
14821341Sstevel rstat.reloc++;
14831341Sstevel } else {
14841341Sstevel page_unlock(pp);
14851341Sstevel rstat.noreloc++;
14861341Sstevel }
14871341Sstevel }
14881341Sstevel }
14891341Sstevel if (pkt->cmd_cfga.private != NULL && ddi_copyout(&rstat,
14901341Sstevel pkt->cmd_cfga.private, sizeof (rstat), flag) != 0)
14911341Sstevel return (EFAULT);
14921341Sstevel return (DDI_SUCCESS);
14931341Sstevel }
14941341Sstevel
14951341Sstevel default:
14961341Sstevel return (EINVAL);
14971341Sstevel }
14981341Sstevel }
14991341Sstevel
15001341Sstevel static int
ac_reset_timeout(int rw)15011341Sstevel ac_reset_timeout(int rw)
15021341Sstevel {
15031341Sstevel mutex_enter(&ac_hot_plug_mode_mutex);
15041341Sstevel
15051341Sstevel if ((ac_hot_plug_timeout == (timeout_id_t)NULL) &&
15061341Sstevel (rw == KSTAT_READ)) {
15071341Sstevel /*
15081341Sstevel * We are in hot-plug mode. A kstat_read is not
15091341Sstevel * going to affect this. return 0 to allow the
15101341Sstevel * kstat_read to continue.
15111341Sstevel */
15121341Sstevel mutex_exit(&ac_hot_plug_mode_mutex);
15131341Sstevel return (0);
15141341Sstevel
15151341Sstevel } else if ((ac_hot_plug_timeout == (timeout_id_t)NULL) &&
15161341Sstevel (rw == KSTAT_WRITE)) {
15171341Sstevel /*
15181341Sstevel * There are no pending timeouts and we have received a
15191341Sstevel * kstat_write request so we must be transitioning
15201341Sstevel * from "hot-plug" mode to non "hot-plug" mode.
15211341Sstevel * Try to lock all boards before allowing the kstat_write.
15221341Sstevel */
15231341Sstevel if (ac_enter_transition() == TRUE)
15241341Sstevel fhc_bdlist_unlock();
15251341Sstevel else {
15261341Sstevel /* cannot lock boards so fail */
15271341Sstevel mutex_exit(&ac_hot_plug_mode_mutex);
15281341Sstevel return (-1);
15291341Sstevel }
15301341Sstevel
15311341Sstevel /*
15321341Sstevel * We need to display a Warning about hot-plugging any
15331341Sstevel * boards. This message is only needed when we are
15341341Sstevel * transitioning out of "hot-plug" mode.
15351341Sstevel */
15361341Sstevel cmn_err(CE_WARN, "This machine is being taken out of "
15371341Sstevel "hot-plug mode.");
15381341Sstevel cmn_err(CE_CONT, "Do not attempt to hot-plug boards "
15391341Sstevel "or power supplies in this system until further notice.");
15401341Sstevel
15411341Sstevel } else if (ac_hot_plug_timeout != (timeout_id_t)NULL) {
15421341Sstevel /*
15431341Sstevel * There is a pending timeout so we must already be
15441341Sstevel * in non "hot-plug" mode. It doesn't matter if the
15451341Sstevel * kstat request is a read or a write.
15461341Sstevel *
15471341Sstevel * We need to cancel the existing timeout.
15481341Sstevel */
15491341Sstevel (void) untimeout(ac_hot_plug_timeout);
15501341Sstevel ac_hot_plug_timeout = NULL;
15511341Sstevel }
15521341Sstevel
15531341Sstevel /*
15541341Sstevel * create a new timeout.
15551341Sstevel */
15561341Sstevel ac_hot_plug_timeout = timeout(ac_timeout, NULL,
15571341Sstevel drv_usectohz(ac_hot_plug_timeout_interval * 1000000));
15581341Sstevel
15591341Sstevel mutex_exit(&ac_hot_plug_mode_mutex);
15601341Sstevel return (0);
15611341Sstevel }
15621341Sstevel
15631341Sstevel static void
ac_timeout(void * arg)15641341Sstevel ac_timeout(void *arg)
15651341Sstevel {
15661341Sstevel struct ac_soft_state *softsp;
15671341Sstevel fhc_bd_t *board;
15681341Sstevel
15691341Sstevel #ifdef lint
15701341Sstevel arg = arg;
15711341Sstevel #endif /* lint */
15721341Sstevel
15731341Sstevel ac_hot_plug_timeout = (timeout_id_t)NULL;
15741341Sstevel
15751341Sstevel (void) fhc_bdlist_lock(-1);
15761341Sstevel
15771341Sstevel /*
15781341Sstevel * Foreach ac in the board list we need to
15791341Sstevel * re-program the pcr into "hot-plug" mode.
15801341Sstevel * We also program the pic register with the
15811341Sstevel * bus pause timing
15821341Sstevel */
15831341Sstevel board = fhc_bd_first();
15841341Sstevel while (board != NULL) {
15851341Sstevel softsp = board->ac_softsp;
15861341Sstevel if (softsp == NULL) {
15871341Sstevel /*
15881341Sstevel * This board must not have an AC.
15891341Sstevel * Skip it and move on.
15901341Sstevel */
15911341Sstevel board = fhc_bd_next(board);
15921341Sstevel continue;
15931341Sstevel }
15941341Sstevel /* program the pcr into hot-plug mode */
15951341Sstevel *softsp->ac_mccr = AC_CLEAR_PCR(*softsp->ac_mccr);
15961341Sstevel *softsp->ac_mccr = AC_SET_HOT_PLUG(*softsp->ac_mccr);
15971341Sstevel
15981341Sstevel /* program the pic with the bus pause time value */
15991341Sstevel *softsp->ac_counter = AC_SET_PIC_BUS_PAUSE(softsp->board);
16001341Sstevel
16011341Sstevel /* get the next board */
16021341Sstevel board = fhc_bd_next(board);
16031341Sstevel }
16041341Sstevel
16051341Sstevel ac_exit_transition();
16061341Sstevel
16071341Sstevel fhc_bdlist_unlock();
16081341Sstevel
16091341Sstevel /*
16101341Sstevel * It is now safe to start hot-plugging again. We need
16111341Sstevel * to display a message.
16121341Sstevel */
16131341Sstevel cmn_err(CE_NOTE, "This machine is now in hot-plug mode.");
16141341Sstevel cmn_err(CE_CONT, "Board and power supply hot-plug operations "
16151341Sstevel "can be resumed.");
16161341Sstevel }
16171341Sstevel
16181341Sstevel /*
16191341Sstevel * This function will acquire the lock and set the in_transition
16201341Sstevel * bit for all the slots. If the slots are being used,
16211341Sstevel * we return FALSE; else set in_transition and return TRUE.
16221341Sstevel */
16231341Sstevel static int
ac_enter_transition(void)16241341Sstevel ac_enter_transition(void)
16251341Sstevel {
16261341Sstevel fhc_bd_t *list;
16271341Sstevel sysc_cfga_stat_t *sysc_stat_lk;
16281341Sstevel
16291341Sstevel /* mutex lock the structure */
16301341Sstevel (void) fhc_bdlist_lock(-1);
16311341Sstevel
16321341Sstevel list = fhc_bd_clock();
16331341Sstevel
16341341Sstevel /* change the in_transition bit */
16351341Sstevel sysc_stat_lk = &list->sc;
16361341Sstevel if (sysc_stat_lk->in_transition == TRUE) {
16371341Sstevel fhc_bdlist_unlock();
16381341Sstevel return (FALSE);
16391341Sstevel } else {
16401341Sstevel sysc_stat_lk->in_transition = TRUE;
16411341Sstevel return (TRUE);
16421341Sstevel }
16431341Sstevel }
16441341Sstevel
16451341Sstevel /*
16461341Sstevel * clear the in_transition bit for all the slots.
16471341Sstevel */
16481341Sstevel static void
ac_exit_transition(void)16491341Sstevel ac_exit_transition(void)
16501341Sstevel {
16511341Sstevel fhc_bd_t *list;
16521341Sstevel sysc_cfga_stat_t *sysc_stat_lk;
16531341Sstevel
16541341Sstevel ASSERT(fhc_bdlist_locked());
16551341Sstevel
16561341Sstevel list = fhc_bd_clock();
16571341Sstevel
16581341Sstevel sysc_stat_lk = &list->sc;
16591341Sstevel ASSERT(sysc_stat_lk->in_transition == TRUE);
16601341Sstevel sysc_stat_lk->in_transition = FALSE;
16611341Sstevel }
1662