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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)21429949e86Sstevel _info(struct modinfo *modinfop)
21529949e86Sstevel {
21629949e86Sstevel return (mod_info(&modlinkage, modinfop));
21729949e86Sstevel }
21829949e86Sstevel
21929949e86Sstevel /* ARGSUSED */
22029949e86Sstevel static int
ac_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
ac_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
ac_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
ac_open(dev_t * devp,int flag,int otyp,cred_t * credp)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
ac_close(dev_t devt,int flag,int otyp,cred_t * credp)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
ac_pkt_init(ac_cfga_pkt_t * pkt,intptr_t arg,int flag)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
ac_pkt_fini(ac_cfga_pkt_t * pkt,intptr_t arg,int flag)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
ac_ioctl(dev_t devt,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)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
ac_add_kstats(struct ac_soft_state * softsp)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
ac_add_picN_kstats(dev_info_t * dip)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
ac_del_kstats(struct ac_soft_state * softsp)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
ac_kstat_stat(sysc_cfga_rstate_t rst,sysc_cfga_ostate_t ost)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
ac_kstat_cond(sysc_cfga_cond_t cond)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
ac_misc_kstat_update(kstat_t * ksp,int rw)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
ac_counters_kstat_update(kstat_t * ksp,int rw)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
ac_get_memory_status(struct ac_soft_state * softsp,enum ac_bank_id id)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
ac_eval_memory_status(struct ac_soft_state * softsp,enum ac_bank_id id)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
ac_ecache_flush(uint64_t a,uint64_t b)129629949e86Sstevel ac_ecache_flush(uint64_t a, uint64_t b)
129729949e86Sstevel {
129829949e86Sstevel cpu_flush_ecache();
129929949e86Sstevel }
130029949e86Sstevel
130129949e86Sstevel static char *
ac_ostate_typestr(sysc_cfga_ostate_t ostate,ac_audit_evt_t event)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
ac_policy_audit_messages(ac_audit_evt_t event,ac_cfga_pkt_t * pkt)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
ac_mem_exercise(ac_cfga_pkt_t * pkt,int flag)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
ac_reset_timeout(int rw)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
ac_timeout(void * arg)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
ac_enter_transition(void)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
ac_exit_transition(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