103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
23d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel /*
2803831d35Sstevel * IOSRAM leaf driver to SBBC nexus driver. This driver is used
2903831d35Sstevel * by Starcat Domain SW to read/write from/to the IO sram.
3003831d35Sstevel */
3103831d35Sstevel
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/conf.h>
3403831d35Sstevel #include <sys/ddi.h>
3503831d35Sstevel #include <sys/sunddi.h>
3603831d35Sstevel #include <sys/ddi_impldefs.h>
3703831d35Sstevel #include <sys/obpdefs.h>
3803831d35Sstevel #include <sys/promif.h>
3903831d35Sstevel #include <sys/prom_plat.h>
4003831d35Sstevel #include <sys/cmn_err.h>
4103831d35Sstevel #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
4203831d35Sstevel #include <sys/modctl.h> /* for modldrv */
4303831d35Sstevel #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */
4403831d35Sstevel #include <sys/errno.h>
4503831d35Sstevel #include <sys/kmem.h>
4603831d35Sstevel #include <sys/kstat.h>
4703831d35Sstevel #include <sys/debug.h>
4803831d35Sstevel
4903831d35Sstevel #include <sys/axq.h>
5003831d35Sstevel #include <sys/iosramreg.h>
5103831d35Sstevel #include <sys/iosramio.h>
5203831d35Sstevel #include <sys/iosramvar.h>
5303831d35Sstevel
5403831d35Sstevel
5503831d35Sstevel #if defined(DEBUG)
5603831d35Sstevel int iosram_debug = 0;
5703831d35Sstevel static void iosram_dprintf(const char *fmt, ...);
5803831d35Sstevel #define DPRINTF(level, arg) \
5903831d35Sstevel { if (iosram_debug >= level) iosram_dprintf arg; }
6003831d35Sstevel #else /* !DEBUG */
6103831d35Sstevel #define DPRINTF(level, arg)
6203831d35Sstevel #endif /* !DEBUG */
6303831d35Sstevel
6403831d35Sstevel
6503831d35Sstevel /*
6603831d35Sstevel * IOSRAM module global state
6703831d35Sstevel */
6803831d35Sstevel static void *iosramsoft_statep; /* IOSRAM state pointer */
6903831d35Sstevel static kmutex_t iosram_mutex; /* mutex lock */
7003831d35Sstevel
7103831d35Sstevel static iosram_chunk_t *chunks = NULL; /* array of TOC entries */
7203831d35Sstevel static int nchunks = 0; /* # of TOC entries */
7303831d35Sstevel static iosram_chunk_t *iosram_hashtab[IOSRAM_HASHSZ]; /* key hash table */
7403831d35Sstevel
7503831d35Sstevel static kcondvar_t iosram_tswitch_wait; /* tunnel switch wait cv */
7603831d35Sstevel static int iosram_tswitch_wakeup = 0; /* flag indicationg one or */
7703831d35Sstevel /* more threads waiting on */
7803831d35Sstevel /* iosram_tswitch_wait cv */
7903831d35Sstevel static int iosram_tswitch_active = 0; /* tunnel switch active flag */
8003831d35Sstevel static int iosram_tswitch_aborted = 0; /* tunnel switch abort flag */
8103831d35Sstevel static clock_t iosram_tswitch_tstamp = 0; /* lbolt of last tswitch end */
8203831d35Sstevel static kcondvar_t iosram_rw_wait; /* read/write wait cv */
8303831d35Sstevel static int iosram_rw_wakeup = 0; /* flag indicationg one or */
8403831d35Sstevel /* more threads waiting on */
8503831d35Sstevel /* iosram_rw_wait cv */
8603831d35Sstevel static int iosram_rw_active = 0; /* # threads accessing IOSRAM */
8703831d35Sstevel #if defined(DEBUG)
8803831d35Sstevel static int iosram_rw_active_max = 0;
8903831d35Sstevel #endif
9003831d35Sstevel
9103831d35Sstevel static struct iosramsoft *iosram_new_master = NULL; /* new tunnel target */
9203831d35Sstevel static struct iosramsoft *iosram_master = NULL; /* master tunnel */
9303831d35Sstevel static struct iosramsoft *iosram_instances = NULL; /* list of softstates */
9403831d35Sstevel
9503831d35Sstevel static ddi_acc_handle_t iosram_handle = NULL; /* master IOSRAM map handle */
9603831d35Sstevel
9703831d35Sstevel static void (*iosram_hdrchange_handler)() = NULL;
9803831d35Sstevel
9903831d35Sstevel #if IOSRAM_STATS
10003831d35Sstevel static struct iosram_stat iosram_stats; /* IOSRAM statistics */
10103831d35Sstevel static void iosram_print_stats(); /* forward declaration */
10203831d35Sstevel #endif /* IOSRAM_STATS */
10303831d35Sstevel
10403831d35Sstevel
10503831d35Sstevel #if IOSRAM_LOG
10603831d35Sstevel kmutex_t iosram_log_mutex;
10703831d35Sstevel int iosram_log_level = 1;
10803831d35Sstevel int iosram_log_print = 0; /* print log when recorded */
10903831d35Sstevel uint32_t iosram_logseq;
11003831d35Sstevel iosram_log_t iosram_logbuf[IOSRAM_MAXLOG];
11103831d35Sstevel static void iosram_print_log(int cnt); /* forward declaration */
11203831d35Sstevel #endif /* IOSRAM_LOG */
11303831d35Sstevel
11403831d35Sstevel
11503831d35Sstevel /* driver entry point fn definitions */
11603831d35Sstevel static int iosram_open(dev_t *, int, int, cred_t *);
11703831d35Sstevel static int iosram_close(dev_t, int, int, cred_t *);
11803831d35Sstevel static int iosram_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
11903831d35Sstevel
12003831d35Sstevel /* configuration entry point fn definitions */
12103831d35Sstevel static int iosram_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
12203831d35Sstevel static int iosram_attach(dev_info_t *, ddi_attach_cmd_t);
12303831d35Sstevel static int iosram_detach(dev_info_t *, ddi_detach_cmd_t);
12403831d35Sstevel
12503831d35Sstevel
12603831d35Sstevel /* forward declaractions */
12703831d35Sstevel static iosram_chunk_t *iosram_find_chunk(uint32_t key);
12803831d35Sstevel static void iosram_set_master(struct iosramsoft *softp);
12903831d35Sstevel static int iosram_is_chosen(struct iosramsoft *softp);
13003831d35Sstevel static int iosram_tunnel_capable(struct iosramsoft *softp);
13103831d35Sstevel static int iosram_read_toc(struct iosramsoft *softp);
13203831d35Sstevel static void iosram_init_hashtab(void);
13303831d35Sstevel static void iosram_update_addrs(struct iosramsoft *softp);
13403831d35Sstevel
13503831d35Sstevel static int iosram_setup_map(struct iosramsoft *softp);
13603831d35Sstevel static void iosram_remove_map(struct iosramsoft *softp);
13703831d35Sstevel static int iosram_add_intr(iosramsoft_t *);
13803831d35Sstevel static int iosram_remove_intr(iosramsoft_t *);
13903831d35Sstevel
14003831d35Sstevel static void iosram_add_instance(struct iosramsoft *softp);
14103831d35Sstevel static void iosram_remove_instance(int instance);
14203831d35Sstevel static int iosram_switch_tunnel(iosramsoft_t *softp);
14303831d35Sstevel static void iosram_abort_tswitch();
14403831d35Sstevel
14503831d35Sstevel #if defined(DEBUG)
14603831d35Sstevel /* forward declaractions for debugging */
14703831d35Sstevel static int iosram_get_keys(iosram_toc_entry_t *buf, uint32_t *len);
14803831d35Sstevel static void iosram_print_cback();
14903831d35Sstevel static void iosram_print_state(int);
15003831d35Sstevel static void iosram_print_flags();
15103831d35Sstevel #endif
15203831d35Sstevel
15303831d35Sstevel
15403831d35Sstevel
15503831d35Sstevel /*
15603831d35Sstevel * cb_ops
15703831d35Sstevel */
15803831d35Sstevel static struct cb_ops iosram_cb_ops = {
15903831d35Sstevel iosram_open, /* cb_open */
16003831d35Sstevel iosram_close, /* cb_close */
16103831d35Sstevel nodev, /* cb_strategy */
16203831d35Sstevel nodev, /* cb_print */
16303831d35Sstevel nodev, /* cb_dump */
16403831d35Sstevel nodev, /* cb_read */
16503831d35Sstevel nodev, /* cb_write */
16603831d35Sstevel iosram_ioctl, /* cb_ioctl */
16703831d35Sstevel nodev, /* cb_devmap */
16803831d35Sstevel nodev, /* cb_mmap */
16903831d35Sstevel nodev, /* cb_segmap */
17003831d35Sstevel nochpoll, /* cb_chpoll */
17103831d35Sstevel ddi_prop_op, /* cb_prop_op */
17203831d35Sstevel NULL, /* cb_stream */
17303831d35Sstevel (int)(D_NEW | D_MP | D_HOTPLUG) /* cb_flag */
17403831d35Sstevel };
17503831d35Sstevel
17603831d35Sstevel /*
17703831d35Sstevel * Declare ops vectors for auto configuration.
17803831d35Sstevel */
17903831d35Sstevel struct dev_ops iosram_ops = {
18003831d35Sstevel DEVO_REV, /* devo_rev */
18103831d35Sstevel 0, /* devo_refcnt */
18203831d35Sstevel iosram_getinfo, /* devo_getinfo */
18303831d35Sstevel nulldev, /* devo_identify */
18403831d35Sstevel nulldev, /* devo_probe */
18503831d35Sstevel iosram_attach, /* devo_attach */
18603831d35Sstevel iosram_detach, /* devo_detach */
18703831d35Sstevel nodev, /* devo_reset */
18803831d35Sstevel &iosram_cb_ops, /* devo_cb_ops */
18903831d35Sstevel (struct bus_ops *)NULL, /* devo_bus_ops */
19019397407SSherry Moore nulldev, /* devo_power */
19119397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */
19203831d35Sstevel };
19303831d35Sstevel
19403831d35Sstevel /*
19503831d35Sstevel * Loadable module support.
19603831d35Sstevel */
19703831d35Sstevel extern struct mod_ops mod_driverops;
19803831d35Sstevel
19903831d35Sstevel static struct modldrv iosrammodldrv = {
20003831d35Sstevel &mod_driverops, /* type of module - driver */
20119397407SSherry Moore "IOSRAM Leaf driver",
20203831d35Sstevel &iosram_ops,
20303831d35Sstevel };
20403831d35Sstevel
20503831d35Sstevel static struct modlinkage iosrammodlinkage = {
20603831d35Sstevel MODREV_1,
20703831d35Sstevel &iosrammodldrv,
20803831d35Sstevel NULL
20903831d35Sstevel };
21003831d35Sstevel
21103831d35Sstevel
21203831d35Sstevel int
_init(void)21303831d35Sstevel _init(void)
21403831d35Sstevel {
21503831d35Sstevel int error;
21603831d35Sstevel int i;
21703831d35Sstevel
21803831d35Sstevel mutex_init(&iosram_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
21903831d35Sstevel cv_init(&iosram_tswitch_wait, NULL, CV_DRIVER, NULL);
22003831d35Sstevel cv_init(&iosram_rw_wait, NULL, CV_DRIVER, NULL);
22103831d35Sstevel #if defined(IOSRAM_LOG)
22203831d35Sstevel mutex_init(&iosram_log_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
22303831d35Sstevel #endif
22403831d35Sstevel
22503831d35Sstevel DPRINTF(1, ("_init:IOSRAM\n"));
22603831d35Sstevel
22703831d35Sstevel for (i = 0; i < IOSRAM_HASHSZ; i++) {
22803831d35Sstevel iosram_hashtab[i] = NULL;
22903831d35Sstevel }
23003831d35Sstevel
23103831d35Sstevel if ((error = ddi_soft_state_init(&iosramsoft_statep,
23203831d35Sstevel sizeof (struct iosramsoft), 1)) != 0) {
23303831d35Sstevel goto failed;
23403831d35Sstevel }
23503831d35Sstevel if ((error = mod_install(&iosrammodlinkage)) != 0) {
23603831d35Sstevel ddi_soft_state_fini(&iosramsoft_statep);
23703831d35Sstevel goto failed;
23803831d35Sstevel }
23903831d35Sstevel
24003831d35Sstevel IOSRAMLOG(0, "_init:IOSRAM ... error:%d statep:%p\n",
24103831d35Sstevel error, iosramsoft_statep, NULL, NULL);
24203831d35Sstevel
24303831d35Sstevel return (error);
24403831d35Sstevel
24503831d35Sstevel failed:
24603831d35Sstevel cv_destroy(&iosram_tswitch_wait);
24703831d35Sstevel cv_destroy(&iosram_rw_wait);
24803831d35Sstevel mutex_destroy(&iosram_mutex);
24903831d35Sstevel #if defined(IOSRAM_LOG)
25003831d35Sstevel mutex_destroy(&iosram_log_mutex);
25103831d35Sstevel #endif
25203831d35Sstevel IOSRAMLOG(0, "_init:IOSRAM ... error:%d statep:%p\n",
25303831d35Sstevel error, iosramsoft_statep, NULL, NULL);
25403831d35Sstevel
25503831d35Sstevel return (error);
25603831d35Sstevel }
25703831d35Sstevel
25803831d35Sstevel
25903831d35Sstevel int
_fini(void)26003831d35Sstevel _fini(void)
26103831d35Sstevel {
26203831d35Sstevel #ifndef DEBUG
26303831d35Sstevel return (EBUSY);
26403831d35Sstevel #else /* !DEBUG */
26503831d35Sstevel int error;
26603831d35Sstevel
26703831d35Sstevel if ((error = mod_remove(&iosrammodlinkage)) == 0) {
26803831d35Sstevel ddi_soft_state_fini(&iosramsoft_statep);
26903831d35Sstevel
27003831d35Sstevel cv_destroy(&iosram_tswitch_wait);
27103831d35Sstevel cv_destroy(&iosram_rw_wait);
27203831d35Sstevel mutex_destroy(&iosram_mutex);
27303831d35Sstevel #if defined(IOSRAM_LOG)
27403831d35Sstevel mutex_destroy(&iosram_log_mutex);
27503831d35Sstevel #endif
27603831d35Sstevel }
27703831d35Sstevel DPRINTF(1, ("_fini:IOSRAM error:%d\n", error));
27803831d35Sstevel
27903831d35Sstevel return (error);
28003831d35Sstevel #endif /* !DEBUG */
28103831d35Sstevel }
28203831d35Sstevel
28303831d35Sstevel
28403831d35Sstevel int
_info(struct modinfo * modinfop)28503831d35Sstevel _info(struct modinfo *modinfop)
28603831d35Sstevel {
28703831d35Sstevel return (mod_info(&iosrammodlinkage, modinfop));
28803831d35Sstevel }
28903831d35Sstevel
29003831d35Sstevel
29103831d35Sstevel static int
iosram_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)29203831d35Sstevel iosram_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
29303831d35Sstevel {
29403831d35Sstevel int instance;
29503831d35Sstevel int propval;
29603831d35Sstevel int length;
29703831d35Sstevel char name[32];
29803831d35Sstevel struct iosramsoft *softp;
29903831d35Sstevel
30003831d35Sstevel instance = ddi_get_instance(dip);
30103831d35Sstevel
302*07d06da5SSurya Prakki DPRINTF(1, ("iosram(%d): attach dip:%p\n", instance, (void *)dip));
30303831d35Sstevel
30403831d35Sstevel IOSRAMLOG(1, "ATTACH: dip:%p instance %d ... start\n",
30503831d35Sstevel dip, instance, NULL, NULL);
30603831d35Sstevel switch (cmd) {
30703831d35Sstevel case DDI_ATTACH:
30803831d35Sstevel break;
30903831d35Sstevel case DDI_RESUME:
31003831d35Sstevel if (!(softp = ddi_get_soft_state(iosramsoft_statep,
31103831d35Sstevel instance))) {
31203831d35Sstevel return (DDI_FAILURE);
31303831d35Sstevel }
31403831d35Sstevel mutex_enter(&iosram_mutex);
31503831d35Sstevel mutex_enter(&softp->intr_mutex);
31603831d35Sstevel if (!softp->suspended) {
31703831d35Sstevel mutex_exit(&softp->intr_mutex);
31803831d35Sstevel mutex_exit(&iosram_mutex);
31903831d35Sstevel return (DDI_FAILURE);
32003831d35Sstevel }
32103831d35Sstevel softp->suspended = 0;
32203831d35Sstevel
32303831d35Sstevel /*
32403831d35Sstevel * enable SBBC interrupts if SBBC is mapped in
32503831d35Sstevel * restore the value saved during detach
32603831d35Sstevel */
32703831d35Sstevel if (softp->sbbc_region) {
32803831d35Sstevel ddi_put32(softp->sbbc_handle,
32903831d35Sstevel &(softp->sbbc_region->int_enable.reg),
33003831d35Sstevel softp->int_enable_sav);
33103831d35Sstevel }
33203831d35Sstevel
33303831d35Sstevel /*
33403831d35Sstevel * Trigger soft interrupt handler to process any pending
33503831d35Sstevel * interrupts.
33603831d35Sstevel */
33703831d35Sstevel if (softp->intr_pending && !softp->intr_busy &&
33803831d35Sstevel (softp->softintr_id != NULL)) {
33903831d35Sstevel ddi_trigger_softintr(softp->softintr_id);
34003831d35Sstevel }
34103831d35Sstevel
34203831d35Sstevel mutex_exit(&softp->intr_mutex);
34303831d35Sstevel mutex_exit(&iosram_mutex);
34403831d35Sstevel
34503831d35Sstevel return (DDI_SUCCESS);
34603831d35Sstevel
34703831d35Sstevel default:
34803831d35Sstevel return (DDI_FAILURE);
34903831d35Sstevel }
35003831d35Sstevel
35103831d35Sstevel if (ddi_soft_state_zalloc(iosramsoft_statep, instance) != 0) {
35203831d35Sstevel return (DDI_FAILURE);
35303831d35Sstevel }
35403831d35Sstevel
35503831d35Sstevel if ((softp = ddi_get_soft_state(iosramsoft_statep, instance)) == NULL) {
35603831d35Sstevel return (DDI_FAILURE);
35703831d35Sstevel }
35803831d35Sstevel softp->dip = dip;
35903831d35Sstevel softp->instance = instance;
36003831d35Sstevel softp->sbbc_region = NULL;
36103831d35Sstevel
36203831d35Sstevel /*
36303831d35Sstevel * If this instance is not tunnel capable, we don't attach it.
36403831d35Sstevel */
36503831d35Sstevel if (iosram_tunnel_capable(softp) == 0) {
36603831d35Sstevel DPRINTF(1, ("iosram(%d): not tunnel_capable\n", instance));
36703831d35Sstevel IOSRAMLOG(1, "ATTACH(%d): not tunnel_capable\n", instance, NULL,
36803831d35Sstevel NULL, NULL);
36903831d35Sstevel goto attach_fail;
37003831d35Sstevel }
37103831d35Sstevel
37203831d35Sstevel /*
37303831d35Sstevel * Need to create an "interrupt-priorities" property to define the PIL
37403831d35Sstevel * to be used with the interrupt service routine.
37503831d35Sstevel */
37603831d35Sstevel if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
37703831d35Sstevel "interrupt-priorities", &length) == DDI_PROP_NOT_FOUND) {
37803831d35Sstevel DPRINTF(1, ("iosram(%d): creating interrupt priority property",
37903831d35Sstevel instance));
38003831d35Sstevel propval = IOSRAM_PIL;
38103831d35Sstevel if (ddi_prop_create(DDI_DEV_T_NONE, dip, 0,
38203831d35Sstevel "interrupt-priorities", (caddr_t)&propval, sizeof (propval))
38303831d35Sstevel != DDI_PROP_SUCCESS) {
38403831d35Sstevel cmn_err(CE_WARN,
38503831d35Sstevel "iosram_attach: failed to create property");
38603831d35Sstevel goto attach_fail;
38703831d35Sstevel }
38803831d35Sstevel }
38903831d35Sstevel
39003831d35Sstevel /*
39103831d35Sstevel * Get interrupts cookies and initialize per-instance mutexes
39203831d35Sstevel */
39303831d35Sstevel if (ddi_get_iblock_cookie(softp->dip, 0, &softp->real_iblk)
39403831d35Sstevel != DDI_SUCCESS) {
39503831d35Sstevel IOSRAMLOG(1, "ATTACH(%d): cannot get soft intr cookie\n",
39603831d35Sstevel instance, NULL, NULL, NULL);
39703831d35Sstevel goto attach_fail;
39803831d35Sstevel }
39903831d35Sstevel mutex_init(&softp->intr_mutex, NULL, MUTEX_DRIVER,
40003831d35Sstevel (void *)softp->real_iblk);
40103831d35Sstevel
40203831d35Sstevel /*
40303831d35Sstevel * Add this instance to the iosram_instances list so that it can be used
40403831d35Sstevel * for tunnel in future.
40503831d35Sstevel */
40603831d35Sstevel mutex_enter(&iosram_mutex);
40703831d35Sstevel softp->state = IOSRAM_STATE_INIT;
40803831d35Sstevel iosram_add_instance(softp);
40903831d35Sstevel
41003831d35Sstevel /*
41103831d35Sstevel * If this is the chosen IOSRAM and there is no master IOSRAM yet, then
41203831d35Sstevel * let's set this instance as the master.
41303831d35Sstevel */
41403831d35Sstevel if (iosram_master == NULL && iosram_is_chosen(softp)) {
415*07d06da5SSurya Prakki (void) iosram_switch_tunnel(softp);
41603831d35Sstevel
41703831d35Sstevel /*
41803831d35Sstevel * XXX Do we need to panic if unable to setup master IOSRAM?
41903831d35Sstevel */
42003831d35Sstevel if (iosram_master == NULL) {
42103831d35Sstevel cmn_err(CE_WARN,
42203831d35Sstevel "iosram(%d): can't setup master tunnel\n",
42303831d35Sstevel instance);
42403831d35Sstevel softp->state = 0;
42503831d35Sstevel iosram_remove_instance(softp->instance);
42603831d35Sstevel mutex_exit(&iosram_mutex);
42703831d35Sstevel mutex_destroy(&softp->intr_mutex);
42803831d35Sstevel goto attach_fail;
42903831d35Sstevel }
43003831d35Sstevel }
43103831d35Sstevel
43203831d35Sstevel mutex_exit(&iosram_mutex);
43303831d35Sstevel
43403831d35Sstevel /*
43503831d35Sstevel * Create minor node
43603831d35Sstevel */
43703831d35Sstevel (void) sprintf(name, "iosram%d", instance);
43803831d35Sstevel if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL, NULL) ==
43903831d35Sstevel DDI_FAILURE) {
44003831d35Sstevel /*
44103831d35Sstevel * Minor node seems to be needed only for debugging purposes.
44203831d35Sstevel * Therefore, there is no need to fail this attach request.
44303831d35Sstevel * Simply print a message out.
44403831d35Sstevel */
44503831d35Sstevel cmn_err(CE_NOTE, "!iosram(%d): can't create minor node\n",
44603831d35Sstevel instance);
44703831d35Sstevel }
44803831d35Sstevel ddi_report_dev(dip);
44903831d35Sstevel
45003831d35Sstevel DPRINTF(1, ("iosram_attach(%d): success.\n", instance));
45103831d35Sstevel IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... success softp:%p\n",
45203831d35Sstevel dip, instance, softp, NULL);
45303831d35Sstevel
45403831d35Sstevel return (DDI_SUCCESS);
45503831d35Sstevel
45603831d35Sstevel attach_fail:
45703831d35Sstevel DPRINTF(1, ("iosram_attach(%d):failed.\n", instance));
45803831d35Sstevel IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... failed.\n",
45903831d35Sstevel dip, instance, NULL, NULL);
46003831d35Sstevel
46103831d35Sstevel ddi_soft_state_free(iosramsoft_statep, instance);
46203831d35Sstevel return (DDI_FAILURE);
46303831d35Sstevel }
46403831d35Sstevel
46503831d35Sstevel
46603831d35Sstevel static int
iosram_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)46703831d35Sstevel iosram_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
46803831d35Sstevel {
46903831d35Sstevel int instance;
47003831d35Sstevel struct iosramsoft *softp;
47103831d35Sstevel
47203831d35Sstevel instance = ddi_get_instance(dip);
47303831d35Sstevel if (!(softp = ddi_get_soft_state(iosramsoft_statep, instance))) {
47403831d35Sstevel return (DDI_FAILURE);
47503831d35Sstevel }
47603831d35Sstevel
47703831d35Sstevel IOSRAMLOG(1, "DETACH: dip:%p instance %d softp:%p\n",
47803831d35Sstevel dip, instance, softp, NULL);
47903831d35Sstevel
48003831d35Sstevel switch (cmd) {
48103831d35Sstevel case DDI_DETACH:
48203831d35Sstevel break;
48303831d35Sstevel case DDI_SUSPEND:
48403831d35Sstevel mutex_enter(&iosram_mutex);
48503831d35Sstevel mutex_enter(&softp->intr_mutex);
48603831d35Sstevel if (softp->suspended) {
48703831d35Sstevel mutex_exit(&softp->intr_mutex);
48803831d35Sstevel mutex_exit(&iosram_mutex);
48903831d35Sstevel return (DDI_FAILURE);
49003831d35Sstevel }
49103831d35Sstevel softp->suspended = 1;
49203831d35Sstevel /*
49303831d35Sstevel * Disable SBBC interrupts if SBBC is mapped in
49403831d35Sstevel */
49503831d35Sstevel if (softp->sbbc_region) {
49603831d35Sstevel /* save current interrupt enable register */
49703831d35Sstevel softp->int_enable_sav = ddi_get32(softp->sbbc_handle,
49803831d35Sstevel &(softp->sbbc_region->int_enable.reg));
49903831d35Sstevel ddi_put32(softp->sbbc_handle,
50003831d35Sstevel &(softp->sbbc_region->int_enable.reg), 0x0);
50103831d35Sstevel }
50203831d35Sstevel mutex_exit(&softp->intr_mutex);
50303831d35Sstevel mutex_exit(&iosram_mutex);
50403831d35Sstevel return (DDI_SUCCESS);
50503831d35Sstevel
50603831d35Sstevel default:
50703831d35Sstevel return (DDI_FAILURE);
50803831d35Sstevel }
50903831d35Sstevel
51003831d35Sstevel
51103831d35Sstevel /*
51203831d35Sstevel * Indicate that this instance is being detached so that this instance
51303831d35Sstevel * does not become a target for tunnel switch in future.
51403831d35Sstevel */
51503831d35Sstevel mutex_enter(&iosram_mutex);
51603831d35Sstevel softp->state |= IOSRAM_STATE_DETACH;
51703831d35Sstevel
51803831d35Sstevel /*
51903831d35Sstevel * If this instance is currently the master or the target of the tunnel
52003831d35Sstevel * switch, then we need to wait and switch tunnel, if necessary.
52103831d35Sstevel */
52203831d35Sstevel if (iosram_master == softp || (softp->state & IOSRAM_STATE_TSWITCH)) {
52303831d35Sstevel mutex_exit(&iosram_mutex);
524*07d06da5SSurya Prakki (void) iosram_switchfrom(instance);
52503831d35Sstevel mutex_enter(&iosram_mutex);
52603831d35Sstevel }
52703831d35Sstevel
52803831d35Sstevel /*
52903831d35Sstevel * If the tunnel switch is in progress and we are the master or target
53003831d35Sstevel * of tunnel relocation, then we can't detach this instance right now.
53103831d35Sstevel */
53203831d35Sstevel if (softp->state & IOSRAM_STATE_TSWITCH) {
53303831d35Sstevel softp->state &= ~IOSRAM_STATE_DETACH;
53403831d35Sstevel mutex_exit(&iosram_mutex);
53503831d35Sstevel return (DDI_FAILURE);
53603831d35Sstevel }
53703831d35Sstevel
53803831d35Sstevel /*
53903831d35Sstevel * We can't allow master IOSRAM to be detached as we won't be able to
54003831d35Sstevel * communicate otherwise.
54103831d35Sstevel */
54203831d35Sstevel if (iosram_master == softp) {
54303831d35Sstevel softp->state &= ~IOSRAM_STATE_DETACH;
54403831d35Sstevel mutex_exit(&iosram_mutex);
54503831d35Sstevel return (DDI_FAILURE);
54603831d35Sstevel }
54703831d35Sstevel
54803831d35Sstevel /*
54903831d35Sstevel * Now remove our instance from the iosram_instances list.
55003831d35Sstevel */
55103831d35Sstevel iosram_remove_instance(instance);
55203831d35Sstevel mutex_exit(&iosram_mutex);
55303831d35Sstevel
55403831d35Sstevel /*
55503831d35Sstevel * Instances should only ever be mapped if they are the master and/or
55603831d35Sstevel * participating in a tunnel switch. Neither should be the case here.
55703831d35Sstevel */
55803831d35Sstevel ASSERT((softp->state & IOSRAM_STATE_MAPPED) == 0);
55903831d35Sstevel
56003831d35Sstevel /*
56103831d35Sstevel * Destroy per-instance mutexes
56203831d35Sstevel */
56303831d35Sstevel mutex_destroy(&softp->intr_mutex);
56403831d35Sstevel
56503831d35Sstevel ddi_remove_minor_node(dip, NULL);
56603831d35Sstevel
56703831d35Sstevel /*
56803831d35Sstevel * Finally remove our soft state structure
56903831d35Sstevel */
57003831d35Sstevel ddi_soft_state_free(iosramsoft_statep, instance);
57103831d35Sstevel
57203831d35Sstevel return (DDI_SUCCESS);
57303831d35Sstevel }
57403831d35Sstevel
57503831d35Sstevel
57603831d35Sstevel /* ARGSUSED0 */
57703831d35Sstevel static int
iosram_getinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)57803831d35Sstevel iosram_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
57903831d35Sstevel void **result)
58003831d35Sstevel {
58103831d35Sstevel dev_t dev = (dev_t)arg;
58203831d35Sstevel struct iosramsoft *softp;
58303831d35Sstevel int instance, ret;
58403831d35Sstevel
58503831d35Sstevel instance = getminor(dev);
58603831d35Sstevel
58703831d35Sstevel IOSRAMLOG(2, "GETINFO: dip:%x instance %d dev:%x infocmd:%x\n",
58803831d35Sstevel dip, instance, dev, infocmd);
58903831d35Sstevel
59003831d35Sstevel switch (infocmd) {
59103831d35Sstevel case DDI_INFO_DEVT2DEVINFO:
59203831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance);
59303831d35Sstevel if (softp == NULL) {
59403831d35Sstevel *result = NULL;
59503831d35Sstevel ret = DDI_FAILURE;
59603831d35Sstevel } else {
59703831d35Sstevel *result = softp->dip;
59803831d35Sstevel ret = DDI_SUCCESS;
59903831d35Sstevel }
60003831d35Sstevel break;
60103831d35Sstevel case DDI_INFO_DEVT2INSTANCE:
60203831d35Sstevel *result = (void *)(uintptr_t)instance;
60303831d35Sstevel ret = DDI_SUCCESS;
60403831d35Sstevel break;
60503831d35Sstevel default:
60603831d35Sstevel ret = DDI_FAILURE;
60703831d35Sstevel break;
60803831d35Sstevel }
60903831d35Sstevel
61003831d35Sstevel return (ret);
61103831d35Sstevel }
61203831d35Sstevel
61303831d35Sstevel
61403831d35Sstevel /*ARGSUSED1*/
61503831d35Sstevel static int
iosram_open(dev_t * dev,int flag,int otype,cred_t * credp)61603831d35Sstevel iosram_open(dev_t *dev, int flag, int otype, cred_t *credp)
61703831d35Sstevel {
61803831d35Sstevel struct iosramsoft *softp;
61903831d35Sstevel int instance;
62003831d35Sstevel
62103831d35Sstevel instance = getminor(*dev);
62203831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance);
62303831d35Sstevel
62403831d35Sstevel if (softp == NULL) {
62503831d35Sstevel return (ENXIO);
62603831d35Sstevel }
62703831d35Sstevel
62803831d35Sstevel IOSRAMLOG(1, "OPEN: dev:%p otype:%x ... instance:%d softp:%p\n",
62903831d35Sstevel *dev, otype, softp->instance, softp);
63003831d35Sstevel
63103831d35Sstevel return (0);
63203831d35Sstevel }
63303831d35Sstevel
63403831d35Sstevel
63503831d35Sstevel /*ARGSUSED1*/
63603831d35Sstevel static int
iosram_close(dev_t dev,int flag,int otype,cred_t * credp)63703831d35Sstevel iosram_close(dev_t dev, int flag, int otype, cred_t *credp)
63803831d35Sstevel {
63903831d35Sstevel struct iosramsoft *softp;
64003831d35Sstevel int instance;
64103831d35Sstevel
64203831d35Sstevel instance = getminor(dev);
64303831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance);
64403831d35Sstevel if (softp == NULL) {
64503831d35Sstevel return (ENXIO);
64603831d35Sstevel }
64703831d35Sstevel
64803831d35Sstevel IOSRAMLOG(1, "CLOSE: dev:%p otype:%x ... instance:%d softp:%p\n",
64903831d35Sstevel dev, otype, softp->instance, softp);
65003831d35Sstevel
65103831d35Sstevel return (0);
65203831d35Sstevel }
65303831d35Sstevel
65403831d35Sstevel
65503831d35Sstevel int
iosram_rd(uint32_t key,uint32_t off,uint32_t len,caddr_t dptr)65603831d35Sstevel iosram_rd(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
65703831d35Sstevel {
65803831d35Sstevel iosram_chunk_t *chunkp;
65903831d35Sstevel uint32_t chunk_len;
66003831d35Sstevel uint8_t *iosramp;
66103831d35Sstevel ddi_acc_handle_t handle;
66203831d35Sstevel int boff;
66303831d35Sstevel union {
66403831d35Sstevel uchar_t cbuf[UINT32SZ];
66503831d35Sstevel uint32_t data;
66603831d35Sstevel } word;
66703831d35Sstevel
66803831d35Sstevel int error = 0;
66903831d35Sstevel uint8_t *buf = (uint8_t *)dptr;
67003831d35Sstevel
67103831d35Sstevel /*
67203831d35Sstevel * We try to read from the IOSRAM using double word or word access
67303831d35Sstevel * provided both "off" and "buf" are (or can be) double word or word
67403831d35Sstevel * aligned. Othewise, we try to align the "off" to a word boundary and
67503831d35Sstevel * then try to read data from the IOSRAM using word access, but store it
67603831d35Sstevel * into buf buffer using byte access.
67703831d35Sstevel *
67803831d35Sstevel * If the leading/trailing portion of the IOSRAM data is not word
67903831d35Sstevel * aligned, it will always be copied using byte access.
68003831d35Sstevel */
68103831d35Sstevel IOSRAMLOG(1, "RD: key: 0x%x off:%x len:%x buf:%p\n",
68203831d35Sstevel key, off, len, buf);
68303831d35Sstevel
68403831d35Sstevel /*
68503831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, make
68603831d35Sstevel * sure the requested read is within the chunk's bounds and no tunnel
68703831d35Sstevel * switch is active.
68803831d35Sstevel */
68903831d35Sstevel mutex_enter(&iosram_mutex);
69003831d35Sstevel chunkp = iosram_find_chunk(key);
69103831d35Sstevel chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0;
69203831d35Sstevel
69303831d35Sstevel if (iosram_master == NULL) {
69403831d35Sstevel error = EIO;
69503831d35Sstevel } else if (chunkp == NULL) {
69603831d35Sstevel error = EINVAL;
69703831d35Sstevel } else if ((off >= chunk_len) || (len > chunk_len) ||
69803831d35Sstevel ((off + len) > chunk_len)) {
69903831d35Sstevel error = EMSGSIZE;
70003831d35Sstevel } else if (iosram_tswitch_active) {
70103831d35Sstevel error = EAGAIN;
70203831d35Sstevel }
70303831d35Sstevel
70403831d35Sstevel if (error) {
70503831d35Sstevel mutex_exit(&iosram_mutex);
70603831d35Sstevel return (error);
70703831d35Sstevel }
70803831d35Sstevel
70903831d35Sstevel /*
71003831d35Sstevel * Bump reference count to indicate #thread accessing IOSRAM and release
71103831d35Sstevel * the lock.
71203831d35Sstevel */
71303831d35Sstevel iosram_rw_active++;
71403831d35Sstevel #if defined(DEBUG)
71503831d35Sstevel if (iosram_rw_active > iosram_rw_active_max) {
71603831d35Sstevel iosram_rw_active_max = iosram_rw_active;
71703831d35Sstevel }
71803831d35Sstevel #endif
71903831d35Sstevel mutex_exit(&iosram_mutex);
72003831d35Sstevel
72103831d35Sstevel IOSRAM_STAT(read);
72203831d35Sstevel IOSRAM_STAT_ADD(bread, len);
72303831d35Sstevel
72403831d35Sstevel /* Get starting address and map handle */
72503831d35Sstevel iosramp = chunkp->basep + off;
72603831d35Sstevel handle = iosram_handle;
72703831d35Sstevel
72803831d35Sstevel /*
72903831d35Sstevel * Align the off to word boundary and then try reading/writing data
73003831d35Sstevel * using double word or word access.
73103831d35Sstevel */
73203831d35Sstevel if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) {
73303831d35Sstevel int cnt = UINT32SZ - boff;
73403831d35Sstevel
73503831d35Sstevel if (cnt > len) {
73603831d35Sstevel cnt = len;
73703831d35Sstevel }
73803831d35Sstevel IOSRAMLOG(2,
73903831d35Sstevel "RD: align rep_get8(buf:%p sramp:%p cnt:%x) len:%x\n",
74003831d35Sstevel buf, iosramp, cnt, len);
74103831d35Sstevel ddi_rep_get8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR);
74203831d35Sstevel buf += cnt;
74303831d35Sstevel iosramp += cnt;
74403831d35Sstevel len -= cnt;
74503831d35Sstevel }
74603831d35Sstevel
74703831d35Sstevel if ((len >= UINT64SZ) &&
74803831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) {
74903831d35Sstevel /*
75003831d35Sstevel * Both source and destination are double word aligned
75103831d35Sstevel */
75203831d35Sstevel int cnt = len/UINT64SZ;
75303831d35Sstevel
75403831d35Sstevel IOSRAMLOG(2,
75503831d35Sstevel "RD: rep_get64(buf:%p sramp:%p cnt:%x) len:%x\n",
75603831d35Sstevel buf, iosramp, cnt, len);
75703831d35Sstevel ddi_rep_get64(handle, (uint64_t *)buf, (uint64_t *)iosramp,
75803831d35Sstevel cnt, DDI_DEV_AUTOINCR);
75903831d35Sstevel iosramp += cnt * UINT64SZ;
76003831d35Sstevel buf += cnt * UINT64SZ;
76103831d35Sstevel len -= cnt * UINT64SZ;
76203831d35Sstevel
76303831d35Sstevel /*
76403831d35Sstevel * read remaining data using word and byte access
76503831d35Sstevel */
76603831d35Sstevel if (len >= UINT32SZ) {
76703831d35Sstevel IOSRAMLOG(2,
76803831d35Sstevel "RD: get32(buf:%p sramp:%p) len:%x\n",
76903831d35Sstevel buf, iosramp, len, NULL);
77003831d35Sstevel *(uint32_t *)buf = ddi_get32(handle,
77103831d35Sstevel (uint32_t *)iosramp);
77203831d35Sstevel iosramp += UINT32SZ;
77303831d35Sstevel buf += UINT32SZ;
77403831d35Sstevel len -= UINT32SZ;
77503831d35Sstevel }
77603831d35Sstevel
77703831d35Sstevel if (len != 0) {
77819397407SSherry Moore ddi_rep_get8(handle, buf, iosramp, len,
77919397407SSherry Moore DDI_DEV_AUTOINCR);
78003831d35Sstevel }
78103831d35Sstevel } else if ((len >= UINT32SZ) &&
78203831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) {
78303831d35Sstevel /*
78403831d35Sstevel * Both source and destination are word aligned
78503831d35Sstevel */
78603831d35Sstevel int cnt = len/UINT32SZ;
78703831d35Sstevel
78803831d35Sstevel IOSRAMLOG(2,
78903831d35Sstevel "RD: rep_get32(buf:%p sramp:%p cnt:%x) len:%x\n",
79003831d35Sstevel buf, iosramp, cnt, len);
79103831d35Sstevel ddi_rep_get32(handle, (uint32_t *)buf, (uint32_t *)iosramp,
79203831d35Sstevel cnt, DDI_DEV_AUTOINCR);
79303831d35Sstevel iosramp += cnt * UINT32SZ;
79403831d35Sstevel buf += cnt * UINT32SZ;
79503831d35Sstevel len -= cnt * UINT32SZ;
79603831d35Sstevel
79703831d35Sstevel /*
79803831d35Sstevel * copy the remainder using byte access
79903831d35Sstevel */
80003831d35Sstevel if (len != 0) {
80119397407SSherry Moore ddi_rep_get8(handle, buf, iosramp, len,
80219397407SSherry Moore DDI_DEV_AUTOINCR);
80303831d35Sstevel }
80403831d35Sstevel } else if (len != 0) {
80503831d35Sstevel /*
80603831d35Sstevel * We know that the "off" (i.e. iosramp) is at least word
80703831d35Sstevel * aligned. We need to read IOSRAM word at a time and copy it
80803831d35Sstevel * byte at a time.
80903831d35Sstevel */
81003831d35Sstevel ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0);
81103831d35Sstevel
81203831d35Sstevel IOSRAMLOG(2,
81303831d35Sstevel "RD: unaligned get32(buf:%p sramp:%p) len:%x\n",
81403831d35Sstevel buf, iosramp, len, NULL);
81503831d35Sstevel for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) {
81603831d35Sstevel word.data = ddi_get32(handle, (uint32_t *)iosramp);
81703831d35Sstevel *buf++ = word.cbuf[0];
81803831d35Sstevel *buf++ = word.cbuf[1];
81903831d35Sstevel *buf++ = word.cbuf[2];
82003831d35Sstevel *buf++ = word.cbuf[3];
82103831d35Sstevel }
82203831d35Sstevel
82303831d35Sstevel /*
82403831d35Sstevel * copy the remaining data using byte access
82503831d35Sstevel */
82603831d35Sstevel if (len != 0) {
82703831d35Sstevel ddi_rep_get8(handle, buf, iosramp, len,
82803831d35Sstevel DDI_DEV_AUTOINCR);
82903831d35Sstevel }
83003831d35Sstevel }
83103831d35Sstevel
83203831d35Sstevel /*
83303831d35Sstevel * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and any
83403831d35Sstevel * threads are waiting for r/w activity to complete, wake them up.
83503831d35Sstevel */
83603831d35Sstevel mutex_enter(&iosram_mutex);
83703831d35Sstevel ASSERT(iosram_rw_active > 0);
83803831d35Sstevel
83903831d35Sstevel if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
84003831d35Sstevel iosram_rw_wakeup = 0;
84103831d35Sstevel cv_broadcast(&iosram_rw_wait);
84203831d35Sstevel }
84303831d35Sstevel mutex_exit(&iosram_mutex);
84403831d35Sstevel
84503831d35Sstevel return (error);
84603831d35Sstevel }
84703831d35Sstevel
84803831d35Sstevel
84903831d35Sstevel /*
85003831d35Sstevel * _iosram_write(key, off, len, dptr, force)
85103831d35Sstevel * Internal common routine to write to the IOSRAM.
85203831d35Sstevel */
85303831d35Sstevel static int
_iosram_write(uint32_t key,uint32_t off,uint32_t len,caddr_t dptr,int force)85403831d35Sstevel _iosram_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr, int force)
85503831d35Sstevel {
85603831d35Sstevel iosram_chunk_t *chunkp;
85703831d35Sstevel uint32_t chunk_len;
85803831d35Sstevel uint8_t *iosramp;
85903831d35Sstevel ddi_acc_handle_t handle;
86003831d35Sstevel int boff;
86103831d35Sstevel union {
86203831d35Sstevel uint8_t cbuf[UINT32SZ];
86303831d35Sstevel uint32_t data;
86403831d35Sstevel } word;
86503831d35Sstevel
86603831d35Sstevel int error = 0;
86703831d35Sstevel uint8_t *buf = (uint8_t *)dptr;
86803831d35Sstevel
86903831d35Sstevel /*
87003831d35Sstevel * We try to write to the IOSRAM using double word or word access
87103831d35Sstevel * provided both "off" and "buf" are (or can be) double word or word
87203831d35Sstevel * aligned. Othewise, we try to align the "off" to a word boundary and
87303831d35Sstevel * then try to write data to the IOSRAM using word access, but read data
87403831d35Sstevel * from the buf buffer using byte access.
87503831d35Sstevel *
87603831d35Sstevel * If the leading/trailing portion of the IOSRAM data is not word
87703831d35Sstevel * aligned, it will always be written using byte access.
87803831d35Sstevel */
87903831d35Sstevel IOSRAMLOG(1, "WR: key: 0x%x off:%x len:%x buf:%p\n",
88003831d35Sstevel key, off, len, buf);
88103831d35Sstevel
88203831d35Sstevel /*
88303831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, make
88403831d35Sstevel * sure the requested write is within the chunk's bounds and no tunnel
88503831d35Sstevel * switch is active.
88603831d35Sstevel */
88703831d35Sstevel mutex_enter(&iosram_mutex);
88803831d35Sstevel chunkp = iosram_find_chunk(key);
88903831d35Sstevel chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0;
89003831d35Sstevel
89103831d35Sstevel if (iosram_master == NULL) {
89203831d35Sstevel error = EIO;
89303831d35Sstevel } else if (chunkp == NULL) {
89403831d35Sstevel error = EINVAL;
89503831d35Sstevel } else if ((off >= chunk_len) || (len > chunk_len) ||
89603831d35Sstevel ((off+len) > chunk_len)) {
89703831d35Sstevel error = EMSGSIZE;
89803831d35Sstevel } else if (iosram_tswitch_active && !force) {
89903831d35Sstevel error = EAGAIN;
90003831d35Sstevel }
90103831d35Sstevel
90203831d35Sstevel if (error) {
90303831d35Sstevel mutex_exit(&iosram_mutex);
90403831d35Sstevel return (error);
90503831d35Sstevel }
90603831d35Sstevel
90703831d35Sstevel /*
90803831d35Sstevel * If this is a forced write and there's a tunnel switch in progress,
90903831d35Sstevel * abort the switch.
91003831d35Sstevel */
91103831d35Sstevel if (iosram_tswitch_active && force) {
91203831d35Sstevel cmn_err(CE_NOTE, "!iosram: Aborting tswitch on force_write");
91303831d35Sstevel iosram_abort_tswitch();
91403831d35Sstevel }
91503831d35Sstevel
91603831d35Sstevel /*
91703831d35Sstevel * Bump reference count to indicate #thread accessing IOSRAM
91803831d35Sstevel * and release the lock.
91903831d35Sstevel */
92003831d35Sstevel iosram_rw_active++;
92103831d35Sstevel #if defined(DEBUG)
92203831d35Sstevel if (iosram_rw_active > iosram_rw_active_max) {
92303831d35Sstevel iosram_rw_active_max = iosram_rw_active;
92403831d35Sstevel }
92503831d35Sstevel #endif
92603831d35Sstevel mutex_exit(&iosram_mutex);
92703831d35Sstevel
92803831d35Sstevel
92903831d35Sstevel IOSRAM_STAT(write);
93003831d35Sstevel IOSRAM_STAT_ADD(bwrite, len);
93103831d35Sstevel
93203831d35Sstevel /* Get starting address and map handle */
93303831d35Sstevel iosramp = chunkp->basep + off;
93403831d35Sstevel handle = iosram_handle;
93503831d35Sstevel
93603831d35Sstevel /*
93703831d35Sstevel * Align the off to word boundary and then try reading/writing
93803831d35Sstevel * data using double word or word access.
93903831d35Sstevel */
94003831d35Sstevel if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) {
94103831d35Sstevel int cnt = UINT32SZ - boff;
94203831d35Sstevel
94303831d35Sstevel if (cnt > len) {
94403831d35Sstevel cnt = len;
94503831d35Sstevel }
94603831d35Sstevel IOSRAMLOG(2,
94703831d35Sstevel "WR: align rep_put8(buf:%p sramp:%p cnt:%x) len:%x\n",
94803831d35Sstevel buf, iosramp, cnt, len);
94903831d35Sstevel ddi_rep_put8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR);
95003831d35Sstevel buf += cnt;
95103831d35Sstevel iosramp += cnt;
95203831d35Sstevel len -= cnt;
95303831d35Sstevel }
95403831d35Sstevel
95503831d35Sstevel if ((len >= UINT64SZ) &&
95603831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) {
95703831d35Sstevel /*
95803831d35Sstevel * Both source and destination are double word aligned
95903831d35Sstevel */
96003831d35Sstevel int cnt = len/UINT64SZ;
96103831d35Sstevel
96203831d35Sstevel IOSRAMLOG(2,
96303831d35Sstevel "WR: rep_put64(buf:%p sramp:%p cnt:%x) len:%x\n",
96403831d35Sstevel buf, iosramp, cnt, len);
96503831d35Sstevel ddi_rep_put64(handle, (uint64_t *)buf, (uint64_t *)iosramp,
96603831d35Sstevel cnt, DDI_DEV_AUTOINCR);
96703831d35Sstevel iosramp += cnt * UINT64SZ;
96803831d35Sstevel buf += cnt * UINT64SZ;
96903831d35Sstevel len -= cnt * UINT64SZ;
97003831d35Sstevel
97103831d35Sstevel /*
97203831d35Sstevel * Copy the remaining data using word & byte access
97303831d35Sstevel */
97403831d35Sstevel if (len >= UINT32SZ) {
97503831d35Sstevel IOSRAMLOG(2,
97603831d35Sstevel "WR: put32(buf:%p sramp:%p) len:%x\n", buf, iosramp,
97703831d35Sstevel len, NULL);
97803831d35Sstevel ddi_put32(handle, (uint32_t *)iosramp,
97903831d35Sstevel *(uint32_t *)buf);
98003831d35Sstevel iosramp += UINT32SZ;
98103831d35Sstevel buf += UINT32SZ;
98203831d35Sstevel len -= UINT32SZ;
98303831d35Sstevel }
98403831d35Sstevel
98503831d35Sstevel if (len != 0) {
98603831d35Sstevel ddi_rep_put8(handle, buf, iosramp, len,
98703831d35Sstevel DDI_DEV_AUTOINCR);
98803831d35Sstevel }
98903831d35Sstevel } else if ((len >= UINT32SZ) &&
99003831d35Sstevel ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) {
99103831d35Sstevel /*
99203831d35Sstevel * Both source and destination are word aligned
99303831d35Sstevel */
99403831d35Sstevel int cnt = len/UINT32SZ;
99503831d35Sstevel
99603831d35Sstevel IOSRAMLOG(2,
99703831d35Sstevel "WR: rep_put32(buf:%p sramp:%p cnt:%x) len:%x\n",
99803831d35Sstevel buf, iosramp, cnt, len);
99903831d35Sstevel ddi_rep_put32(handle, (uint32_t *)buf, (uint32_t *)iosramp,
100003831d35Sstevel cnt, DDI_DEV_AUTOINCR);
100103831d35Sstevel iosramp += cnt * UINT32SZ;
100203831d35Sstevel buf += cnt * UINT32SZ;
100303831d35Sstevel len -= cnt * UINT32SZ;
100403831d35Sstevel
100503831d35Sstevel /*
100603831d35Sstevel * copy the remainder using byte access
100703831d35Sstevel */
100803831d35Sstevel if (len != 0) {
100903831d35Sstevel ddi_rep_put8(handle, buf, iosramp, len,
101003831d35Sstevel DDI_DEV_AUTOINCR);
101103831d35Sstevel }
101203831d35Sstevel } else if (len != 0) {
101303831d35Sstevel /*
101403831d35Sstevel * We know that the "off" is at least word aligned. We
101503831d35Sstevel * need to read data from buf buffer byte at a time, and
101603831d35Sstevel * write it to the IOSRAM word at a time.
101703831d35Sstevel */
101803831d35Sstevel
101903831d35Sstevel ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0);
102003831d35Sstevel
102103831d35Sstevel IOSRAMLOG(2,
102203831d35Sstevel "WR: unaligned put32(buf:%p sramp:%p) len:%x\n",
102303831d35Sstevel buf, iosramp, len, NULL);
102403831d35Sstevel for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) {
102503831d35Sstevel word.cbuf[0] = *buf++;
102603831d35Sstevel word.cbuf[1] = *buf++;
102703831d35Sstevel word.cbuf[2] = *buf++;
102803831d35Sstevel word.cbuf[3] = *buf++;
102903831d35Sstevel ddi_put32(handle, (uint32_t *)iosramp, word.data);
103003831d35Sstevel }
103103831d35Sstevel
103203831d35Sstevel /*
103303831d35Sstevel * copy the remaining data using byte access
103403831d35Sstevel */
103503831d35Sstevel if (len != 0) {
103603831d35Sstevel ddi_rep_put8(handle, buf, iosramp,
103703831d35Sstevel len, DDI_DEV_AUTOINCR);
103803831d35Sstevel }
103903831d35Sstevel }
104003831d35Sstevel
104103831d35Sstevel /*
104203831d35Sstevel * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and
104303831d35Sstevel * any threads are waiting for r/w activity to complete, wake them up.
104403831d35Sstevel */
104503831d35Sstevel mutex_enter(&iosram_mutex);
104603831d35Sstevel ASSERT(iosram_rw_active > 0);
104703831d35Sstevel
104803831d35Sstevel if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
104903831d35Sstevel iosram_rw_wakeup = 0;
105003831d35Sstevel cv_broadcast(&iosram_rw_wait);
105103831d35Sstevel }
105203831d35Sstevel mutex_exit(&iosram_mutex);
105303831d35Sstevel
105403831d35Sstevel return (error);
105503831d35Sstevel }
105603831d35Sstevel
105703831d35Sstevel
105803831d35Sstevel int
iosram_force_write(uint32_t key,uint32_t off,uint32_t len,caddr_t dptr)105903831d35Sstevel iosram_force_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
106003831d35Sstevel {
106103831d35Sstevel return (_iosram_write(key, off, len, dptr, 1 /* force */));
106203831d35Sstevel }
106303831d35Sstevel
106403831d35Sstevel
106503831d35Sstevel int
iosram_wr(uint32_t key,uint32_t off,uint32_t len,caddr_t dptr)106603831d35Sstevel iosram_wr(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
106703831d35Sstevel {
106803831d35Sstevel return (_iosram_write(key, off, len, dptr, 0));
106903831d35Sstevel }
107003831d35Sstevel
107103831d35Sstevel
107203831d35Sstevel /*
107303831d35Sstevel * iosram_register(key, handler, arg)
107403831d35Sstevel * Register a handler and an arg for the specified chunk. This handler
107503831d35Sstevel * will be invoked when an interrupt is received from the other side and
107603831d35Sstevel * the int_pending flag for the corresponding key is marked
107703831d35Sstevel * IOSRAM_INT_TO_DOM.
107803831d35Sstevel */
107903831d35Sstevel /* ARGSUSED */
108003831d35Sstevel int
iosram_register(uint32_t key,void (* handler)(),void * arg)108103831d35Sstevel iosram_register(uint32_t key, void (*handler)(), void *arg)
108203831d35Sstevel {
108303831d35Sstevel struct iosram_chunk *chunkp;
108403831d35Sstevel int error = 0;
108503831d35Sstevel
108603831d35Sstevel /*
108703831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, and no
108803831d35Sstevel * other callback is registered, proceed with the registration.
108903831d35Sstevel */
109003831d35Sstevel mutex_enter(&iosram_mutex);
109103831d35Sstevel chunkp = iosram_find_chunk(key);
109203831d35Sstevel
109303831d35Sstevel if (iosram_master == NULL) {
109403831d35Sstevel error = EIO;
109503831d35Sstevel } else if (chunkp == NULL) {
109603831d35Sstevel error = EINVAL;
109703831d35Sstevel } else if (chunkp->cback.handler != NULL) {
109803831d35Sstevel error = EBUSY;
109903831d35Sstevel } else {
110003831d35Sstevel chunkp->cback.busy = 0;
110103831d35Sstevel chunkp->cback.unregister = 0;
110203831d35Sstevel chunkp->cback.handler = handler;
110303831d35Sstevel chunkp->cback.arg = arg;
110403831d35Sstevel }
110503831d35Sstevel mutex_exit(&iosram_mutex);
110603831d35Sstevel
110703831d35Sstevel IOSRAMLOG(1, "REG: key: 0x%x hdlr:%p arg:%p error:%d\n",
110803831d35Sstevel key, handler, arg, error);
110903831d35Sstevel
111003831d35Sstevel return (error);
111103831d35Sstevel }
111203831d35Sstevel
111303831d35Sstevel
111403831d35Sstevel /*
111503831d35Sstevel * iosram_unregister()
111603831d35Sstevel * Unregister handler associated with the specified chunk.
111703831d35Sstevel */
111803831d35Sstevel int
iosram_unregister(uint32_t key)111903831d35Sstevel iosram_unregister(uint32_t key)
112003831d35Sstevel {
112103831d35Sstevel struct iosram_chunk *chunkp;
112203831d35Sstevel int error = 0;
112303831d35Sstevel
112403831d35Sstevel /*
112503831d35Sstevel * Acquire lock and look for the requested chunk. If it exists and has
112603831d35Sstevel * a callback registered, unregister it.
112703831d35Sstevel */
112803831d35Sstevel mutex_enter(&iosram_mutex);
112903831d35Sstevel chunkp = iosram_find_chunk(key);
113003831d35Sstevel
113103831d35Sstevel if (iosram_master == NULL) {
113203831d35Sstevel error = EIO;
113303831d35Sstevel } else if (chunkp == NULL) {
113403831d35Sstevel error = EINVAL;
113503831d35Sstevel } else if (chunkp->cback.busy) {
113603831d35Sstevel /*
113703831d35Sstevel * If the handler is already busy (being invoked), then we flag
113803831d35Sstevel * it so it will be unregistered after the invocation completes.
113903831d35Sstevel */
114003831d35Sstevel DPRINTF(1, ("IOSRAM(%d): unregister: delaying unreg k:0x%08x\n",
114103831d35Sstevel iosram_master->instance, key));
114203831d35Sstevel chunkp->cback.unregister = 1;
114303831d35Sstevel } else if (chunkp->cback.handler != NULL) {
114403831d35Sstevel chunkp->cback.handler = NULL;
114503831d35Sstevel chunkp->cback.arg = NULL;
114603831d35Sstevel }
114703831d35Sstevel mutex_exit(&iosram_mutex);
114803831d35Sstevel
114903831d35Sstevel IOSRAMLOG(1, "UNREG: key:%x error:%d\n", key, error, NULL, NULL);
115003831d35Sstevel return (error);
115103831d35Sstevel }
115203831d35Sstevel
115303831d35Sstevel
115403831d35Sstevel /*
115503831d35Sstevel * iosram_get_flag():
115603831d35Sstevel * Get data_valid and/or int_pending flags associated with the
115703831d35Sstevel * specified key.
115803831d35Sstevel */
115903831d35Sstevel int
iosram_get_flag(uint32_t key,uint8_t * data_valid,uint8_t * int_pending)116003831d35Sstevel iosram_get_flag(uint32_t key, uint8_t *data_valid, uint8_t *int_pending)
116103831d35Sstevel {
116203831d35Sstevel iosram_chunk_t *chunkp;
116303831d35Sstevel iosram_flags_t flags;
116403831d35Sstevel int error = 0;
116503831d35Sstevel
116603831d35Sstevel /*
116703831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, and no
116803831d35Sstevel * tunnel switch is in progress, read the chunk's flags.
116903831d35Sstevel */
117003831d35Sstevel mutex_enter(&iosram_mutex);
117103831d35Sstevel chunkp = iosram_find_chunk(key);
117203831d35Sstevel
117303831d35Sstevel if (iosram_master == NULL) {
117403831d35Sstevel error = EIO;
117503831d35Sstevel } else if (chunkp == NULL) {
117603831d35Sstevel error = EINVAL;
117703831d35Sstevel } else if (iosram_tswitch_active) {
117803831d35Sstevel error = EAGAIN;
117903831d35Sstevel } else {
118003831d35Sstevel IOSRAM_STAT(getflag);
118103831d35Sstevel
118203831d35Sstevel /*
118303831d35Sstevel * Read the flags
118403831d35Sstevel */
118503831d35Sstevel ddi_rep_get8(iosram_handle, (uint8_t *)&flags,
118603831d35Sstevel (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t),
118703831d35Sstevel DDI_DEV_AUTOINCR);
118803831d35Sstevel
118903831d35Sstevel /*
119003831d35Sstevel * Get each flag value that the caller is interested in.
119103831d35Sstevel */
119203831d35Sstevel if (data_valid != NULL) {
119303831d35Sstevel *data_valid = flags.data_valid;
119403831d35Sstevel }
119503831d35Sstevel
119603831d35Sstevel if (int_pending != NULL) {
119703831d35Sstevel *int_pending = flags.int_pending;
119803831d35Sstevel }
119903831d35Sstevel }
120003831d35Sstevel mutex_exit(&iosram_mutex);
120103831d35Sstevel
120203831d35Sstevel IOSRAMLOG(1, "GetFlag key:%x data_valid:%x int_pending:%x error:%d\n",
120303831d35Sstevel key, flags.data_valid, flags.int_pending, error);
120403831d35Sstevel return (error);
120503831d35Sstevel }
120603831d35Sstevel
120703831d35Sstevel
120803831d35Sstevel /*
120903831d35Sstevel * iosram_set_flag():
121003831d35Sstevel * Set data_valid and int_pending flags associated with the specified key.
121103831d35Sstevel */
121203831d35Sstevel int
iosram_set_flag(uint32_t key,uint8_t data_valid,uint8_t int_pending)121303831d35Sstevel iosram_set_flag(uint32_t key, uint8_t data_valid, uint8_t int_pending)
121403831d35Sstevel {
121503831d35Sstevel iosram_chunk_t *chunkp;
121603831d35Sstevel iosram_flags_t flags;
121703831d35Sstevel int error = 0;
121803831d35Sstevel
121903831d35Sstevel /*
122003831d35Sstevel * Acquire lock and look for the requested chunk. If it exists, and no
122103831d35Sstevel * tunnel switch is in progress, write the chunk's flags.
122203831d35Sstevel */
122303831d35Sstevel mutex_enter(&iosram_mutex);
122403831d35Sstevel chunkp = iosram_find_chunk(key);
122503831d35Sstevel
122603831d35Sstevel if (iosram_master == NULL) {
122703831d35Sstevel error = EIO;
122803831d35Sstevel } else if ((chunkp == NULL) ||
122903831d35Sstevel ((data_valid != IOSRAM_DATA_INVALID) &&
123003831d35Sstevel (data_valid != IOSRAM_DATA_VALID)) ||
123103831d35Sstevel ((int_pending != IOSRAM_INT_NONE) &&
123203831d35Sstevel (int_pending != IOSRAM_INT_TO_SSC) &&
123303831d35Sstevel (int_pending != IOSRAM_INT_TO_DOM))) {
123403831d35Sstevel error = EINVAL;
123503831d35Sstevel } else if (iosram_tswitch_active) {
123603831d35Sstevel error = EAGAIN;
123703831d35Sstevel } else {
123803831d35Sstevel IOSRAM_STAT(setflag);
123903831d35Sstevel flags.data_valid = data_valid;
124003831d35Sstevel flags.int_pending = int_pending;
124103831d35Sstevel ddi_rep_put8(iosram_handle, (uint8_t *)&flags,
124203831d35Sstevel (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t),
124303831d35Sstevel DDI_DEV_AUTOINCR);
124403831d35Sstevel }
124503831d35Sstevel mutex_exit(&iosram_mutex);
124603831d35Sstevel
124703831d35Sstevel IOSRAMLOG(1, "SetFlag key:%x data_valid:%x int_pending:%x error:%d\n",
124803831d35Sstevel key, flags.data_valid, flags.int_pending, error);
124903831d35Sstevel return (error);
125003831d35Sstevel }
125103831d35Sstevel
125203831d35Sstevel
125303831d35Sstevel /*
125403831d35Sstevel * iosram_ctrl()
125503831d35Sstevel * This function provides access to a variety of services not available
125603831d35Sstevel * through the basic API.
125703831d35Sstevel */
125803831d35Sstevel int
iosram_ctrl(uint32_t key,uint32_t cmd,void * arg)125903831d35Sstevel iosram_ctrl(uint32_t key, uint32_t cmd, void *arg)
126003831d35Sstevel {
126103831d35Sstevel struct iosram_chunk *chunkp;
126203831d35Sstevel int error = 0;
126303831d35Sstevel
126403831d35Sstevel /*
126503831d35Sstevel * Acquire lock and do some argument sanity checking.
126603831d35Sstevel */
126703831d35Sstevel mutex_enter(&iosram_mutex);
126803831d35Sstevel chunkp = iosram_find_chunk(key);
126903831d35Sstevel
127003831d35Sstevel if (iosram_master == NULL) {
127103831d35Sstevel error = EIO;
127203831d35Sstevel } else if (chunkp == NULL) {
127303831d35Sstevel error = EINVAL;
127403831d35Sstevel }
127503831d35Sstevel
127603831d35Sstevel if (error != 0) {
127703831d35Sstevel mutex_exit(&iosram_mutex);
127803831d35Sstevel return (error);
127903831d35Sstevel }
128003831d35Sstevel
128103831d35Sstevel /*
128203831d35Sstevel * Arguments seem okay so far, so process the command.
128303831d35Sstevel */
128403831d35Sstevel switch (cmd) {
128503831d35Sstevel case IOSRAM_CMD_CHUNKLEN:
128603831d35Sstevel /*
128703831d35Sstevel * Return the length of the chunk indicated by the key.
128803831d35Sstevel */
128903831d35Sstevel if (arg == NULL) {
129003831d35Sstevel error = EINVAL;
129103831d35Sstevel break;
129203831d35Sstevel }
129303831d35Sstevel
129403831d35Sstevel *(uint32_t *)arg = chunkp->toc_data.len;
129503831d35Sstevel break;
129603831d35Sstevel
129703831d35Sstevel default:
129803831d35Sstevel error = ENOTSUP;
129903831d35Sstevel break;
130003831d35Sstevel }
130103831d35Sstevel
130203831d35Sstevel mutex_exit(&iosram_mutex);
130303831d35Sstevel return (error);
130403831d35Sstevel }
130503831d35Sstevel
130603831d35Sstevel
130703831d35Sstevel /*
130803831d35Sstevel * iosram_hdr_ctrl()
130903831d35Sstevel * This function provides an interface for the Mailbox Protocol
131003831d35Sstevel * implementation to use when interacting with the IOSRAM header.
131103831d35Sstevel */
131203831d35Sstevel int
iosram_hdr_ctrl(uint32_t cmd,void * arg)131303831d35Sstevel iosram_hdr_ctrl(uint32_t cmd, void *arg)
131403831d35Sstevel {
131503831d35Sstevel int error = 0;
131603831d35Sstevel
131703831d35Sstevel /*
131803831d35Sstevel * Acquire lock and do some argument sanity checking.
131903831d35Sstevel */
132003831d35Sstevel mutex_enter(&iosram_mutex);
132103831d35Sstevel
132203831d35Sstevel if (iosram_master == NULL) {
132303831d35Sstevel error = EIO;
132403831d35Sstevel }
132503831d35Sstevel
132603831d35Sstevel if (error != 0) {
132703831d35Sstevel mutex_exit(&iosram_mutex);
132803831d35Sstevel return (error);
132903831d35Sstevel }
133003831d35Sstevel
133103831d35Sstevel switch (cmd) {
133203831d35Sstevel case IOSRAM_HDRCMD_GET_SMS_MBOX_VER:
133303831d35Sstevel /*
133403831d35Sstevel * Return the value of the sms_mbox_version field.
133503831d35Sstevel */
133603831d35Sstevel if (arg == NULL) {
133703831d35Sstevel error = EINVAL;
133803831d35Sstevel break;
133903831d35Sstevel }
134003831d35Sstevel
134103831d35Sstevel *(uint32_t *)arg = IOSRAM_GET_HDRFIELD32(iosram_master,
134203831d35Sstevel sms_mbox_version);
134303831d35Sstevel break;
134403831d35Sstevel
134503831d35Sstevel case IOSRAM_HDRCMD_SET_OS_MBOX_VER:
134603831d35Sstevel /*
134703831d35Sstevel * Set the value of the os_mbox_version field.
134803831d35Sstevel */
134903831d35Sstevel IOSRAM_SET_HDRFIELD32(iosram_master, os_mbox_version,
135003831d35Sstevel (uint32_t)(uintptr_t)arg);
135103831d35Sstevel IOSRAM_SET_HDRFIELD32(iosram_master, os_change_mask,
135203831d35Sstevel IOSRAM_HDRFIELD_OS_MBOX_VER);
1353*07d06da5SSurya Prakki (void) iosram_send_intr();
135403831d35Sstevel break;
135503831d35Sstevel
135603831d35Sstevel case IOSRAM_HDRCMD_REG_CALLBACK:
135703831d35Sstevel iosram_hdrchange_handler = (void (*)())arg;
135803831d35Sstevel break;
135903831d35Sstevel
136003831d35Sstevel default:
136103831d35Sstevel error = ENOTSUP;
136203831d35Sstevel break;
136303831d35Sstevel }
136403831d35Sstevel
136503831d35Sstevel mutex_exit(&iosram_mutex);
136603831d35Sstevel return (error);
136703831d35Sstevel }
136803831d35Sstevel
136903831d35Sstevel
137003831d35Sstevel /*
137103831d35Sstevel * iosram_softintr()
137203831d35Sstevel * IOSRAM soft interrupt handler
137303831d35Sstevel */
137403831d35Sstevel static uint_t
iosram_softintr(caddr_t arg)137503831d35Sstevel iosram_softintr(caddr_t arg)
137603831d35Sstevel {
137703831d35Sstevel uint32_t hdr_changes;
137803831d35Sstevel iosramsoft_t *softp = (iosramsoft_t *)arg;
137903831d35Sstevel iosram_chunk_t *chunkp;
138003831d35Sstevel void (*handler)();
138103831d35Sstevel int i;
138203831d35Sstevel uint8_t flag;
138303831d35Sstevel
138403831d35Sstevel DPRINTF(1, ("iosram(%d): in iosram_softintr\n", softp->instance));
138503831d35Sstevel
138603831d35Sstevel IOSRAMLOG(2, "SINTR arg/softp:%p pending:%d busy:%d\n",
138703831d35Sstevel arg, softp->intr_pending, softp->intr_busy, NULL);
138803831d35Sstevel
138903831d35Sstevel mutex_enter(&iosram_mutex);
139003831d35Sstevel mutex_enter(&softp->intr_mutex);
139103831d35Sstevel
139203831d35Sstevel /*
139303831d35Sstevel * Do not process interrupt if interrupt handler is already running or
139403831d35Sstevel * no interrupts are pending.
139503831d35Sstevel */
139603831d35Sstevel if (softp->intr_busy || !softp->intr_pending) {
139703831d35Sstevel mutex_exit(&softp->intr_mutex);
139803831d35Sstevel mutex_exit(&iosram_mutex);
139903831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: busy=%d pending=%d\n",
140003831d35Sstevel softp->instance, softp->intr_busy, softp->intr_pending));
140103831d35Sstevel return (softp->intr_pending ? DDI_INTR_CLAIMED :
140203831d35Sstevel DDI_INTR_UNCLAIMED);
140303831d35Sstevel }
140403831d35Sstevel
140503831d35Sstevel /*
140603831d35Sstevel * It's possible for the SC to send an interrupt on the new master
140703831d35Sstevel * before we are able to set our internal state. If so, we'll retrigger
140803831d35Sstevel * soft interrupt right after tunnel switch completion.
140903831d35Sstevel */
141003831d35Sstevel if (softp->state & IOSRAM_STATE_TSWITCH) {
141103831d35Sstevel mutex_exit(&softp->intr_mutex);
141203831d35Sstevel mutex_exit(&iosram_mutex);
141303831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: doing switch "
141403831d35Sstevel "state=0x%x\n", softp->instance, softp->state));
141503831d35Sstevel return (DDI_INTR_CLAIMED);
141603831d35Sstevel }
141703831d35Sstevel
141803831d35Sstevel /*
141903831d35Sstevel * Do not process interrupt if we are not the master.
142003831d35Sstevel */
142103831d35Sstevel if (!(softp->state & IOSRAM_STATE_MASTER)) {
142203831d35Sstevel mutex_exit(&softp->intr_mutex);
142303831d35Sstevel mutex_exit(&iosram_mutex);
142403831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: no master state=0x%x\n ",
142503831d35Sstevel softp->instance, softp->state));
142603831d35Sstevel return (DDI_INTR_CLAIMED);
142703831d35Sstevel }
142803831d35Sstevel
142903831d35Sstevel IOSRAM_STAT(sintr_recv);
143003831d35Sstevel
143103831d35Sstevel /*
143203831d35Sstevel * If the driver is suspended, then we should not process any
143303831d35Sstevel * interrupts. Instead, we trigger a soft interrupt when the driver
143403831d35Sstevel * resumes.
143503831d35Sstevel */
143603831d35Sstevel if (softp->suspended) {
143703831d35Sstevel mutex_exit(&softp->intr_mutex);
143803831d35Sstevel mutex_exit(&iosram_mutex);
143903831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: suspended\n",
144003831d35Sstevel softp->instance));
144103831d35Sstevel return (DDI_INTR_CLAIMED);
144203831d35Sstevel }
144303831d35Sstevel
144403831d35Sstevel /*
144503831d35Sstevel * Indicate that the IOSRAM interrupt handler is busy. Note that this
144603831d35Sstevel * includes incrementing the reader/writer count, since we don't want
144703831d35Sstevel * any tunnel switches to start up while we're processing callbacks.
144803831d35Sstevel */
144903831d35Sstevel softp->intr_busy = 1;
145003831d35Sstevel iosram_rw_active++;
145103831d35Sstevel #if defined(DEBUG)
145203831d35Sstevel if (iosram_rw_active > iosram_rw_active_max) {
145303831d35Sstevel iosram_rw_active_max = iosram_rw_active;
145403831d35Sstevel }
145503831d35Sstevel #endif
145603831d35Sstevel
145703831d35Sstevel do {
145803831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: processing interrupt\n",
145903831d35Sstevel softp->instance));
146003831d35Sstevel
146103831d35Sstevel softp->intr_pending = 0;
146203831d35Sstevel
146303831d35Sstevel mutex_exit(&softp->intr_mutex);
146403831d35Sstevel
146503831d35Sstevel /*
146603831d35Sstevel * Process changes to the IOSRAM header.
146703831d35Sstevel */
146803831d35Sstevel hdr_changes = IOSRAM_GET_HDRFIELD32(iosram_master,
146903831d35Sstevel sms_change_mask);
147003831d35Sstevel if (hdr_changes != 0) {
147103831d35Sstevel int error;
147203831d35Sstevel
147303831d35Sstevel IOSRAM_SET_HDRFIELD32(iosram_master, sms_change_mask,
147403831d35Sstevel 0);
147503831d35Sstevel if (hdr_changes & IOSRAM_HDRFIELD_TOC_INDEX) {
147603831d35Sstevel /*
147703831d35Sstevel * XXX is it safe to temporarily release the
147803831d35Sstevel * iosram_mutex here?
147903831d35Sstevel */
148003831d35Sstevel mutex_exit(&iosram_mutex);
148103831d35Sstevel error = iosram_read_toc(iosram_master);
148203831d35Sstevel mutex_enter(&iosram_mutex);
148303831d35Sstevel if (error) {
148403831d35Sstevel cmn_err(CE_WARN, "iosram_read_toc: new"
148503831d35Sstevel " TOC invalid; using old TOC.");
148603831d35Sstevel }
148703831d35Sstevel iosram_update_addrs(iosram_master);
148803831d35Sstevel }
148903831d35Sstevel
149003831d35Sstevel if (iosram_hdrchange_handler != NULL) {
149103831d35Sstevel mutex_exit(&iosram_mutex);
149203831d35Sstevel iosram_hdrchange_handler();
149303831d35Sstevel mutex_enter(&iosram_mutex);
149403831d35Sstevel }
149503831d35Sstevel }
149603831d35Sstevel
149703831d35Sstevel /*
149803831d35Sstevel * Get data_valid/int_pending flags and generate a callback if
149903831d35Sstevel * applicable. For now, we read only those flags for which a
150003831d35Sstevel * callback has been registered. We can optimize reading of
150103831d35Sstevel * flags by reading them all at once and then process them
150203831d35Sstevel * later.
150303831d35Sstevel */
150403831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++,
150503831d35Sstevel chunkp++) {
150603831d35Sstevel #if DEBUG
150703831d35Sstevel flag = ddi_get8(iosram_handle,
150803831d35Sstevel &(chunkp->flagsp->int_pending));
150903831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr chunk #%d "
151003831d35Sstevel "flag=0x%x handler=%p\n",
151103831d35Sstevel softp->instance, i, (int)flag,
1512*07d06da5SSurya Prakki (void *)chunkp->cback.handler));
151303831d35Sstevel #endif
151403831d35Sstevel if ((handler = chunkp->cback.handler) == NULL) {
151503831d35Sstevel continue;
151603831d35Sstevel }
151703831d35Sstevel flag = ddi_get8(iosram_handle,
151803831d35Sstevel &(chunkp->flagsp->int_pending));
151903831d35Sstevel if (flag == IOSRAM_INT_TO_DOM) {
152003831d35Sstevel DPRINTF(1,
152103831d35Sstevel ("IOSRAM(%d): softintr: invoking handler\n",
152203831d35Sstevel softp->instance));
152303831d35Sstevel IOSRAMLOG(1,
152403831d35Sstevel "SINTR invoking hdlr:%p arg:%p index:%d\n",
152503831d35Sstevel handler, chunkp->cback.arg, i, NULL);
152603831d35Sstevel IOSRAM_STAT(callbacks);
152703831d35Sstevel
152803831d35Sstevel ddi_put8(iosram_handle,
152903831d35Sstevel &(chunkp->flagsp->int_pending),
153003831d35Sstevel IOSRAM_INT_NONE);
153103831d35Sstevel chunkp->cback.busy = 1;
153203831d35Sstevel mutex_exit(&iosram_mutex);
153303831d35Sstevel (*handler)(chunkp->cback.arg);
153403831d35Sstevel mutex_enter(&iosram_mutex);
153503831d35Sstevel chunkp->cback.busy = 0;
153603831d35Sstevel
153703831d35Sstevel /*
153803831d35Sstevel * If iosram_unregister was called while the
153903831d35Sstevel * callback was being invoked, complete the
154003831d35Sstevel * unregistration here.
154103831d35Sstevel */
154203831d35Sstevel if (chunkp->cback.unregister) {
154303831d35Sstevel DPRINTF(1, ("IOSRAM(%d): softintr: "
154403831d35Sstevel "delayed unreg k:0x%08x\n",
154503831d35Sstevel softp->instance,
154603831d35Sstevel chunkp->toc_data.key));
154703831d35Sstevel chunkp->cback.handler = NULL;
154803831d35Sstevel chunkp->cback.arg = NULL;
154903831d35Sstevel chunkp->cback.unregister = 0;
155003831d35Sstevel }
155103831d35Sstevel }
155203831d35Sstevel
155303831d35Sstevel /*
155403831d35Sstevel * If there's a tunnel switch waiting to run, give it
155503831d35Sstevel * higher priority than these callbacks by bailing out.
155603831d35Sstevel * They'll still be invoked on the new master iosram
155703831d35Sstevel * when the tunnel switch is done.
155803831d35Sstevel */
155903831d35Sstevel if (iosram_tswitch_active) {
156003831d35Sstevel break;
156103831d35Sstevel }
156203831d35Sstevel }
156303831d35Sstevel
156403831d35Sstevel mutex_enter(&softp->intr_mutex);
156503831d35Sstevel
156603831d35Sstevel } while (softp->intr_pending && !softp->suspended &&
156703831d35Sstevel !iosram_tswitch_active);
156803831d35Sstevel
156903831d35Sstevel /*
157003831d35Sstevel * Indicate IOSRAM interrupt handler is not BUSY any more
157103831d35Sstevel */
157203831d35Sstevel softp->intr_busy = 0;
157303831d35Sstevel
157403831d35Sstevel ASSERT(iosram_rw_active > 0);
157503831d35Sstevel if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
157603831d35Sstevel iosram_rw_wakeup = 0;
157703831d35Sstevel cv_broadcast(&iosram_rw_wait);
157803831d35Sstevel }
157903831d35Sstevel
158003831d35Sstevel mutex_exit(&softp->intr_mutex);
158103831d35Sstevel mutex_exit(&iosram_mutex);
158203831d35Sstevel
158303831d35Sstevel DPRINTF(1, ("iosram(%d): softintr exit\n", softp->instance));
158403831d35Sstevel
158503831d35Sstevel return (DDI_INTR_CLAIMED);
158603831d35Sstevel }
158703831d35Sstevel
158803831d35Sstevel
158903831d35Sstevel /*
159003831d35Sstevel * iosram_intr()
159103831d35Sstevel * IOSRAM real interrupt handler
159203831d35Sstevel */
159303831d35Sstevel static uint_t
iosram_intr(caddr_t arg)159403831d35Sstevel iosram_intr(caddr_t arg)
159503831d35Sstevel {
159603831d35Sstevel iosramsoft_t *softp = (iosramsoft_t *)arg;
159703831d35Sstevel int result = DDI_INTR_UNCLAIMED;
159803831d35Sstevel uint32_t int_status;
159903831d35Sstevel
160003831d35Sstevel DPRINTF(2, ("iosram(%d): in iosram_intr\n", softp->instance));
160103831d35Sstevel
160203831d35Sstevel mutex_enter(&softp->intr_mutex);
160303831d35Sstevel
160403831d35Sstevel if (softp->sbbc_handle == NULL) {
160503831d35Sstevel /*
160603831d35Sstevel * The SBBC registers region is not mapped in.
160703831d35Sstevel * Set the interrupt pending flag here, and process the
160803831d35Sstevel * interrupt after the tunnel switch.
160903831d35Sstevel */
161003831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_intr: SBBC not mapped\n",
161103831d35Sstevel softp->instance));
161203831d35Sstevel softp->intr_pending = 1;
161303831d35Sstevel mutex_exit(&softp->intr_mutex);
161403831d35Sstevel return (DDI_INTR_UNCLAIMED);
161503831d35Sstevel }
161603831d35Sstevel
161703831d35Sstevel int_status = ddi_get32(softp->sbbc_handle,
161803831d35Sstevel &(softp->sbbc_region->int_status.reg));
161903831d35Sstevel DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n", int_status));
162003831d35Sstevel
162103831d35Sstevel if (int_status & IOSRAM_SBBC_INT0) {
162203831d35Sstevel result = DDI_INTR_CLAIMED;
162303831d35Sstevel DPRINTF(1, ("iosram_intr: int0 detected!\n"));
162403831d35Sstevel }
162503831d35Sstevel
162603831d35Sstevel if (int_status & IOSRAM_SBBC_INT1) {
162703831d35Sstevel result = DDI_INTR_CLAIMED;
162803831d35Sstevel DPRINTF(1, ("iosram_intr: int1 detected!\n"));
162903831d35Sstevel }
163003831d35Sstevel
163103831d35Sstevel if (result == DDI_INTR_CLAIMED) {
163203831d35Sstevel ddi_put32(softp->sbbc_handle,
163303831d35Sstevel &(softp->sbbc_region->int_status.reg), int_status);
163403831d35Sstevel int_status = ddi_get32(softp->sbbc_handle,
163503831d35Sstevel &(softp->sbbc_region->int_status.reg));
163603831d35Sstevel DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n",
163703831d35Sstevel int_status));
163803831d35Sstevel
163903831d35Sstevel softp->intr_pending = 1;
164003831d35Sstevel /*
164103831d35Sstevel * Trigger soft interrupt if not executing and
164203831d35Sstevel * not suspended.
164303831d35Sstevel */
164403831d35Sstevel if (!softp->intr_busy && !softp->suspended &&
164503831d35Sstevel (softp->softintr_id != NULL)) {
164603831d35Sstevel DPRINTF(1, ("iosram(%d): trigger softint\n",
164703831d35Sstevel softp->instance));
164803831d35Sstevel ddi_trigger_softintr(softp->softintr_id);
164903831d35Sstevel }
165003831d35Sstevel }
165103831d35Sstevel
165203831d35Sstevel IOSRAM_STAT(intr_recv);
165303831d35Sstevel
165403831d35Sstevel mutex_exit(&softp->intr_mutex);
165503831d35Sstevel
165603831d35Sstevel IOSRAMLOG(2, "INTR arg/softp:%p pending:%d busy:%d\n",
165703831d35Sstevel arg, softp->intr_pending, softp->intr_busy, NULL);
165803831d35Sstevel DPRINTF(1, ("iosram(%d): iosram_intr exit\n", softp->instance));
165903831d35Sstevel
166003831d35Sstevel return (result);
166103831d35Sstevel }
166203831d35Sstevel
166303831d35Sstevel
166403831d35Sstevel /*
166503831d35Sstevel * iosram_send_intr()
166603831d35Sstevel * Send an interrupt to the SSP side via AXQ driver
166703831d35Sstevel */
166803831d35Sstevel int
iosram_send_intr()166903831d35Sstevel iosram_send_intr()
167003831d35Sstevel {
167103831d35Sstevel IOSRAMLOG(1, "SendIntr called\n", NULL, NULL, NULL, NULL);
167203831d35Sstevel IOSRAM_STAT(intr_send);
167303831d35Sstevel DPRINTF(1, ("iosram iosram_send_intr invoked\n"));
167403831d35Sstevel
167503831d35Sstevel return (axq_cpu2ssc_intr(0));
167603831d35Sstevel }
167703831d35Sstevel
167803831d35Sstevel
167903831d35Sstevel #if defined(DEBUG)
168003831d35Sstevel static void
iosram_dummy_cback(void * arg)168103831d35Sstevel iosram_dummy_cback(void *arg)
168203831d35Sstevel {
168303831d35Sstevel DPRINTF(1, ("iosram_dummy_cback invoked arg:%p\n", arg));
168403831d35Sstevel }
168503831d35Sstevel #endif /* DEBUG */
168603831d35Sstevel
168703831d35Sstevel
168803831d35Sstevel /*ARGSUSED1*/
168903831d35Sstevel static int
iosram_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)169003831d35Sstevel iosram_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
169103831d35Sstevel int *rvalp)
169203831d35Sstevel {
169303831d35Sstevel struct iosramsoft *softp;
169403831d35Sstevel int error = DDI_SUCCESS;
169503831d35Sstevel
169603831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, getminor(dev));
169703831d35Sstevel if (softp == NULL) {
169803831d35Sstevel return (ENXIO);
169903831d35Sstevel }
170003831d35Sstevel IOSRAMLOG(1, "IOCTL: dev:%p cmd:%x arg:%p ... instance %d\n",
170103831d35Sstevel dev, cmd, arg, softp->instance);
170203831d35Sstevel
170303831d35Sstevel switch (cmd) {
170403831d35Sstevel #if defined(DEBUG)
170503831d35Sstevel case IOSRAM_GET_FLAG:
170603831d35Sstevel {
170703831d35Sstevel iosram_io_t req;
170803831d35Sstevel uint8_t data_valid, int_pending;
170903831d35Sstevel
171003831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
171103831d35Sstevel return (EFAULT);
171203831d35Sstevel }
171303831d35Sstevel
171403831d35Sstevel DPRINTF(2, ("IOSRAM_GET_FLAG(key:%x\n", req.key));
171503831d35Sstevel
171603831d35Sstevel req.retval = iosram_get_flag(req.key, &data_valid,
171703831d35Sstevel &int_pending);
171803831d35Sstevel req.data_valid = (uint32_t)data_valid;
171903831d35Sstevel req.int_pending = (uint32_t)int_pending;
172003831d35Sstevel
172103831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
172203831d35Sstevel DPRINTF(1,
172303831d35Sstevel ("IOSRAM_GET_FLAG: can't copyout req.retval (%x)",
172403831d35Sstevel req.retval));
172503831d35Sstevel error = EFAULT;
172603831d35Sstevel }
172703831d35Sstevel
172803831d35Sstevel return (error);
172903831d35Sstevel }
173003831d35Sstevel
173103831d35Sstevel case IOSRAM_SET_FLAG:
173203831d35Sstevel {
173303831d35Sstevel iosram_io_t req;
173403831d35Sstevel
173503831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
173603831d35Sstevel return (EFAULT);
173703831d35Sstevel }
173803831d35Sstevel
173903831d35Sstevel DPRINTF(2, ("IOSRAM_SET_FLAG(key:%x data_valid:%x "
174003831d35Sstevel "int_pending:%x\n", req.key, req.data_valid,
174103831d35Sstevel req.int_pending));
174203831d35Sstevel
174303831d35Sstevel req.retval = iosram_set_flag(req.key, req.data_valid,
174403831d35Sstevel req.int_pending);
174503831d35Sstevel
174603831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
174703831d35Sstevel DPRINTF(1, ("IOSRAM_SET_FLAG: can't copyout req.retval"
174803831d35Sstevel " (%x)\n", req.retval));
174903831d35Sstevel error = EFAULT;
175003831d35Sstevel }
175103831d35Sstevel
175203831d35Sstevel return (error);
175303831d35Sstevel }
175403831d35Sstevel
175503831d35Sstevel case IOSRAM_RD:
175603831d35Sstevel {
175703831d35Sstevel caddr_t bufp;
175803831d35Sstevel int len;
175903831d35Sstevel iosram_io_t req;
176003831d35Sstevel
176103831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
176203831d35Sstevel return (EFAULT);
176303831d35Sstevel }
176403831d35Sstevel
176503831d35Sstevel DPRINTF(2, ("IOSRAM_RD(k:%x o:%x len:%x bufp:%p\n", req.key,
176603831d35Sstevel req.off, req.len, (void *)(uintptr_t)req.bufp));
176703831d35Sstevel
176803831d35Sstevel len = req.len;
176903831d35Sstevel bufp = kmem_alloc(len, KM_SLEEP);
177003831d35Sstevel
177103831d35Sstevel req.retval = iosram_rd(req.key, req.off, req.len, bufp);
177203831d35Sstevel
177303831d35Sstevel if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, len, mode)) {
177403831d35Sstevel DPRINTF(1, ("IOSRAM_RD: copyout(%p, %p,%x,%x) failed\n",
1775*07d06da5SSurya Prakki (void *)bufp, (void *)(uintptr_t)req.bufp, len,
1776*07d06da5SSurya Prakki mode));
177703831d35Sstevel error = EFAULT;
177803831d35Sstevel } else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
177903831d35Sstevel DPRINTF(1, ("IOSRAM_RD: can't copyout retval (%x)\n",
178003831d35Sstevel req.retval));
178103831d35Sstevel error = EFAULT;
178203831d35Sstevel }
178303831d35Sstevel
178403831d35Sstevel kmem_free(bufp, len);
178503831d35Sstevel return (error);
178603831d35Sstevel }
178703831d35Sstevel
178803831d35Sstevel case IOSRAM_WR:
178903831d35Sstevel {
179003831d35Sstevel caddr_t bufp;
179103831d35Sstevel iosram_io_t req;
179203831d35Sstevel int len;
179303831d35Sstevel
179403831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
179503831d35Sstevel return (EFAULT);
179603831d35Sstevel }
179703831d35Sstevel
179803831d35Sstevel DPRINTF(2, ("IOSRAM_WR(k:%x o:%x len:%x bufp:%p\n",
1799*07d06da5SSurya Prakki req.key, req.off, req.len, (void *)(uintptr_t)req.bufp));
180003831d35Sstevel len = req.len;
180103831d35Sstevel bufp = kmem_alloc(len, KM_SLEEP);
180203831d35Sstevel if (ddi_copyin((void *)(uintptr_t)req.bufp, bufp, len, mode)) {
180303831d35Sstevel error = EFAULT;
180403831d35Sstevel } else {
180503831d35Sstevel req.retval = iosram_wr(req.key, req.off, req.len,
180603831d35Sstevel bufp);
180703831d35Sstevel
180803831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req),
180903831d35Sstevel mode)) {
181003831d35Sstevel error = EFAULT;
181103831d35Sstevel }
181203831d35Sstevel }
181303831d35Sstevel kmem_free(bufp, len);
181403831d35Sstevel return (error);
181503831d35Sstevel }
181603831d35Sstevel
181703831d35Sstevel case IOSRAM_TOC:
181803831d35Sstevel {
181903831d35Sstevel caddr_t bufp;
182003831d35Sstevel int len;
182103831d35Sstevel iosram_io_t req;
182203831d35Sstevel
182303831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
182403831d35Sstevel return (EFAULT);
182503831d35Sstevel }
182603831d35Sstevel
182703831d35Sstevel DPRINTF(2, ("IOSRAM_TOC (req.bufp:%x req.len:%x) \n",
182803831d35Sstevel req.bufp, req.len));
182903831d35Sstevel
183003831d35Sstevel len = req.len;
183103831d35Sstevel bufp = kmem_alloc(len, KM_SLEEP);
183203831d35Sstevel
183303831d35Sstevel req.retval = iosram_get_keys((iosram_toc_entry_t *)bufp,
183403831d35Sstevel &req.len);
183503831d35Sstevel
183603831d35Sstevel if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, req.len,
183703831d35Sstevel mode)) {
183803831d35Sstevel DPRINTF(1,
183903831d35Sstevel ("IOSRAM_TOC: copyout(%p, %p,%x,%x) failed\n",
1840*07d06da5SSurya Prakki (void *)bufp, (void *)(uintptr_t)req.bufp, req.len,
1841*07d06da5SSurya Prakki mode));
184203831d35Sstevel error = EFAULT;
184303831d35Sstevel } else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
184403831d35Sstevel DPRINTF(1, ("IOSRAM_TOC: can't copyout retval (%x)\n",
184503831d35Sstevel req.retval));
184603831d35Sstevel error = EFAULT;
184703831d35Sstevel }
184803831d35Sstevel kmem_free(bufp, len);
184903831d35Sstevel return (error);
185003831d35Sstevel }
185103831d35Sstevel
185203831d35Sstevel case IOSRAM_SEND_INTR:
185303831d35Sstevel {
185403831d35Sstevel DPRINTF(2, ("IOSRAM_SEND_INTR\n"));
185503831d35Sstevel
185603831d35Sstevel switch ((int)arg) {
185703831d35Sstevel case 0x11:
185803831d35Sstevel case 0x22:
185903831d35Sstevel case 0x44:
186003831d35Sstevel case 0x88:
186103831d35Sstevel ddi_put32(softp->sbbc_handle,
186203831d35Sstevel &(softp->sbbc_region->int_enable.reg), (int)arg);
186303831d35Sstevel DPRINTF(1, ("Wrote 0x%x to int_enable.reg\n",
186403831d35Sstevel (int)arg));
186503831d35Sstevel break;
186603831d35Sstevel case 0xBB:
186703831d35Sstevel ddi_put32(softp->sbbc_handle,
186803831d35Sstevel &(softp->sbbc_region->p0_int_gen.reg), 1);
186903831d35Sstevel DPRINTF(1, ("Wrote 1 to p0_int_gen.reg\n"));
187003831d35Sstevel break;
187103831d35Sstevel default:
187203831d35Sstevel error = iosram_send_intr();
187303831d35Sstevel }
187403831d35Sstevel
187503831d35Sstevel return (error);
187603831d35Sstevel }
187703831d35Sstevel
187803831d35Sstevel case IOSRAM_PRINT_CBACK:
187903831d35Sstevel iosram_print_cback();
188003831d35Sstevel break;
188103831d35Sstevel
188203831d35Sstevel case IOSRAM_PRINT_STATE:
188303831d35Sstevel iosram_print_state((int)arg);
188403831d35Sstevel break;
188503831d35Sstevel
188603831d35Sstevel #if IOSRAM_STATS
188703831d35Sstevel case IOSRAM_PRINT_STATS:
188803831d35Sstevel iosram_print_stats();
188903831d35Sstevel break;
189003831d35Sstevel #endif
189103831d35Sstevel
189203831d35Sstevel #if IOSRAM_LOG
189303831d35Sstevel case IOSRAM_PRINT_LOG:
189403831d35Sstevel iosram_print_log((int)arg);
189503831d35Sstevel break;
189603831d35Sstevel #endif
189703831d35Sstevel
189803831d35Sstevel case IOSRAM_TUNNEL_SWITCH:
189903831d35Sstevel error = iosram_switchfrom((int)arg);
190003831d35Sstevel break;
190103831d35Sstevel
190203831d35Sstevel case IOSRAM_PRINT_FLAGS:
190303831d35Sstevel iosram_print_flags();
190403831d35Sstevel break;
190503831d35Sstevel
190603831d35Sstevel case IOSRAM_REG_CBACK:
190703831d35Sstevel {
190803831d35Sstevel iosram_io_t req;
190903831d35Sstevel
191003831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
191103831d35Sstevel return (EFAULT);
191203831d35Sstevel }
191303831d35Sstevel
191403831d35Sstevel DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key));
191503831d35Sstevel
191603831d35Sstevel req.retval = iosram_register(req.key, iosram_dummy_cback,
191703831d35Sstevel (void *)(uintptr_t)req.key);
191803831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
191903831d35Sstevel error = EFAULT;
192003831d35Sstevel }
192103831d35Sstevel
192203831d35Sstevel return (error);
192303831d35Sstevel }
192403831d35Sstevel
192503831d35Sstevel case IOSRAM_UNREG_CBACK:
192603831d35Sstevel {
192703831d35Sstevel iosram_io_t req;
192803831d35Sstevel
192903831d35Sstevel if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
193003831d35Sstevel return (EFAULT);
193103831d35Sstevel }
193203831d35Sstevel
193303831d35Sstevel DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key));
193403831d35Sstevel
193503831d35Sstevel req.retval = iosram_unregister(req.key);
193603831d35Sstevel if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
193703831d35Sstevel error = EFAULT;
193803831d35Sstevel }
193903831d35Sstevel
194003831d35Sstevel return (error);
194103831d35Sstevel }
194203831d35Sstevel
194303831d35Sstevel case IOSRAM_SEMA_ACQUIRE:
194403831d35Sstevel {
194503831d35Sstevel DPRINTF(1, ("IOSRAM_SEMA_ACQUIRE\n"));
194603831d35Sstevel error = iosram_sema_acquire(NULL);
194703831d35Sstevel return (error);
194803831d35Sstevel }
194903831d35Sstevel
195003831d35Sstevel case IOSRAM_SEMA_RELEASE:
195103831d35Sstevel {
195203831d35Sstevel DPRINTF(1, ("IOSRAM_SEMA_RELEASE\n"));
195303831d35Sstevel error = iosram_sema_release();
195403831d35Sstevel return (error);
195503831d35Sstevel }
195603831d35Sstevel
195703831d35Sstevel #endif /* DEBUG */
195803831d35Sstevel
195903831d35Sstevel default:
196003831d35Sstevel DPRINTF(1, ("iosram_ioctl: Illegal command %x\n", cmd));
196103831d35Sstevel error = ENOTTY;
196203831d35Sstevel }
196303831d35Sstevel
196403831d35Sstevel return (error);
196503831d35Sstevel }
196603831d35Sstevel
196703831d35Sstevel
196803831d35Sstevel /*
196903831d35Sstevel * iosram_switch_tunnel(softp)
197003831d35Sstevel * Switch master tunnel to the specified instance
197103831d35Sstevel * Must be called while holding iosram_mutex
197203831d35Sstevel */
197303831d35Sstevel /*ARGSUSED*/
197403831d35Sstevel static int
iosram_switch_tunnel(iosramsoft_t * softp)197503831d35Sstevel iosram_switch_tunnel(iosramsoft_t *softp)
197603831d35Sstevel {
197703831d35Sstevel #ifdef DEBUG
197803831d35Sstevel int instance = softp->instance;
197903831d35Sstevel #endif
198003831d35Sstevel int error = 0;
198103831d35Sstevel iosramsoft_t *prev_master;
198203831d35Sstevel
198303831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
198403831d35Sstevel
198503831d35Sstevel DPRINTF(1, ("tunnel switch new master:%p (%d) current master:%p (%d)\n",
1986*07d06da5SSurya Prakki (void *)softp, instance, (void *)iosram_master,
198703831d35Sstevel ((iosram_master) ? iosram_master->instance : -1)));
198803831d35Sstevel IOSRAMLOG(1, "TSWTCH: new_master:%p (%p) iosram_master:%p (%d)\n",
198903831d35Sstevel softp, instance, iosram_master,
199003831d35Sstevel ((iosram_master) ? iosram_master->instance : -1));
199103831d35Sstevel
199203831d35Sstevel if (softp == NULL || (softp->state & IOSRAM_STATE_DETACH)) {
199303831d35Sstevel return (ENXIO);
199403831d35Sstevel }
199503831d35Sstevel if (iosram_master == softp) {
199603831d35Sstevel return (0);
199703831d35Sstevel }
199803831d35Sstevel
199903831d35Sstevel
200003831d35Sstevel /*
200103831d35Sstevel * We protect against the softp structure being deallocated by setting
200203831d35Sstevel * the IOSRAM_STATE_TSWITCH state flag. The detach routine will check
200303831d35Sstevel * for this flag and if set, it will wait for this flag to be reset or
200403831d35Sstevel * refuse the detach operation.
200503831d35Sstevel */
200603831d35Sstevel iosram_new_master = softp;
200703831d35Sstevel softp->state |= IOSRAM_STATE_TSWITCH;
200803831d35Sstevel prev_master = iosram_master;
200903831d35Sstevel if (prev_master) {
201003831d35Sstevel prev_master->state |= IOSRAM_STATE_TSWITCH;
201103831d35Sstevel }
201203831d35Sstevel mutex_exit(&iosram_mutex);
201303831d35Sstevel
201403831d35Sstevel /*
201503831d35Sstevel * Map the target IOSRAM, read the TOC, and register interrupts if not
201603831d35Sstevel * already done.
201703831d35Sstevel */
201803831d35Sstevel DPRINTF(1, ("iosram(%d): mapping IOSRAM and SBBC\n",
201903831d35Sstevel softp->instance));
202003831d35Sstevel IOSRAMLOG(1, "TSWTCH: mapping instance:%d softp:%p\n",
202103831d35Sstevel instance, softp, NULL, NULL);
202203831d35Sstevel
202303831d35Sstevel if (iosram_setup_map(softp) != DDI_SUCCESS) {
202403831d35Sstevel error = ENXIO;
202503831d35Sstevel } else if ((chunks == NULL) && (iosram_read_toc(softp) != 0)) {
202603831d35Sstevel iosram_remove_map(softp);
202703831d35Sstevel error = EINVAL;
202803831d35Sstevel } else if (iosram_add_intr(softp) != DDI_SUCCESS) {
202903831d35Sstevel /*
203003831d35Sstevel * If there was no previous master, purge the TOC data that
203103831d35Sstevel * iosram_read_toc() created.
203203831d35Sstevel */
203303831d35Sstevel if ((prev_master == NULL) && (chunks != NULL)) {
203403831d35Sstevel kmem_free(chunks, nchunks * sizeof (iosram_chunk_t));
203503831d35Sstevel chunks = NULL;
203603831d35Sstevel nchunks = 0;
203703831d35Sstevel iosram_init_hashtab();
203803831d35Sstevel }
203903831d35Sstevel iosram_remove_map(softp);
204003831d35Sstevel error = ENXIO;
204103831d35Sstevel }
204203831d35Sstevel
204303831d35Sstevel /*
204403831d35Sstevel * If we are asked to abort tunnel switch, do so now, before invoking
204503831d35Sstevel * the OBP callback.
204603831d35Sstevel */
204703831d35Sstevel if (iosram_tswitch_aborted) {
204803831d35Sstevel
204903831d35Sstevel /*
205003831d35Sstevel * Once the tunnel switch is aborted, this thread should not
205103831d35Sstevel * resume. If it does, we simply log a message. We can't unmap
205203831d35Sstevel * the new master IOSRAM as it may be accessed in
205303831d35Sstevel * iosram_abort_tswitch(). It will be unmapped when it is
205403831d35Sstevel * detached.
205503831d35Sstevel */
205603831d35Sstevel IOSRAMLOG(1,
205703831d35Sstevel "TSWTCH: aborted (pre OBP cback). Thread resumed.\n",
205803831d35Sstevel NULL, NULL, NULL, NULL);
205903831d35Sstevel error = EIO;
206003831d35Sstevel }
206103831d35Sstevel
206203831d35Sstevel if (error) {
206303831d35Sstevel IOSRAMLOG(1,
206403831d35Sstevel "TSWTCH: map failed instance:%d softp:%p error:%x\n",
206503831d35Sstevel instance, softp, error, NULL);
206603831d35Sstevel goto done;
206703831d35Sstevel }
206803831d35Sstevel
206903831d35Sstevel if (prev_master != NULL) {
207003831d35Sstevel int result;
207103831d35Sstevel
207203831d35Sstevel /*
207303831d35Sstevel * Now invoke the OBP interface to do the tunnel switch.
207403831d35Sstevel */
207503831d35Sstevel result = prom_starcat_switch_tunnel(softp->portid,
207603831d35Sstevel OBP_TSWITCH_REQREPLY);
207703831d35Sstevel if (result != 0) {
207803831d35Sstevel error = EIO;
207903831d35Sstevel }
208003831d35Sstevel IOSRAMLOG(1,
208103831d35Sstevel "TSWTCH: OBP tswitch portid:%x result:%x error:%x\n",
208203831d35Sstevel softp->portid, result, error, NULL);
208303831d35Sstevel IOSRAM_STAT(tswitch);
208403831d35Sstevel iosram_tswitch_tstamp = ddi_get_lbolt();
208503831d35Sstevel }
208603831d35Sstevel
208703831d35Sstevel mutex_enter(&iosram_mutex);
208803831d35Sstevel if (iosram_tswitch_aborted) {
208903831d35Sstevel /*
209003831d35Sstevel * Tunnel switch aborted. This thread should not resume.
209103831d35Sstevel * For now, we simply log a message, but don't unmap any
209203831d35Sstevel * IOSRAM at this stage as it may be accessed within the
209303831d35Sstevel * isoram_abort_tswitch(). The IOSRAM will be unmapped
209403831d35Sstevel * when that instance is detached.
209503831d35Sstevel */
209603831d35Sstevel if (iosram_tswitch_aborted) {
209703831d35Sstevel IOSRAMLOG(1,
209803831d35Sstevel "TSWTCH: aborted (post OBP cback). Thread"
209903831d35Sstevel " resumed.\n", NULL, NULL, NULL, NULL);
210003831d35Sstevel error = EIO;
210103831d35Sstevel mutex_exit(&iosram_mutex);
210203831d35Sstevel }
210303831d35Sstevel } else if (error) {
210403831d35Sstevel /*
210503831d35Sstevel * Tunnel switch failed. Continue using previous tunnel.
210603831d35Sstevel * However, unmap new (target) IOSRAM.
210703831d35Sstevel */
210803831d35Sstevel iosram_new_master = NULL;
210903831d35Sstevel mutex_exit(&iosram_mutex);
2110*07d06da5SSurya Prakki (void) iosram_remove_intr(softp);
211103831d35Sstevel iosram_remove_map(softp);
211203831d35Sstevel } else {
211303831d35Sstevel /*
211403831d35Sstevel * Tunnel switch was successful. Set the new master.
211503831d35Sstevel * Also unmap old master IOSRAM and remove any interrupts
211603831d35Sstevel * associated with that.
211703831d35Sstevel *
211803831d35Sstevel * Note that a call to iosram_force_write() allows access
211903831d35Sstevel * to the IOSRAM while tunnel switch is in progress. That
212003831d35Sstevel * means we need to set the new master before unmapping
212103831d35Sstevel * the old master.
212203831d35Sstevel */
212303831d35Sstevel iosram_set_master(softp);
212403831d35Sstevel iosram_new_master = NULL;
212503831d35Sstevel mutex_exit(&iosram_mutex);
212603831d35Sstevel
212703831d35Sstevel if (prev_master) {
212803831d35Sstevel IOSRAMLOG(1, "TSWTCH: unmapping prev_master:%p (%d)\n",
212903831d35Sstevel prev_master, prev_master->instance, NULL, NULL);
2130*07d06da5SSurya Prakki (void) iosram_remove_intr(prev_master);
213103831d35Sstevel iosram_remove_map(prev_master);
213203831d35Sstevel }
213303831d35Sstevel }
213403831d35Sstevel
213503831d35Sstevel done:
213603831d35Sstevel mutex_enter(&iosram_mutex);
213703831d35Sstevel
213803831d35Sstevel /*
213903831d35Sstevel * Clear the tunnel switch flag on the source and destination
214003831d35Sstevel * instances.
214103831d35Sstevel */
214203831d35Sstevel if (prev_master) {
214303831d35Sstevel prev_master->state &= ~IOSRAM_STATE_TSWITCH;
214403831d35Sstevel }
214503831d35Sstevel softp->state &= ~IOSRAM_STATE_TSWITCH;
214603831d35Sstevel
214703831d35Sstevel /*
214803831d35Sstevel * Since incoming interrupts could get lost during a tunnel switch,
214903831d35Sstevel * trigger a soft interrupt just in case. No harm other than a bit
215003831d35Sstevel * of wasted effort will be caused if no interrupts were dropped.
215103831d35Sstevel */
215203831d35Sstevel mutex_enter(&softp->intr_mutex);
215303831d35Sstevel iosram_master->intr_pending = 1;
215403831d35Sstevel if ((iosram_master->softintr_id != NULL) &&
215503831d35Sstevel (iosram_master->intr_busy == 0)) {
215603831d35Sstevel ddi_trigger_softintr(iosram_master->softintr_id);
215703831d35Sstevel }
215803831d35Sstevel mutex_exit(&softp->intr_mutex);
215903831d35Sstevel
216003831d35Sstevel IOSRAMLOG(1, "TSWTCH: done error:%d iosram_master:%p instance:%d\n",
216103831d35Sstevel error, iosram_master,
216203831d35Sstevel (iosram_master) ? iosram_master->instance : -1, NULL);
216303831d35Sstevel
216403831d35Sstevel return (error);
216503831d35Sstevel }
216603831d35Sstevel
216703831d35Sstevel
216803831d35Sstevel /*
216903831d35Sstevel * iosram_abort_tswitch()
217003831d35Sstevel * Must be called while holding iosram_mutex.
217103831d35Sstevel */
217203831d35Sstevel static void
iosram_abort_tswitch()217303831d35Sstevel iosram_abort_tswitch()
217403831d35Sstevel {
217503831d35Sstevel uint32_t master_valid, new_master_valid;
217603831d35Sstevel
217703831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
217803831d35Sstevel
217903831d35Sstevel if ((!iosram_tswitch_active) || iosram_tswitch_aborted) {
218003831d35Sstevel return;
218103831d35Sstevel }
218203831d35Sstevel
218303831d35Sstevel ASSERT(iosram_master != NULL);
218403831d35Sstevel
218503831d35Sstevel IOSRAMLOG(1, "ABORT: iosram_master:%p (%d) iosram_new_master:%p (%d)\n",
218603831d35Sstevel iosram_master, iosram_master->instance, iosram_new_master,
218703831d35Sstevel (iosram_new_master == NULL) ? -1 : iosram_new_master->instance);
218803831d35Sstevel
218903831d35Sstevel /*
219003831d35Sstevel * The first call to iosram_force_write() in the middle of tunnel switch
219103831d35Sstevel * will get here. We lookup IOSRAM VALID location and setup appropriate
219203831d35Sstevel * master, if one is still valid. We also set iosram_tswitch_aborted to
219303831d35Sstevel * prevent reentering this code and to catch if the OBP callback thread
219403831d35Sstevel * somehow resumes.
219503831d35Sstevel */
219603831d35Sstevel iosram_tswitch_aborted = 1;
219703831d35Sstevel
219803831d35Sstevel if ((iosram_new_master == NULL) ||
219903831d35Sstevel (iosram_new_master = iosram_master)) {
220003831d35Sstevel /*
220103831d35Sstevel * New master hasn't been selected yet, or OBP callback
220203831d35Sstevel * succeeded and we already selected new IOSRAM as master, but
220303831d35Sstevel * system crashed in the middle of unmapping previous master or
220403831d35Sstevel * cleaning up state. Use the existing master.
220503831d35Sstevel */
220603831d35Sstevel ASSERT(iosram_master->iosramp != NULL);
220703831d35Sstevel ASSERT(IOSRAM_GET_HDRFIELD32(iosram_master, status) ==
220803831d35Sstevel IOSRAM_VALID);
220903831d35Sstevel IOSRAMLOG(1, "ABORT: master (%d) already determined.\n",
221003831d35Sstevel iosram_master->instance, NULL, NULL, NULL);
221103831d35Sstevel
221203831d35Sstevel return;
221303831d35Sstevel }
221403831d35Sstevel
221503831d35Sstevel /*
221603831d35Sstevel * System crashed in the middle of tunnel switch and we know that the
221703831d35Sstevel * new target has not been marked master yet. That means, the old
221803831d35Sstevel * master should still be mapped. We need to abort the tunnel switch
221903831d35Sstevel * and setup a valid master, if possible, so that we can write to the
222003831d35Sstevel * IOSRAM.
222103831d35Sstevel *
222203831d35Sstevel * We select a new master based upon the IOSRAM header status fields in
222303831d35Sstevel * the previous master IOSRAM and the target IOSRAM as follows:
222403831d35Sstevel *
222503831d35Sstevel * iosram_master iosram-tswitch
222603831d35Sstevel * (Prev Master) (New Target) Decision
222703831d35Sstevel * --------------- --------------- -----------
222803831d35Sstevel * VALID don't care prev master
222903831d35Sstevel * INTRANSIT INVALID prev master
223003831d35Sstevel * INTRANSIT INTRANSIT prev master
223103831d35Sstevel * INTRANSIT VALID new target
223203831d35Sstevel * INVALID INVALID shouldn't ever happen
223303831d35Sstevel * INVALID INTRANSIT shouldn't ever happen
223403831d35Sstevel * INVALID VALID new target
223503831d35Sstevel */
223603831d35Sstevel
223703831d35Sstevel master_valid = (iosram_master->iosramp != NULL) ?
223803831d35Sstevel IOSRAM_GET_HDRFIELD32(iosram_master, status) : IOSRAM_INVALID;
223903831d35Sstevel new_master_valid = (iosram_new_master->iosramp != NULL) ?
224003831d35Sstevel IOSRAM_GET_HDRFIELD32(iosram_new_master, status) : IOSRAM_INVALID;
224103831d35Sstevel
224203831d35Sstevel if (master_valid == IOSRAM_VALID) {
224303831d35Sstevel /* EMPTY */
224403831d35Sstevel /*
224503831d35Sstevel * OBP hasn't been called yet or, if it has, it hasn't started
224603831d35Sstevel * copying yet. Use the existing master. Note that the new
224703831d35Sstevel * master may not be mapped yet.
224803831d35Sstevel */
224903831d35Sstevel IOSRAMLOG(1, "ABORT: prev master(%d) is VALID\n",
225003831d35Sstevel iosram_master->instance, NULL, NULL, NULL);
225103831d35Sstevel } else if (master_valid == IOSRAM_INTRANSIT) {
225203831d35Sstevel /*
225303831d35Sstevel * The system crashed after OBP started processing the tunnel
225403831d35Sstevel * switch but before the iosram driver determined that it was
225503831d35Sstevel * complete. Use the new master if it has been marked valid,
225603831d35Sstevel * meaning that OBP finished copying data to it, or the old
225703831d35Sstevel * master otherwise.
225803831d35Sstevel */
225903831d35Sstevel IOSRAMLOG(1, "ABORT: prev master(%d) is INTRANSIT\n",
226003831d35Sstevel iosram_master->instance, NULL, NULL, NULL);
226103831d35Sstevel
226203831d35Sstevel if (new_master_valid == IOSRAM_VALID) {
226303831d35Sstevel iosram_set_master(iosram_new_master);
226403831d35Sstevel IOSRAMLOG(1, "ABORT: new master(%d) is VALID\n",
226503831d35Sstevel iosram_new_master->instance, NULL, NULL,
226603831d35Sstevel NULL);
226703831d35Sstevel } else {
2268*07d06da5SSurya Prakki (void) prom_starcat_switch_tunnel(iosram_master->portid,
226903831d35Sstevel OBP_TSWITCH_NOREPLY);
227003831d35Sstevel
227103831d35Sstevel IOSRAMLOG(1, "ABORT: new master(%d) is INVALID\n",
227203831d35Sstevel iosram_new_master->instance, NULL, NULL,
227303831d35Sstevel NULL);
227403831d35Sstevel }
227503831d35Sstevel } else {
227603831d35Sstevel /*
227703831d35Sstevel * The system crashed after OBP marked the old master INVALID,
227803831d35Sstevel * which means the new master is the way to go.
227903831d35Sstevel */
228003831d35Sstevel IOSRAMLOG(1, "ABORT: prev master(%d) is INVALID\n",
228103831d35Sstevel iosram_master->instance, NULL, NULL, NULL);
228203831d35Sstevel
228303831d35Sstevel ASSERT(new_master_valid == IOSRAM_VALID);
228403831d35Sstevel
228503831d35Sstevel iosram_set_master(iosram_new_master);
228603831d35Sstevel }
228703831d35Sstevel
228803831d35Sstevel IOSRAMLOG(1, "ABORT: Instance %d selected as master\n",
228903831d35Sstevel iosram_master->instance, NULL, NULL, NULL);
229003831d35Sstevel }
229103831d35Sstevel
229203831d35Sstevel
229303831d35Sstevel /*
229403831d35Sstevel * iosram_switchfrom(instance)
229503831d35Sstevel * Switch master tunnel away from the specified instance
229603831d35Sstevel */
229703831d35Sstevel /*ARGSUSED*/
229803831d35Sstevel int
iosram_switchfrom(int instance)229903831d35Sstevel iosram_switchfrom(int instance)
230003831d35Sstevel {
230103831d35Sstevel struct iosramsoft *softp;
230203831d35Sstevel int error = 0;
230303831d35Sstevel int count;
230403831d35Sstevel clock_t current_tstamp;
230503831d35Sstevel clock_t tstamp_interval;
230603831d35Sstevel struct iosramsoft *last_master = NULL;
230703831d35Sstevel static int last_master_instance = -1;
230803831d35Sstevel
230903831d35Sstevel IOSRAMLOG(1, "SwtchFrom: instance:%d iosram_master:%p (%d)\n",
231003831d35Sstevel instance, iosram_master,
231103831d35Sstevel ((iosram_master) ? iosram_master->instance : -1), NULL);
231203831d35Sstevel
231303831d35Sstevel mutex_enter(&iosram_mutex);
231403831d35Sstevel
231503831d35Sstevel /*
231603831d35Sstevel * Wait if another tunnel switch is in progress
231703831d35Sstevel */
231803831d35Sstevel for (count = 0; iosram_tswitch_active && count < IOSRAM_TSWITCH_RETRY;
231903831d35Sstevel count++) {
232003831d35Sstevel iosram_tswitch_wakeup = 1;
232103831d35Sstevel cv_wait(&iosram_tswitch_wait, &iosram_mutex);
232203831d35Sstevel }
232303831d35Sstevel
232403831d35Sstevel if (iosram_tswitch_active) {
232503831d35Sstevel mutex_exit(&iosram_mutex);
232603831d35Sstevel return (EAGAIN);
232703831d35Sstevel }
232803831d35Sstevel
232903831d35Sstevel /*
233003831d35Sstevel * Check if the specified instance holds the tunnel. If not,
233103831d35Sstevel * then we are done.
233203831d35Sstevel */
233303831d35Sstevel if ((iosram_master == NULL) || (iosram_master->instance != instance)) {
233403831d35Sstevel mutex_exit(&iosram_mutex);
233503831d35Sstevel return (0);
233603831d35Sstevel }
233703831d35Sstevel
233803831d35Sstevel /*
233903831d35Sstevel * Before beginning the tunnel switch process, wait for any outstanding
234003831d35Sstevel * read/write activity to complete.
234103831d35Sstevel */
234203831d35Sstevel iosram_tswitch_active = 1;
234303831d35Sstevel while (iosram_rw_active) {
234403831d35Sstevel iosram_rw_wakeup = 1;
234503831d35Sstevel cv_wait(&iosram_rw_wait, &iosram_mutex);
234603831d35Sstevel }
234703831d35Sstevel
234803831d35Sstevel /*
234903831d35Sstevel * If a previous tunnel switch just completed, we have to make sure
235003831d35Sstevel * HWAD has enough time to find the new tunnel before we switch
235103831d35Sstevel * away from it. Otherwise, OBP's mailbox message to OSD will never
235203831d35Sstevel * get through. Just to be paranoid about synchronization of lbolt
235303831d35Sstevel * across different CPUs, make sure the current attempt isn't noted
235403831d35Sstevel * as starting _before_ the last tunnel switch completed.
235503831d35Sstevel */
235603831d35Sstevel current_tstamp = ddi_get_lbolt();
235703831d35Sstevel if (current_tstamp > iosram_tswitch_tstamp) {
235803831d35Sstevel tstamp_interval = current_tstamp - iosram_tswitch_tstamp;
235903831d35Sstevel } else {
236003831d35Sstevel tstamp_interval = 0;
236103831d35Sstevel }
236203831d35Sstevel if (drv_hztousec(tstamp_interval) < IOSRAM_TSWITCH_DELAY_US) {
236303831d35Sstevel mutex_exit(&iosram_mutex);
236403831d35Sstevel delay(drv_usectohz(IOSRAM_TSWITCH_DELAY_US) - tstamp_interval);
236503831d35Sstevel mutex_enter(&iosram_mutex);
236603831d35Sstevel }
236703831d35Sstevel
236803831d35Sstevel /*
236903831d35Sstevel * The specified instance holds the tunnel. We need to move it to some
237003831d35Sstevel * other IOSRAM. Try out all possible IOSRAMs listed in
237103831d35Sstevel * iosram_instances. For now, we always search from the first entry.
237203831d35Sstevel * In future, it may be desirable to start where we left off.
237303831d35Sstevel */
237403831d35Sstevel for (softp = iosram_instances; softp != NULL; softp = softp->next) {
237503831d35Sstevel if (iosram_tswitch_aborted) {
237603831d35Sstevel break;
237703831d35Sstevel }
237803831d35Sstevel
237903831d35Sstevel /* we can't switch _to_ the instance we're switching _from_ */
238003831d35Sstevel if (softp->instance == instance) {
238103831d35Sstevel continue;
238203831d35Sstevel }
238303831d35Sstevel
238403831d35Sstevel /* skip over instances being detached */
238503831d35Sstevel if (softp->state & IOSRAM_STATE_DETACH) {
238603831d35Sstevel continue;
238703831d35Sstevel }
238803831d35Sstevel
238903831d35Sstevel /*
239003831d35Sstevel * Try to avoid reverting to the last instance we switched away
239103831d35Sstevel * from, as we expect that one to be detached eventually. Keep
239203831d35Sstevel * track of it, though, so we can go ahead and try switching to
239303831d35Sstevel * it if no other viable candidates are found.
239403831d35Sstevel */
239503831d35Sstevel if (softp->instance == last_master_instance) {
239603831d35Sstevel last_master = softp;
239703831d35Sstevel continue;
239803831d35Sstevel }
239903831d35Sstevel
240003831d35Sstevel /*
240103831d35Sstevel * Do the tunnel switch. If successful, record the instance of
240203831d35Sstevel * the master we just left behind so we can try to avoid
240303831d35Sstevel * reverting to it next time.
240403831d35Sstevel */
240503831d35Sstevel if (iosram_switch_tunnel(softp) == 0) {
240603831d35Sstevel last_master_instance = instance;
240703831d35Sstevel break;
240803831d35Sstevel }
240903831d35Sstevel }
241003831d35Sstevel
241103831d35Sstevel /*
241203831d35Sstevel * If we failed to switch the tunnel, but we skipped over an instance
241303831d35Sstevel * that had previously been switched out of because we expected it to be
241403831d35Sstevel * detached, go ahead and try it anyway (unless the tswitch was aborted
241503831d35Sstevel * or the instance we skipped is finally being detached).
241603831d35Sstevel */
241703831d35Sstevel if ((softp == NULL) && (last_master != NULL) &&
241803831d35Sstevel !iosram_tswitch_aborted &&
241903831d35Sstevel !(last_master->state & IOSRAM_STATE_DETACH)) {
242003831d35Sstevel if (iosram_switch_tunnel(last_master) == 0) {
242103831d35Sstevel softp = last_master;
242203831d35Sstevel last_master_instance = instance;
242303831d35Sstevel }
242403831d35Sstevel }
242503831d35Sstevel
242603831d35Sstevel if ((softp == NULL) || (iosram_tswitch_aborted)) {
242703831d35Sstevel error = EIO;
242803831d35Sstevel }
242903831d35Sstevel
243003831d35Sstevel /*
243103831d35Sstevel * If there are additional tunnel switches queued up waiting for this
243203831d35Sstevel * one to complete, wake them up.
243303831d35Sstevel */
243403831d35Sstevel if (iosram_tswitch_wakeup) {
243503831d35Sstevel iosram_tswitch_wakeup = 0;
243603831d35Sstevel cv_broadcast(&iosram_tswitch_wait);
243703831d35Sstevel }
243803831d35Sstevel iosram_tswitch_active = 0;
243903831d35Sstevel mutex_exit(&iosram_mutex);
244003831d35Sstevel return (error);
244103831d35Sstevel }
244203831d35Sstevel
244303831d35Sstevel
244403831d35Sstevel /*
244503831d35Sstevel * iosram_tunnel_capable(softp)
244603831d35Sstevel * Check if this IOSRAM instance is tunnel-capable by looing at
244703831d35Sstevel * "tunnel-capable" property.
244803831d35Sstevel */
244903831d35Sstevel static int
iosram_tunnel_capable(struct iosramsoft * softp)245003831d35Sstevel iosram_tunnel_capable(struct iosramsoft *softp)
245103831d35Sstevel {
245203831d35Sstevel int proplen;
245303831d35Sstevel int tunnel_capable;
245403831d35Sstevel
245503831d35Sstevel /*
245603831d35Sstevel * Look up IOSRAM_TUNNELOK_PROP property, if any.
245703831d35Sstevel */
245803831d35Sstevel proplen = sizeof (tunnel_capable);
245903831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, softp->dip,
246003831d35Sstevel DDI_PROP_DONTPASS, IOSRAM_TUNNELOK_PROP, (caddr_t)&tunnel_capable,
246103831d35Sstevel &proplen) != DDI_PROP_SUCCESS) {
246203831d35Sstevel tunnel_capable = 0;
246303831d35Sstevel }
246403831d35Sstevel return (tunnel_capable);
246503831d35Sstevel }
246603831d35Sstevel
246703831d35Sstevel
246803831d35Sstevel static int
iosram_sbbc_setup_map(struct iosramsoft * softp)246903831d35Sstevel iosram_sbbc_setup_map(struct iosramsoft *softp)
247003831d35Sstevel {
247103831d35Sstevel int rv;
247203831d35Sstevel struct ddi_device_acc_attr attr;
247303831d35Sstevel dev_info_t *dip = softp->dip;
247403831d35Sstevel uint32_t sema_val;
247503831d35Sstevel
247603831d35Sstevel attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
247703831d35Sstevel attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
247803831d35Sstevel attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
247903831d35Sstevel
248003831d35Sstevel mutex_enter(&iosram_mutex);
248103831d35Sstevel mutex_enter(&softp->intr_mutex);
248203831d35Sstevel
248303831d35Sstevel /*
248403831d35Sstevel * Map SBBC region in
248503831d35Sstevel */
248603831d35Sstevel if ((rv = ddi_regs_map_setup(dip, IOSRAM_SBBC_MAP_INDEX,
248703831d35Sstevel (caddr_t *)&softp->sbbc_region,
248803831d35Sstevel IOSRAM_SBBC_MAP_OFFSET, sizeof (iosram_sbbc_region_t),
248903831d35Sstevel &attr, &softp->sbbc_handle)) != DDI_SUCCESS) {
249003831d35Sstevel DPRINTF(1, ("Failed to map SBBC region.\n"));
249103831d35Sstevel mutex_exit(&softp->intr_mutex);
249203831d35Sstevel mutex_exit(&iosram_mutex);
249303831d35Sstevel return (rv);
249403831d35Sstevel }
249503831d35Sstevel
249603831d35Sstevel /*
249703831d35Sstevel * Disable SBBC interrupts. SBBC interrupts are enabled
249803831d35Sstevel * once the interrupt handler is registered.
249903831d35Sstevel */
250003831d35Sstevel ddi_put32(softp->sbbc_handle,
250103831d35Sstevel &(softp->sbbc_region->int_enable.reg), 0x0);
250203831d35Sstevel
250303831d35Sstevel /*
250403831d35Sstevel * Clear hardware semaphore value if appropriate.
250503831d35Sstevel * When the first SBBC is mapped in by the IOSRAM driver,
250603831d35Sstevel * the value of the semaphore should be initialized only
250703831d35Sstevel * if it is not held by SMS. For subsequent SBBC's, the
250803831d35Sstevel * semaphore will be always initialized.
250903831d35Sstevel */
251003831d35Sstevel sema_val = IOSRAM_SEMA_RD(softp);
251103831d35Sstevel
251203831d35Sstevel if (!iosram_master) {
251303831d35Sstevel /* the first SBBC is being mapped in */
251403831d35Sstevel if (!(IOSRAM_SEMA_IS_HELD(sema_val) &&
251503831d35Sstevel IOSRAM_SEMA_GET_IDX(sema_val) == IOSRAM_SEMA_SMS_IDX)) {
251603831d35Sstevel /* not held by SMS, we clear the semaphore */
251703831d35Sstevel IOSRAM_SEMA_WR(softp, 0);
251803831d35Sstevel }
251903831d35Sstevel } else {
252003831d35Sstevel /* not the first SBBC, we clear the semaphore */
252103831d35Sstevel IOSRAM_SEMA_WR(softp, 0);
252203831d35Sstevel }
252303831d35Sstevel
252403831d35Sstevel mutex_exit(&softp->intr_mutex);
252503831d35Sstevel mutex_exit(&iosram_mutex);
252603831d35Sstevel return (0);
252703831d35Sstevel }
252803831d35Sstevel
252903831d35Sstevel
253003831d35Sstevel static int
iosram_setup_map(struct iosramsoft * softp)253103831d35Sstevel iosram_setup_map(struct iosramsoft *softp)
253203831d35Sstevel {
253303831d35Sstevel int instance = softp->instance;
253403831d35Sstevel dev_info_t *dip = softp->dip;
253503831d35Sstevel int portid;
253603831d35Sstevel int proplen;
253703831d35Sstevel caddr_t propvalue;
253803831d35Sstevel struct ddi_device_acc_attr attr;
253903831d35Sstevel
254003831d35Sstevel attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
254103831d35Sstevel attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
254203831d35Sstevel attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
254303831d35Sstevel
254403831d35Sstevel /*
254503831d35Sstevel * Lookup IOSRAM_REG_PROP property to find out our IOSRAM length
254603831d35Sstevel */
254703831d35Sstevel if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
254803831d35Sstevel DDI_PROP_DONTPASS, IOSRAM_REG_PROP, (caddr_t)&propvalue,
254903831d35Sstevel &proplen) != DDI_PROP_SUCCESS) {
255003831d35Sstevel cmn_err(CE_WARN, "iosram(%d): can't find register property.\n",
255103831d35Sstevel instance);
255203831d35Sstevel return (DDI_FAILURE);
255303831d35Sstevel } else {
255403831d35Sstevel iosram_reg_t *regprop = (iosram_reg_t *)propvalue;
255503831d35Sstevel
255603831d35Sstevel DPRINTF(1, ("SetupMap(%d): Got reg prop: %x %x %x\n",
255703831d35Sstevel instance, regprop->addr_hi,
255803831d35Sstevel regprop->addr_lo, regprop->size));
255903831d35Sstevel
256003831d35Sstevel softp->iosramlen = regprop->size;
256103831d35Sstevel
256203831d35Sstevel kmem_free(propvalue, proplen);
256303831d35Sstevel }
256403831d35Sstevel DPRINTF(1, ("SetupMap(%d): IOSRAM length: 0x%x\n", instance,
256503831d35Sstevel softp->iosramlen));
256603831d35Sstevel softp->handle = NULL;
256703831d35Sstevel
256803831d35Sstevel /*
256903831d35Sstevel * To minimize boot time, we map the entire IOSRAM as opposed to
257003831d35Sstevel * mapping individual chunk via ddi_regs_map_setup() call.
257103831d35Sstevel */
257203831d35Sstevel if (ddi_regs_map_setup(dip, 0, (caddr_t *)&softp->iosramp,
257303831d35Sstevel 0x0, softp->iosramlen, &attr, &softp->handle) != DDI_SUCCESS) {
257403831d35Sstevel cmn_err(CE_WARN, "iosram(%d): failed to map IOSRAM len:%x\n",
257503831d35Sstevel instance, softp->iosramlen);
257603831d35Sstevel iosram_remove_map(softp);
257703831d35Sstevel return (DDI_FAILURE);
257803831d35Sstevel }
257903831d35Sstevel
258003831d35Sstevel /*
258103831d35Sstevel * Lookup PORTID property on my parent hierarchy
258203831d35Sstevel */
258303831d35Sstevel proplen = sizeof (portid);
258403831d35Sstevel if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
258503831d35Sstevel 0, IOSRAM_PORTID_PROP, (caddr_t)&portid,
258603831d35Sstevel &proplen) != DDI_PROP_SUCCESS) {
258703831d35Sstevel cmn_err(CE_WARN, "iosram(%d): can't find portid property.\n",
258803831d35Sstevel instance);
258903831d35Sstevel iosram_remove_map(softp);
259003831d35Sstevel return (DDI_FAILURE);
259103831d35Sstevel }
259203831d35Sstevel softp->portid = portid;
259303831d35Sstevel
259403831d35Sstevel if (iosram_sbbc_setup_map(softp) != DDI_SUCCESS) {
259503831d35Sstevel cmn_err(CE_WARN, "iosram(%d): can't map SBBC region.\n",
259603831d35Sstevel instance);
259703831d35Sstevel iosram_remove_map(softp);
259803831d35Sstevel return (DDI_FAILURE);
259903831d35Sstevel }
260003831d35Sstevel
260103831d35Sstevel mutex_enter(&iosram_mutex);
260203831d35Sstevel softp->state |= IOSRAM_STATE_MAPPED;
260303831d35Sstevel mutex_exit(&iosram_mutex);
260403831d35Sstevel
260503831d35Sstevel return (DDI_SUCCESS);
260603831d35Sstevel }
260703831d35Sstevel
260803831d35Sstevel
260903831d35Sstevel static void
iosram_remove_map(struct iosramsoft * softp)261003831d35Sstevel iosram_remove_map(struct iosramsoft *softp)
261103831d35Sstevel {
261203831d35Sstevel mutex_enter(&iosram_mutex);
261303831d35Sstevel
261403831d35Sstevel ASSERT((softp->state & IOSRAM_STATE_MASTER) == 0);
261503831d35Sstevel
261603831d35Sstevel if (softp->handle) {
261703831d35Sstevel ddi_regs_map_free(&softp->handle);
261803831d35Sstevel softp->handle = NULL;
261903831d35Sstevel }
262003831d35Sstevel softp->iosramp = NULL;
262103831d35Sstevel
262203831d35Sstevel /*
262303831d35Sstevel * Umap SBBC registers region. Shared with handler for SBBC
262403831d35Sstevel * interrupts, take intr_mutex.
262503831d35Sstevel */
262603831d35Sstevel mutex_enter(&softp->intr_mutex);
262703831d35Sstevel if (softp->sbbc_region) {
262803831d35Sstevel ddi_regs_map_free(&softp->sbbc_handle);
262903831d35Sstevel softp->sbbc_region = NULL;
263003831d35Sstevel }
263103831d35Sstevel mutex_exit(&softp->intr_mutex);
263203831d35Sstevel
263303831d35Sstevel softp->state &= ~IOSRAM_STATE_MAPPED;
263403831d35Sstevel
263503831d35Sstevel mutex_exit(&iosram_mutex);
263603831d35Sstevel }
263703831d35Sstevel
263803831d35Sstevel
263903831d35Sstevel /*
264003831d35Sstevel * iosram_is_chosen(struct iosramsoft *softp)
264103831d35Sstevel *
264203831d35Sstevel * Looks up "chosen" node property to
264303831d35Sstevel * determine if it is the chosen IOSRAM.
264403831d35Sstevel */
264503831d35Sstevel static int
iosram_is_chosen(struct iosramsoft * softp)264603831d35Sstevel iosram_is_chosen(struct iosramsoft *softp)
264703831d35Sstevel {
264803831d35Sstevel char chosen_iosram[MAXNAMELEN];
264903831d35Sstevel char pn[MAXNAMELEN];
265003831d35Sstevel int nodeid;
265103831d35Sstevel int chosen;
265203831d35Sstevel pnode_t dnode;
265303831d35Sstevel
265403831d35Sstevel /*
265503831d35Sstevel * Get /chosen node info. prom interface will handle errors.
265603831d35Sstevel */
265703831d35Sstevel dnode = prom_chosennode();
265803831d35Sstevel
265903831d35Sstevel /*
266003831d35Sstevel * Look for the "iosram" property on the chosen node with a prom
266103831d35Sstevel * interface as ddi_find_devinfo() couldn't be used (calls
266203831d35Sstevel * ddi_walk_devs() that creates one extra lock on the device tree).
266303831d35Sstevel */
266403831d35Sstevel if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) {
266503831d35Sstevel /*
266603831d35Sstevel * Can't find IOSRAM_CHOSEN_PROP property under chosen node
266703831d35Sstevel */
266803831d35Sstevel cmn_err(CE_WARN,
266903831d35Sstevel "iosram(%d): can't find chosen iosram property\n",
267003831d35Sstevel softp->instance);
267103831d35Sstevel return (0);
267203831d35Sstevel }
267303831d35Sstevel
267403831d35Sstevel DPRINTF(1, ("iosram(%d): Got '%x' for chosen '%s' property\n",
267503831d35Sstevel softp->instance, nodeid, IOSRAM_CHOSEN_PROP));
267603831d35Sstevel
267703831d35Sstevel /*
267803831d35Sstevel * get the full OBP pathname of this node
267903831d35Sstevel */
268003831d35Sstevel if (prom_phandle_to_path((phandle_t)nodeid, chosen_iosram,
268103831d35Sstevel sizeof (chosen_iosram)) < 0) {
268203831d35Sstevel cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", nodeid);
268303831d35Sstevel return (0);
268403831d35Sstevel }
268503831d35Sstevel DPRINTF(1, ("iosram(%d): prom_phandle_to_path(%x) is '%s'\n",
268603831d35Sstevel softp->instance, nodeid, chosen_iosram));
268703831d35Sstevel
268803831d35Sstevel (void) ddi_pathname(softp->dip, pn);
268903831d35Sstevel DPRINTF(1, ("iosram(%d): ddi_pathname(%p) is '%s'\n",
2690*07d06da5SSurya Prakki softp->instance, (void *)softp->dip, pn));
269103831d35Sstevel
269203831d35Sstevel chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0;
269303831d35Sstevel DPRINTF(1, ("iosram(%d): ... %s\n", softp->instance,
269403831d35Sstevel chosen ? "MASTER" : "SLAVE"));
269503831d35Sstevel IOSRAMLOG(1, "iosram(%d): ... %s\n", softp->instance,
269603831d35Sstevel (chosen ? "MASTER" : "SLAVE"), NULL, NULL);
269703831d35Sstevel
269803831d35Sstevel return (chosen);
269903831d35Sstevel }
270003831d35Sstevel
270103831d35Sstevel
270203831d35Sstevel /*
270303831d35Sstevel * iosram_set_master(struct iosramsoft *softp)
270403831d35Sstevel *
270503831d35Sstevel * Set master tunnel to the specified IOSRAM
270603831d35Sstevel * Must be called while holding iosram_mutex.
270703831d35Sstevel */
270803831d35Sstevel static void
iosram_set_master(struct iosramsoft * softp)270903831d35Sstevel iosram_set_master(struct iosramsoft *softp)
271003831d35Sstevel {
271103831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
271203831d35Sstevel ASSERT(softp != NULL);
271303831d35Sstevel ASSERT(softp->state & IOSRAM_STATE_MAPPED);
271403831d35Sstevel ASSERT(IOSRAM_GET_HDRFIELD32(softp, status) == IOSRAM_VALID);
271503831d35Sstevel
271603831d35Sstevel /*
271703831d35Sstevel * Clear MASTER flag on any previous IOSRAM master, if any
271803831d35Sstevel */
271903831d35Sstevel if (iosram_master && (iosram_master != softp)) {
272003831d35Sstevel iosram_master->state &= ~IOSRAM_STATE_MASTER;
272103831d35Sstevel }
272203831d35Sstevel
272303831d35Sstevel /*
272403831d35Sstevel * Setup new IOSRAM master
272503831d35Sstevel */
272603831d35Sstevel iosram_update_addrs(softp);
272703831d35Sstevel iosram_handle = softp->handle;
272803831d35Sstevel softp->state |= IOSRAM_STATE_MASTER;
272903831d35Sstevel softp->tswitch_ok++;
273003831d35Sstevel iosram_master = softp;
273103831d35Sstevel
273203831d35Sstevel IOSRAMLOG(1, "SETMASTER: softp:%p instance:%d\n", softp,
273303831d35Sstevel softp->instance, NULL, NULL);
273403831d35Sstevel }
273503831d35Sstevel
273603831d35Sstevel
273703831d35Sstevel /*
273803831d35Sstevel * iosram_read_toc()
273903831d35Sstevel *
274003831d35Sstevel * Read the TOC from an IOSRAM instance that has been mapped in.
274103831d35Sstevel * If the TOC is flawed or the IOSRAM isn't valid, return an error.
274203831d35Sstevel */
274303831d35Sstevel static int
iosram_read_toc(struct iosramsoft * softp)274403831d35Sstevel iosram_read_toc(struct iosramsoft *softp)
274503831d35Sstevel {
274603831d35Sstevel int i;
274703831d35Sstevel int instance = softp->instance;
274803831d35Sstevel uint8_t *toc_entryp;
274903831d35Sstevel iosram_flags_t *flagsp = NULL;
275003831d35Sstevel int new_nchunks;
275103831d35Sstevel iosram_chunk_t *new_chunks;
275203831d35Sstevel iosram_chunk_t *chunkp;
275303831d35Sstevel iosram_chunk_t *old_chunkp;
275403831d35Sstevel iosram_toc_entry_t index;
275503831d35Sstevel
275603831d35Sstevel /*
275703831d35Sstevel * Never try to read the TOC out of an unmapped IOSRAM.
275803831d35Sstevel */
275903831d35Sstevel ASSERT(softp->state & IOSRAM_STATE_MAPPED);
276003831d35Sstevel
276103831d35Sstevel mutex_enter(&iosram_mutex);
276203831d35Sstevel
276303831d35Sstevel /*
276403831d35Sstevel * Check to make sure this IOSRAM is marked valid. Return
276503831d35Sstevel * an error if it isn't.
276603831d35Sstevel */
276703831d35Sstevel if (IOSRAM_GET_HDRFIELD32(softp, status) != IOSRAM_VALID) {
276803831d35Sstevel DPRINTF(1, ("iosram_read_toc(%d): IOSRAM not flagged valid\n",
276903831d35Sstevel instance));
277003831d35Sstevel mutex_exit(&iosram_mutex);
277103831d35Sstevel return (EINVAL);
277203831d35Sstevel }
277303831d35Sstevel
277403831d35Sstevel /*
277503831d35Sstevel * Get the location of the TOC.
277603831d35Sstevel */
277703831d35Sstevel toc_entryp = softp->iosramp + IOSRAM_GET_HDRFIELD32(softp, toc_offset);
277803831d35Sstevel
277903831d35Sstevel /*
278003831d35Sstevel * Read the index entry from the TOC and make sure it looks correct.
278103831d35Sstevel */
278203831d35Sstevel ddi_rep_get8(softp->handle, (uint8_t *)&index, toc_entryp,
278303831d35Sstevel sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR);
278403831d35Sstevel if ((index.key != IOSRAM_INDEX_KEY) ||
278503831d35Sstevel (index.off != IOSRAM_INDEX_OFF)) {
278603831d35Sstevel cmn_err(CE_WARN, "iosram(%d): invalid TOC index.\n", instance);
278703831d35Sstevel mutex_exit(&iosram_mutex);
278803831d35Sstevel return (EINVAL);
278903831d35Sstevel }
279003831d35Sstevel
279103831d35Sstevel /*
279203831d35Sstevel * Allocate storage for the new chunks array and initialize it with data
279303831d35Sstevel * from the TOC and callback data from the corresponding old chunk, if
279403831d35Sstevel * it exists.
279503831d35Sstevel */
279603831d35Sstevel new_nchunks = index.len - 1;
279703831d35Sstevel new_chunks = (iosram_chunk_t *)kmem_zalloc(new_nchunks *
279803831d35Sstevel sizeof (iosram_chunk_t), KM_SLEEP);
279903831d35Sstevel for (i = 0, chunkp = new_chunks; i < new_nchunks; i++, chunkp++) {
280003831d35Sstevel toc_entryp += sizeof (iosram_toc_entry_t);
280103831d35Sstevel ddi_rep_get8(softp->handle, (uint8_t *)&(chunkp->toc_data),
280203831d35Sstevel toc_entryp, sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR);
280303831d35Sstevel chunkp->hash = NULL;
280403831d35Sstevel if ((chunkp->toc_data.off < softp->iosramlen) &&
280503831d35Sstevel (chunkp->toc_data.len <= softp->iosramlen) &&
280603831d35Sstevel ((chunkp->toc_data.off + chunkp->toc_data.len) <=
280703831d35Sstevel softp->iosramlen)) {
280803831d35Sstevel chunkp->basep = softp->iosramp + chunkp->toc_data.off;
280903831d35Sstevel DPRINTF(1,
2810*07d06da5SSurya Prakki ("iosram_read_toc(%d): k:%x o:%x l:%x p:%p\n",
281103831d35Sstevel instance, chunkp->toc_data.key,
281203831d35Sstevel chunkp->toc_data.off, chunkp->toc_data.len,
2813*07d06da5SSurya Prakki (void *)chunkp->basep));
281403831d35Sstevel } else {
281503831d35Sstevel cmn_err(CE_WARN, "iosram(%d): TOC entry %d"
281603831d35Sstevel "out of range... off:%x len:%x\n",
281703831d35Sstevel instance, i + 1, chunkp->toc_data.off,
281803831d35Sstevel chunkp->toc_data.len);
281903831d35Sstevel kmem_free(new_chunks, new_nchunks *
282003831d35Sstevel sizeof (iosram_chunk_t));
282103831d35Sstevel mutex_exit(&iosram_mutex);
282203831d35Sstevel return (EINVAL);
282303831d35Sstevel }
282403831d35Sstevel
282503831d35Sstevel /*
282603831d35Sstevel * Note the existence of the flags chunk, which is required in
282703831d35Sstevel * a correct TOC.
282803831d35Sstevel */
282903831d35Sstevel if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
283003831d35Sstevel flagsp = (iosram_flags_t *)chunkp->basep;
283103831d35Sstevel }
283203831d35Sstevel
283303831d35Sstevel /*
283403831d35Sstevel * If there was an entry for this chunk in the old list, copy
283503831d35Sstevel * the callback data from old to new storage.
283603831d35Sstevel */
283703831d35Sstevel if ((nchunks > 0) &&
283803831d35Sstevel ((old_chunkp = iosram_find_chunk(chunkp->toc_data.key)) !=
283903831d35Sstevel NULL)) {
284003831d35Sstevel bcopy(&(old_chunkp->cback), &(chunkp->cback),
284103831d35Sstevel sizeof (iosram_cback_t));
284203831d35Sstevel }
284303831d35Sstevel }
284403831d35Sstevel /*
284503831d35Sstevel * The TOC is malformed if there is no entry for the flags chunk.
284603831d35Sstevel */
284703831d35Sstevel if (flagsp == NULL) {
284803831d35Sstevel kmem_free(new_chunks, new_nchunks * sizeof (iosram_chunk_t));
284903831d35Sstevel mutex_exit(&iosram_mutex);
285003831d35Sstevel return (EINVAL);
285103831d35Sstevel }
285203831d35Sstevel
285303831d35Sstevel /*
285403831d35Sstevel * Free any memory that is no longer needed and install the new data
285503831d35Sstevel * as current data.
285603831d35Sstevel */
285703831d35Sstevel if (chunks != NULL) {
285803831d35Sstevel kmem_free(chunks, nchunks * sizeof (iosram_chunk_t));
285903831d35Sstevel }
286003831d35Sstevel chunks = new_chunks;
286103831d35Sstevel nchunks = new_nchunks;
286203831d35Sstevel iosram_init_hashtab();
286303831d35Sstevel
286403831d35Sstevel mutex_exit(&iosram_mutex);
286503831d35Sstevel return (0);
286603831d35Sstevel }
286703831d35Sstevel
286803831d35Sstevel
286903831d35Sstevel /*
287003831d35Sstevel * iosram_init_hashtab()
287103831d35Sstevel *
287203831d35Sstevel * Initialize the hash table and populate it with the IOSRAM
287303831d35Sstevel * chunks previously read from the TOC. The caller must hold the
287403831d35Sstevel * ioram_mutex lock.
287503831d35Sstevel */
287603831d35Sstevel static void
iosram_init_hashtab(void)287703831d35Sstevel iosram_init_hashtab(void)
287803831d35Sstevel {
287903831d35Sstevel int i, bucket;
288003831d35Sstevel iosram_chunk_t *chunkp;
288103831d35Sstevel
288203831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
288303831d35Sstevel
288403831d35Sstevel for (i = 0; i < IOSRAM_HASHSZ; i++) {
288503831d35Sstevel iosram_hashtab[i] = NULL;
288603831d35Sstevel }
288703831d35Sstevel
288803831d35Sstevel if (chunks) {
288903831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
289003831d35Sstevel /*
289103831d35Sstevel * Hide the flags chunk by leaving it out of the hash
289203831d35Sstevel * table.
289303831d35Sstevel */
289403831d35Sstevel if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
289503831d35Sstevel continue;
289603831d35Sstevel }
289703831d35Sstevel
289803831d35Sstevel /*
289903831d35Sstevel * Add the current chunk to the hash table.
290003831d35Sstevel */
290103831d35Sstevel bucket = IOSRAM_HASH(chunkp->toc_data.key);
290203831d35Sstevel chunkp->hash = iosram_hashtab[bucket];
290303831d35Sstevel iosram_hashtab[bucket] = chunkp;
290403831d35Sstevel }
290503831d35Sstevel }
290603831d35Sstevel }
290703831d35Sstevel
290803831d35Sstevel
290903831d35Sstevel /*
291003831d35Sstevel * iosram_update_addrs()
291103831d35Sstevel *
291203831d35Sstevel * Process the chunk list, updating each chunk's basep, which is a pointer
291303831d35Sstevel * to the beginning of the chunk's memory in kvaddr space. Record the
291403831d35Sstevel * basep value of the flags chunk to speed up flag access. The caller
291503831d35Sstevel * must hold the iosram_mutex lock.
291603831d35Sstevel */
291703831d35Sstevel static void
iosram_update_addrs(struct iosramsoft * softp)291803831d35Sstevel iosram_update_addrs(struct iosramsoft *softp)
291903831d35Sstevel {
292003831d35Sstevel int i;
292103831d35Sstevel iosram_flags_t *flagsp;
292203831d35Sstevel iosram_chunk_t *chunkp;
292303831d35Sstevel
292403831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
292503831d35Sstevel
292603831d35Sstevel /*
292703831d35Sstevel * First go through all of the chunks updating their base pointers and
292803831d35Sstevel * looking for the flags chunk.
292903831d35Sstevel */
293003831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
293103831d35Sstevel chunkp->basep = softp->iosramp + chunkp->toc_data.off;
293203831d35Sstevel if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
293303831d35Sstevel flagsp = (iosram_flags_t *)(chunkp->basep);
293403831d35Sstevel DPRINTF(1,
293503831d35Sstevel ("iosram_update_addrs flags: o:0x%08x p:%p",
2936*07d06da5SSurya Prakki chunkp->toc_data.off, (void *)flagsp));
293703831d35Sstevel }
293803831d35Sstevel }
293903831d35Sstevel
294003831d35Sstevel /*
294103831d35Sstevel * Now, go through and update each chunk's flags pointer. This can't be
294203831d35Sstevel * done in the first loop because we don't have the address of the flags
294303831d35Sstevel * chunk yet.
294403831d35Sstevel */
294503831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
294603831d35Sstevel chunkp->flagsp = flagsp++;
294703831d35Sstevel DPRINTF(1, ("iosram_update_addrs: k:0x%x f:%p\n",
2948*07d06da5SSurya Prakki chunkp->toc_data.key, (void *)chunkp->flagsp));
294903831d35Sstevel }
295003831d35Sstevel }
295103831d35Sstevel
295203831d35Sstevel /*
295303831d35Sstevel * iosram_find_chunk(key)
295403831d35Sstevel *
295503831d35Sstevel * Return a pointer to iosram_chunk structure corresponding to the
295603831d35Sstevel * "key" IOSRAM chunk. The caller must hold the iosram_mutex lock.
295703831d35Sstevel */
295803831d35Sstevel static iosram_chunk_t *
iosram_find_chunk(uint32_t key)295903831d35Sstevel iosram_find_chunk(uint32_t key)
296003831d35Sstevel {
296103831d35Sstevel iosram_chunk_t *chunkp;
296203831d35Sstevel int index = IOSRAM_HASH(key);
296303831d35Sstevel
296403831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
296503831d35Sstevel
296603831d35Sstevel for (chunkp = iosram_hashtab[index]; chunkp; chunkp = chunkp->hash) {
296703831d35Sstevel if (chunkp->toc_data.key == key) {
296803831d35Sstevel break;
296903831d35Sstevel }
297003831d35Sstevel }
297103831d35Sstevel
297203831d35Sstevel return (chunkp);
297303831d35Sstevel }
297403831d35Sstevel
297503831d35Sstevel
297603831d35Sstevel /*
297703831d35Sstevel * iosram_add_intr(iosramsoft_t *)
297803831d35Sstevel */
297903831d35Sstevel static int
iosram_add_intr(iosramsoft_t * softp)298003831d35Sstevel iosram_add_intr(iosramsoft_t *softp)
298103831d35Sstevel {
298203831d35Sstevel IOSRAMLOG(2, "ADDINTR: softp:%p instance:%d\n",
298303831d35Sstevel softp, softp->instance, NULL, NULL);
298403831d35Sstevel
298503831d35Sstevel if (ddi_add_softintr(softp->dip, DDI_SOFTINT_MED,
298603831d35Sstevel &softp->softintr_id, &softp->soft_iblk, NULL,
298703831d35Sstevel iosram_softintr, (caddr_t)softp) != DDI_SUCCESS) {
298803831d35Sstevel cmn_err(CE_WARN,
298903831d35Sstevel "iosram(%d): Can't register softintr.\n",
299003831d35Sstevel softp->instance);
299103831d35Sstevel return (DDI_FAILURE);
299203831d35Sstevel }
299303831d35Sstevel
299403831d35Sstevel if (ddi_add_intr(softp->dip, 0, &softp->real_iblk, NULL,
299503831d35Sstevel iosram_intr, (caddr_t)softp) != DDI_SUCCESS) {
299603831d35Sstevel cmn_err(CE_WARN,
299703831d35Sstevel "iosram(%d): Can't register intr"
299803831d35Sstevel " handler.\n", softp->instance);
299903831d35Sstevel ddi_remove_softintr(softp->softintr_id);
300003831d35Sstevel return (DDI_FAILURE);
300103831d35Sstevel }
300203831d35Sstevel
300303831d35Sstevel /*
300403831d35Sstevel * Enable SBBC interrupts
300503831d35Sstevel */
300603831d35Sstevel ddi_put32(softp->sbbc_handle, &(softp->sbbc_region->int_enable.reg),
300703831d35Sstevel IOSRAM_SBBC_INT0|IOSRAM_SBBC_INT1);
300803831d35Sstevel
300903831d35Sstevel return (DDI_SUCCESS);
301003831d35Sstevel }
301103831d35Sstevel
301203831d35Sstevel
301303831d35Sstevel /*
301403831d35Sstevel * iosram_remove_intr(iosramsoft_t *)
301503831d35Sstevel */
301603831d35Sstevel static int
iosram_remove_intr(iosramsoft_t * softp)301703831d35Sstevel iosram_remove_intr(iosramsoft_t *softp)
301803831d35Sstevel {
301903831d35Sstevel IOSRAMLOG(2, "REMINTR: softp:%p instance:%d\n",
302003831d35Sstevel softp, softp->instance, NULL, NULL);
302103831d35Sstevel
302203831d35Sstevel /*
302303831d35Sstevel * Disable SBBC interrupts if SBBC is mapped in
302403831d35Sstevel */
302503831d35Sstevel if (softp->sbbc_region) {
302603831d35Sstevel ddi_put32(softp->sbbc_handle,
302703831d35Sstevel &(softp->sbbc_region->int_enable.reg), 0);
302803831d35Sstevel }
302903831d35Sstevel
303003831d35Sstevel /*
303103831d35Sstevel * Remove SBBC interrupt handler
303203831d35Sstevel */
303303831d35Sstevel ddi_remove_intr(softp->dip, 0, softp->real_iblk);
303403831d35Sstevel
303503831d35Sstevel /*
303603831d35Sstevel * Remove soft interrupt handler
303703831d35Sstevel */
303803831d35Sstevel mutex_enter(&iosram_mutex);
303903831d35Sstevel if (softp->softintr_id != NULL) {
304003831d35Sstevel ddi_remove_softintr(softp->softintr_id);
304103831d35Sstevel softp->softintr_id = NULL;
304203831d35Sstevel }
304303831d35Sstevel mutex_exit(&iosram_mutex);
304403831d35Sstevel
304503831d35Sstevel return (0);
304603831d35Sstevel }
304703831d35Sstevel
304803831d35Sstevel
304903831d35Sstevel /*
305003831d35Sstevel * iosram_add_instance(iosramsoft_t *)
305103831d35Sstevel * Must be called while holding iosram_mutex
305203831d35Sstevel */
305303831d35Sstevel static void
iosram_add_instance(iosramsoft_t * new_softp)305403831d35Sstevel iosram_add_instance(iosramsoft_t *new_softp)
305503831d35Sstevel {
305603831d35Sstevel #ifdef DEBUG
305703831d35Sstevel int instance = new_softp->instance;
305803831d35Sstevel iosramsoft_t *softp;
305903831d35Sstevel #endif
306003831d35Sstevel
306103831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
306203831d35Sstevel
306303831d35Sstevel #if defined(DEBUG)
306403831d35Sstevel /* Verify that this instance is not in the list */
306503831d35Sstevel for (softp = iosram_instances; softp != NULL; softp = softp->next) {
306603831d35Sstevel ASSERT(softp->instance != instance);
306703831d35Sstevel }
306803831d35Sstevel #endif
306903831d35Sstevel
307003831d35Sstevel /*
307103831d35Sstevel * Add this instance to the list
307203831d35Sstevel */
307303831d35Sstevel if (iosram_instances != NULL) {
307403831d35Sstevel iosram_instances->prev = new_softp;
307503831d35Sstevel }
307603831d35Sstevel new_softp->next = iosram_instances;
307703831d35Sstevel new_softp->prev = NULL;
307803831d35Sstevel iosram_instances = new_softp;
307903831d35Sstevel }
308003831d35Sstevel
308103831d35Sstevel
308203831d35Sstevel /*
308303831d35Sstevel * iosram_remove_instance(int instance)
308403831d35Sstevel * Must be called while holding iosram_mutex
308503831d35Sstevel */
308603831d35Sstevel static void
iosram_remove_instance(int instance)308703831d35Sstevel iosram_remove_instance(int instance)
308803831d35Sstevel {
308903831d35Sstevel iosramsoft_t *softp;
309003831d35Sstevel
309103831d35Sstevel /*
309203831d35Sstevel * Remove specified instance from the iosram_instances list so that
309303831d35Sstevel * it can't be chosen for tunnel in future.
309403831d35Sstevel */
309503831d35Sstevel ASSERT(mutex_owned(&iosram_mutex));
309603831d35Sstevel
309703831d35Sstevel for (softp = iosram_instances; softp != NULL; softp = softp->next) {
309803831d35Sstevel if (softp->instance == instance) {
309903831d35Sstevel if (softp->next != NULL) {
310003831d35Sstevel softp->next->prev = softp->prev;
310103831d35Sstevel }
310203831d35Sstevel if (softp->prev != NULL) {
310303831d35Sstevel softp->prev->next = softp->next;
310403831d35Sstevel }
310503831d35Sstevel if (iosram_instances == softp) {
310603831d35Sstevel iosram_instances = softp->next;
310703831d35Sstevel }
310803831d35Sstevel
310903831d35Sstevel return;
311003831d35Sstevel }
311103831d35Sstevel }
311203831d35Sstevel }
311303831d35Sstevel
311403831d35Sstevel
311503831d35Sstevel /*
311603831d35Sstevel * iosram_sema_acquire: Acquire hardware semaphore.
311703831d35Sstevel * Return 0 if the semaphore could be acquired, or one of the following
311803831d35Sstevel * possible values:
311903831d35Sstevel * EAGAIN: there is a tunnel switch in progress
312003831d35Sstevel * EBUSY: the semaphore was already "held"
312103831d35Sstevel * ENXIO: an IO error occured (e.g. SBBC not mapped)
312203831d35Sstevel * If old_value is not NULL, the location it points to will be updated
312303831d35Sstevel * with the semaphore value read when attempting to acquire it.
312403831d35Sstevel */
312503831d35Sstevel int
iosram_sema_acquire(uint32_t * old_value)312603831d35Sstevel iosram_sema_acquire(uint32_t *old_value)
312703831d35Sstevel {
312803831d35Sstevel struct iosramsoft *softp;
312903831d35Sstevel int rv;
313003831d35Sstevel uint32_t sema_val;
313103831d35Sstevel
313203831d35Sstevel DPRINTF(2, ("IOSRAM: in iosram_sema_acquire\n"));
313303831d35Sstevel
313403831d35Sstevel mutex_enter(&iosram_mutex);
313503831d35Sstevel
313603831d35Sstevel /*
313703831d35Sstevel * Disallow access if there is a tunnel switch in progress.
313803831d35Sstevel */
313903831d35Sstevel if (iosram_tswitch_active) {
314003831d35Sstevel mutex_exit(&iosram_mutex);
314103831d35Sstevel return (EAGAIN);
314203831d35Sstevel }
314303831d35Sstevel
314403831d35Sstevel /*
314503831d35Sstevel * Use current master IOSRAM for operation, fail if none is
314603831d35Sstevel * currently active.
314703831d35Sstevel */
314803831d35Sstevel if ((softp = iosram_master) == NULL) {
314903831d35Sstevel mutex_exit(&iosram_mutex);
315003831d35Sstevel DPRINTF(1, ("IOSRAM: iosram_sema_acquire: no master\n"));
315103831d35Sstevel return (ENXIO);
315203831d35Sstevel }
315303831d35Sstevel
315403831d35Sstevel mutex_enter(&softp->intr_mutex);
315503831d35Sstevel
315603831d35Sstevel /*
315703831d35Sstevel * Fail if SBBC region has not been mapped. This shouldn't
315803831d35Sstevel * happen if we have a master IOSRAM, but we double-check.
315903831d35Sstevel */
316003831d35Sstevel if (softp->sbbc_region == NULL) {
316103831d35Sstevel mutex_exit(&softp->intr_mutex);
316203831d35Sstevel mutex_exit(&iosram_mutex);
316303831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: "
316403831d35Sstevel "SBBC not mapped\n", softp->instance));
316503831d35Sstevel return (ENXIO);
316603831d35Sstevel }
316703831d35Sstevel
316803831d35Sstevel /* read semaphore value */
316903831d35Sstevel sema_val = IOSRAM_SEMA_RD(softp);
317003831d35Sstevel if (old_value != NULL)
317103831d35Sstevel *old_value = sema_val;
317203831d35Sstevel
317303831d35Sstevel if (IOSRAM_SEMA_IS_HELD(sema_val)) {
317403831d35Sstevel /* semaphore was held by someone else */
317503831d35Sstevel rv = EBUSY;
317603831d35Sstevel } else {
317703831d35Sstevel /* semaphore was not held, we just acquired it */
317803831d35Sstevel rv = 0;
317903831d35Sstevel }
318003831d35Sstevel
318103831d35Sstevel mutex_exit(&softp->intr_mutex);
318203831d35Sstevel mutex_exit(&iosram_mutex);
318303831d35Sstevel
318403831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: "
318503831d35Sstevel "old value=0x%x rv=%d\n", softp->instance, sema_val, rv));
318603831d35Sstevel
318703831d35Sstevel return (rv);
318803831d35Sstevel }
318903831d35Sstevel
319003831d35Sstevel
319103831d35Sstevel /*
319203831d35Sstevel * iosram_sema_release: Release hardware semaphore.
319303831d35Sstevel * This function will "release" the hardware semaphore, and return 0 on
319403831d35Sstevel * success. If an error occured, one of the following values will be
319503831d35Sstevel * returned:
319603831d35Sstevel * EAGAIN: there is a tunnel switch in progress
319703831d35Sstevel * ENXIO: an IO error occured (e.g. SBBC not mapped)
319803831d35Sstevel */
319903831d35Sstevel int
iosram_sema_release(void)320003831d35Sstevel iosram_sema_release(void)
320103831d35Sstevel {
320203831d35Sstevel struct iosramsoft *softp;
320303831d35Sstevel
320403831d35Sstevel DPRINTF(2, ("IOSRAM: in iosram_sema_release\n"));
320503831d35Sstevel
320603831d35Sstevel mutex_enter(&iosram_mutex);
320703831d35Sstevel
320803831d35Sstevel /*
320903831d35Sstevel * Disallow access if there is a tunnel switch in progress.
321003831d35Sstevel */
321103831d35Sstevel if (iosram_tswitch_active) {
321203831d35Sstevel mutex_exit(&iosram_mutex);
321303831d35Sstevel return (EAGAIN);
321403831d35Sstevel }
321503831d35Sstevel
321603831d35Sstevel /*
321703831d35Sstevel * Use current master IOSRAM for operation, fail if none is
321803831d35Sstevel * currently active.
321903831d35Sstevel */
322003831d35Sstevel if ((softp = iosram_master) == NULL) {
322103831d35Sstevel mutex_exit(&iosram_mutex);
322203831d35Sstevel DPRINTF(1, ("IOSRAM: iosram_sema_release: no master\n"));
322303831d35Sstevel return (ENXIO);
322403831d35Sstevel }
322503831d35Sstevel
322603831d35Sstevel mutex_enter(&softp->intr_mutex);
322703831d35Sstevel
322803831d35Sstevel /*
322903831d35Sstevel * Fail if SBBC region has not been mapped in. This shouldn't
323003831d35Sstevel * happen if we have a master IOSRAM, but we double-check.
323103831d35Sstevel */
323203831d35Sstevel if (softp->sbbc_region == NULL) {
323303831d35Sstevel mutex_exit(&softp->intr_mutex);
323403831d35Sstevel mutex_exit(&iosram_mutex);
323503831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: "
323603831d35Sstevel "SBBC not mapped\n", softp->instance));
323703831d35Sstevel return (ENXIO);
323803831d35Sstevel }
323903831d35Sstevel
324003831d35Sstevel /* Release semaphore by clearing our semaphore register */
324103831d35Sstevel IOSRAM_SEMA_WR(softp, 0);
324203831d35Sstevel
324303831d35Sstevel mutex_exit(&softp->intr_mutex);
324403831d35Sstevel mutex_exit(&iosram_mutex);
324503831d35Sstevel
324603831d35Sstevel DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: success\n",
324703831d35Sstevel softp->instance));
324803831d35Sstevel
324903831d35Sstevel return (0);
325003831d35Sstevel }
325103831d35Sstevel
325203831d35Sstevel
325303831d35Sstevel #if defined(IOSRAM_LOG)
325403831d35Sstevel void
iosram_log(caddr_t fmt,intptr_t a1,intptr_t a2,intptr_t a3,intptr_t a4)325503831d35Sstevel iosram_log(caddr_t fmt, intptr_t a1, intptr_t a2, intptr_t a3, intptr_t a4)
325603831d35Sstevel {
325703831d35Sstevel uint32_t seq;
325803831d35Sstevel iosram_log_t *logp;
325903831d35Sstevel
326003831d35Sstevel mutex_enter(&iosram_log_mutex);
326103831d35Sstevel
326203831d35Sstevel seq = iosram_logseq++;
326303831d35Sstevel logp = &iosram_logbuf[seq % IOSRAM_MAXLOG];
326403831d35Sstevel logp->seq = seq;
3265d3d50737SRafael Vanoni logp->tstamp = ddi_get_lbolt();
326603831d35Sstevel logp->fmt = fmt;
326703831d35Sstevel logp->arg1 = a1;
326803831d35Sstevel logp->arg2 = a2;
326903831d35Sstevel logp->arg3 = a3;
327003831d35Sstevel logp->arg4 = a4;
327103831d35Sstevel
327203831d35Sstevel mutex_exit(&iosram_log_mutex);
327303831d35Sstevel
327403831d35Sstevel if (iosram_log_print) {
327503831d35Sstevel cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp);
327603831d35Sstevel if (logp->fmt) {
327703831d35Sstevel cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2,
327803831d35Sstevel logp->arg3, logp->arg4);
327903831d35Sstevel if (logp->fmt[strlen(logp->fmt)-1] != '\n') {
328003831d35Sstevel cmn_err(CE_CONT, "\n");
328103831d35Sstevel }
328203831d35Sstevel } else {
328303831d35Sstevel cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n",
3284*07d06da5SSurya Prakki (void *)logp->fmt, logp->arg1, logp->arg2,
3285*07d06da5SSurya Prakki logp->arg3, logp->arg4);
328603831d35Sstevel }
328703831d35Sstevel }
328803831d35Sstevel }
328903831d35Sstevel #endif /* IOSRAM_LOG */
329003831d35Sstevel
329103831d35Sstevel
329203831d35Sstevel #if defined(DEBUG)
329303831d35Sstevel /*
329403831d35Sstevel * iosram_get_keys(buf, len)
329503831d35Sstevel * Return IOSRAM TOC in the specified buffer
329603831d35Sstevel */
329703831d35Sstevel static int
iosram_get_keys(iosram_toc_entry_t * bufp,uint32_t * len)329803831d35Sstevel iosram_get_keys(iosram_toc_entry_t *bufp, uint32_t *len)
329903831d35Sstevel {
330003831d35Sstevel struct iosram_chunk *chunkp;
330103831d35Sstevel int error = 0;
330203831d35Sstevel int i;
330303831d35Sstevel int cnt = (*len) / sizeof (iosram_toc_entry_t);
330403831d35Sstevel
330503831d35Sstevel IOSRAMLOG(2, "iosram_get_keys(bufp:%p *len:%x)\n", bufp, *len, NULL,
330603831d35Sstevel NULL);
330703831d35Sstevel
330803831d35Sstevel /*
330903831d35Sstevel * Copy data while holding the lock to prevent any data
331003831d35Sstevel * corruption or invalid pointer dereferencing.
331103831d35Sstevel */
331203831d35Sstevel mutex_enter(&iosram_mutex);
331303831d35Sstevel
331403831d35Sstevel if (iosram_master == NULL) {
331503831d35Sstevel error = EIO;
331603831d35Sstevel } else {
331703831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks && i < cnt;
331803831d35Sstevel i++, chunkp++) {
331903831d35Sstevel bufp[i].key = chunkp->toc_data.key;
332003831d35Sstevel bufp[i].off = chunkp->toc_data.off;
332103831d35Sstevel bufp[i].len = chunkp->toc_data.len;
332203831d35Sstevel bufp[i].unused = chunkp->toc_data.unused;
332303831d35Sstevel }
332403831d35Sstevel *len = i * sizeof (iosram_toc_entry_t);
332503831d35Sstevel }
332603831d35Sstevel
332703831d35Sstevel mutex_exit(&iosram_mutex);
332803831d35Sstevel return (error);
332903831d35Sstevel }
333003831d35Sstevel
333103831d35Sstevel
333203831d35Sstevel /*
333303831d35Sstevel * iosram_print_state(instance)
333403831d35Sstevel */
333503831d35Sstevel static void
iosram_print_state(int instance)333603831d35Sstevel iosram_print_state(int instance)
333703831d35Sstevel {
333803831d35Sstevel struct iosramsoft *softp;
333903831d35Sstevel char pn[MAXNAMELEN];
334003831d35Sstevel
334103831d35Sstevel if (instance < 0) {
334203831d35Sstevel softp = iosram_master;
334303831d35Sstevel } else {
334403831d35Sstevel softp = ddi_get_soft_state(iosramsoft_statep, instance);
334503831d35Sstevel }
334603831d35Sstevel
334703831d35Sstevel if (softp == NULL) {
334803831d35Sstevel cmn_err(CE_CONT, "iosram_print_state: Can't find instance %d\n",
334903831d35Sstevel instance);
335003831d35Sstevel return;
335103831d35Sstevel }
335203831d35Sstevel instance = softp->instance;
335303831d35Sstevel
335403831d35Sstevel mutex_enter(&iosram_mutex);
335503831d35Sstevel mutex_enter(&softp->intr_mutex);
335603831d35Sstevel
335703831d35Sstevel cmn_err(CE_CONT, "iosram_print_state(%d): ... %s\n", instance,
335803831d35Sstevel ((softp == iosram_master) ? "MASTER" : "SLAVE"));
335903831d35Sstevel
336003831d35Sstevel (void) ddi_pathname(softp->dip, pn);
336103831d35Sstevel cmn_err(CE_CONT, " pathname:%s\n", pn);
336203831d35Sstevel cmn_err(CE_CONT, " instance:%d portid:%d iosramlen:0x%x\n",
336303831d35Sstevel softp->instance, softp->portid, softp->iosramlen);
3364*07d06da5SSurya Prakki cmn_err(CE_CONT, " softp:%p handle:%p iosramp:%p\n", (void *)softp,
3365*07d06da5SSurya Prakki (void *)softp->handle, (void *)softp->iosramp);
336603831d35Sstevel cmn_err(CE_CONT, " state:0x%x tswitch_ok:%x tswitch_fail:%x\n",
336703831d35Sstevel softp->state, softp->tswitch_ok, softp->tswitch_fail);
336803831d35Sstevel cmn_err(CE_CONT, " softintr_id:%p intr_busy:%x intr_pending:%x\n",
3369*07d06da5SSurya Prakki (void *)softp->softintr_id, softp->intr_busy, softp->intr_pending);
337003831d35Sstevel
337103831d35Sstevel mutex_exit(&softp->intr_mutex);
337203831d35Sstevel mutex_exit(&iosram_mutex);
337303831d35Sstevel }
337403831d35Sstevel
337503831d35Sstevel
337603831d35Sstevel /*
337703831d35Sstevel * iosram_print_stats()
337803831d35Sstevel */
337903831d35Sstevel static void
iosram_print_stats()338003831d35Sstevel iosram_print_stats()
338103831d35Sstevel {
338203831d35Sstevel uint32_t calls;
338303831d35Sstevel
338403831d35Sstevel cmn_err(CE_CONT, "iosram_stats:\n");
338503831d35Sstevel calls = iosram_stats.read;
338603831d35Sstevel cmn_err(CE_CONT, " read ... calls:%x bytes:%lx avg_sz:%x\n",
338703831d35Sstevel calls, iosram_stats.bread,
338803831d35Sstevel (uint32_t)((calls != 0) ? (iosram_stats.bread/calls) : 0));
338903831d35Sstevel
339003831d35Sstevel calls = iosram_stats.write;
339103831d35Sstevel cmn_err(CE_CONT, " write ... calls:%x bytes:%lx avg_sz:%x\n",
339203831d35Sstevel calls, iosram_stats.bwrite,
339303831d35Sstevel (uint32_t)((calls != 0) ? (iosram_stats.bwrite/calls) : 0));
339403831d35Sstevel
339503831d35Sstevel cmn_err(CE_CONT, " intr recv (real:%x soft:%x) sent:%x cback:%x\n",
339603831d35Sstevel iosram_stats.intr_recv, iosram_stats.sintr_recv,
339703831d35Sstevel iosram_stats.intr_send, iosram_stats.callbacks);
339803831d35Sstevel
339903831d35Sstevel cmn_err(CE_CONT, " tswitch: %x getflag:%x setflag:%x\n",
340003831d35Sstevel iosram_stats.tswitch, iosram_stats.getflag,
340103831d35Sstevel iosram_stats.setflag);
340203831d35Sstevel
340303831d35Sstevel cmn_err(CE_CONT, " iosram_rw_active_max: %x\n", iosram_rw_active_max);
340403831d35Sstevel }
340503831d35Sstevel
340603831d35Sstevel
340703831d35Sstevel static void
iosram_print_cback()340803831d35Sstevel iosram_print_cback()
340903831d35Sstevel {
341003831d35Sstevel iosram_chunk_t *chunkp;
341103831d35Sstevel int i;
341203831d35Sstevel
341303831d35Sstevel /*
341403831d35Sstevel * Print callback handlers
341503831d35Sstevel */
341603831d35Sstevel mutex_enter(&iosram_mutex);
341703831d35Sstevel
341803831d35Sstevel cmn_err(CE_CONT, "IOSRAM callbacks:\n");
341903831d35Sstevel for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
342003831d35Sstevel if (chunkp->cback.handler) {
342103831d35Sstevel cmn_err(CE_CONT, " %2d: key:0x%x hdlr:%p arg:%p "
342203831d35Sstevel "busy:%d unreg:%d\n", i, chunkp->toc_data.key,
3423*07d06da5SSurya Prakki (void *)chunkp->cback.handler,
3424*07d06da5SSurya Prakki (void *)chunkp->cback.arg,
342503831d35Sstevel chunkp->cback.busy, chunkp->cback.unregister);
342603831d35Sstevel }
342703831d35Sstevel }
342803831d35Sstevel mutex_exit(&iosram_mutex);
342903831d35Sstevel }
343003831d35Sstevel
343103831d35Sstevel
343203831d35Sstevel static void
iosram_print_flags()343303831d35Sstevel iosram_print_flags()
343403831d35Sstevel {
343503831d35Sstevel int i;
343603831d35Sstevel uint32_t *keys;
343703831d35Sstevel iosram_flags_t *flags;
343803831d35Sstevel
343903831d35Sstevel mutex_enter(&iosram_mutex);
344003831d35Sstevel
344103831d35Sstevel if (iosram_master == NULL) {
344203831d35Sstevel mutex_exit(&iosram_mutex);
344303831d35Sstevel cmn_err(CE_CONT, "IOSRAM Flags: not accessible\n");
344403831d35Sstevel return;
344503831d35Sstevel }
344603831d35Sstevel
344703831d35Sstevel keys = kmem_alloc(nchunks * sizeof (uint32_t), KM_SLEEP);
344803831d35Sstevel flags = kmem_alloc(nchunks * sizeof (iosram_flags_t), KM_SLEEP);
344903831d35Sstevel
345003831d35Sstevel for (i = 0; i < nchunks; i++) {
345103831d35Sstevel keys[i] = chunks[i].toc_data.key;
345203831d35Sstevel ddi_rep_get8(iosram_handle, (uint8_t *)&(flags[i]),
345303831d35Sstevel (uint8_t *)(chunks[i].flagsp), sizeof (iosram_flags_t),
345403831d35Sstevel DDI_DEV_AUTOINCR);
345503831d35Sstevel }
345603831d35Sstevel
345703831d35Sstevel mutex_exit(&iosram_mutex);
345803831d35Sstevel
345903831d35Sstevel cmn_err(CE_CONT, "IOSRAM Flags:\n");
346003831d35Sstevel for (i = 0; i < nchunks; i++) {
346103831d35Sstevel cmn_err(CE_CONT,
346203831d35Sstevel " %2d: key: 0x%x data_valid:%x int_pending:%x\n",
346303831d35Sstevel i, keys[i], flags[i].data_valid, flags[i].int_pending);
346403831d35Sstevel }
346503831d35Sstevel
346603831d35Sstevel kmem_free(keys, nchunks * sizeof (uint32_t));
346703831d35Sstevel kmem_free(flags, nchunks * sizeof (iosram_flags_t));
346803831d35Sstevel }
346903831d35Sstevel
347003831d35Sstevel
347103831d35Sstevel /*PRINTFLIKE1*/
347203831d35Sstevel static void
iosram_dprintf(const char * fmt,...)347303831d35Sstevel iosram_dprintf(const char *fmt, ...)
347403831d35Sstevel {
347503831d35Sstevel char msg_buf[256];
347603831d35Sstevel va_list adx;
347703831d35Sstevel
347803831d35Sstevel va_start(adx, fmt);
3479*07d06da5SSurya Prakki (void) vsprintf(msg_buf, fmt, adx);
348003831d35Sstevel va_end(adx);
348103831d35Sstevel
348203831d35Sstevel cmn_err(CE_CONT, "%s", msg_buf);
348303831d35Sstevel }
348403831d35Sstevel #endif /* DEBUG */
348503831d35Sstevel
348603831d35Sstevel
348703831d35Sstevel #if IOSRAM_LOG
348803831d35Sstevel /*
348903831d35Sstevel * iosram_print_log(int cnt)
349003831d35Sstevel * Print last few entries of the IOSRAM log in reverse order
349103831d35Sstevel */
349203831d35Sstevel static void
iosram_print_log(int cnt)349303831d35Sstevel iosram_print_log(int cnt)
349403831d35Sstevel {
349503831d35Sstevel int i;
349603831d35Sstevel
349703831d35Sstevel if (cnt <= 0) {
349803831d35Sstevel cnt = 20;
349903831d35Sstevel } else if (cnt > IOSRAM_MAXLOG) {
350003831d35Sstevel cnt = IOSRAM_MAXLOG;
350103831d35Sstevel }
350203831d35Sstevel
350303831d35Sstevel
350403831d35Sstevel cmn_err(CE_CONT,
350503831d35Sstevel "\niosram_logseq: 0x%x lbolt: %lx iosram_log_level:%x\n",
3506d3d50737SRafael Vanoni iosram_logseq, ddi_get_lbolt(), iosram_log_level);
350703831d35Sstevel cmn_err(CE_CONT, "iosram_logbuf: %p max entries:0x%x\n",
3508*07d06da5SSurya Prakki (void *)iosram_logbuf, IOSRAM_MAXLOG);
350903831d35Sstevel for (i = iosram_logseq; --i >= 0 && --cnt >= 0; ) {
351003831d35Sstevel iosram_log_t *logp;
351103831d35Sstevel
351203831d35Sstevel mutex_enter(&iosram_log_mutex);
351303831d35Sstevel
351403831d35Sstevel logp = &iosram_logbuf[i %IOSRAM_MAXLOG];
351503831d35Sstevel cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp);
351603831d35Sstevel
351703831d35Sstevel if (logp->fmt) {
351803831d35Sstevel cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2,
351903831d35Sstevel logp->arg3, logp->arg4);
352003831d35Sstevel if (logp->fmt[strlen(logp->fmt)-1] != '\n') {
352103831d35Sstevel cmn_err(CE_CONT, "\n");
352203831d35Sstevel }
352303831d35Sstevel } else {
352403831d35Sstevel cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n",
3525*07d06da5SSurya Prakki (void *)logp->fmt, logp->arg1, logp->arg2,
352603831d35Sstevel logp->arg3, logp->arg4);
352703831d35Sstevel }
352803831d35Sstevel
352903831d35Sstevel mutex_exit(&iosram_log_mutex);
353003831d35Sstevel }
353103831d35Sstevel }
353203831d35Sstevel #endif /* IOSRAM_LOG */
3533