iosram.c revision 7656:2621e50fdf4a
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27
28/*
29 * IOSRAM leaf driver to SBBC nexus driver.  This driver is used
30 * by Starcat Domain SW to read/write from/to the IO sram.
31 */
32
33#include <sys/types.h>
34#include <sys/conf.h>
35#include <sys/ddi.h>
36#include <sys/sunddi.h>
37#include <sys/ddi_impldefs.h>
38#include <sys/obpdefs.h>
39#include <sys/promif.h>
40#include <sys/prom_plat.h>
41#include <sys/cmn_err.h>
42#include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
43#include <sys/modctl.h>		/* for modldrv */
44#include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
45#include <sys/errno.h>
46#include <sys/kmem.h>
47#include <sys/kstat.h>
48#include <sys/debug.h>
49
50#include <sys/axq.h>
51#include <sys/iosramreg.h>
52#include <sys/iosramio.h>
53#include <sys/iosramvar.h>
54
55
56#if defined(DEBUG)
57int	iosram_debug = 0;
58static void iosram_dprintf(const char *fmt, ...);
59#define	DPRINTF(level, arg)	\
60		{ if (iosram_debug >= level) iosram_dprintf arg; }
61#else	/* !DEBUG */
62#define	DPRINTF(level, arg)
63#endif	/* !DEBUG */
64
65
66/*
67 * IOSRAM module global state
68 */
69static void	*iosramsoft_statep;	/* IOSRAM state pointer */
70static kmutex_t	iosram_mutex;		/* mutex lock */
71
72static iosram_chunk_t	*chunks = NULL;	/* array of TOC entries */
73static int	nchunks = 0;		/* # of TOC entries */
74static iosram_chunk_t	*iosram_hashtab[IOSRAM_HASHSZ];	/* key hash table */
75
76static kcondvar_t	iosram_tswitch_wait;	/* tunnel switch wait cv */
77static int	iosram_tswitch_wakeup = 0;	/* flag indicationg one or */
78						/* more threads waiting on */
79						/* iosram_tswitch_wait cv */
80static int	iosram_tswitch_active = 0;	/* tunnel switch active flag */
81static int	iosram_tswitch_aborted = 0;	/* tunnel switch abort flag */
82static clock_t	iosram_tswitch_tstamp = 0;	/* lbolt of last tswitch end */
83static kcondvar_t	iosram_rw_wait;		/* read/write wait cv */
84static int	iosram_rw_wakeup = 0;		/* flag indicationg one or */
85						/* more threads waiting on */
86						/* iosram_rw_wait cv */
87static int	iosram_rw_active = 0;		/* # threads accessing IOSRAM */
88#if defined(DEBUG)
89static int	iosram_rw_active_max = 0;
90#endif
91
92static struct iosramsoft *iosram_new_master = NULL;	/* new tunnel target */
93static struct iosramsoft *iosram_master = NULL;		/* master tunnel */
94static struct iosramsoft *iosram_instances = NULL;	/* list of softstates */
95
96static ddi_acc_handle_t	iosram_handle = NULL;	/* master IOSRAM map handle */
97
98static void	(*iosram_hdrchange_handler)() = NULL;
99
100#if IOSRAM_STATS
101static struct	iosram_stat iosram_stats;	/* IOSRAM statistics */
102static void	iosram_print_stats();		/* forward declaration */
103#endif /* IOSRAM_STATS */
104
105
106#if IOSRAM_LOG
107kmutex_t 	iosram_log_mutex;
108int		iosram_log_level = 1;
109int		iosram_log_print = 0;		/* print log when recorded */
110uint32_t	iosram_logseq;
111iosram_log_t	iosram_logbuf[IOSRAM_MAXLOG];
112static void	iosram_print_log(int cnt);	/* forward declaration */
113#endif	/* IOSRAM_LOG */
114
115
116/* driver entry point fn definitions */
117static int 	iosram_open(dev_t *, int, int, cred_t *);
118static int	iosram_close(dev_t, int, int, cred_t *);
119static int	iosram_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
120
121/* configuration entry point fn definitions */
122static int 	iosram_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
123static int	iosram_attach(dev_info_t *, ddi_attach_cmd_t);
124static int	iosram_detach(dev_info_t *, ddi_detach_cmd_t);
125
126
127/* forward declaractions */
128static iosram_chunk_t	*iosram_find_chunk(uint32_t key);
129static void	iosram_set_master(struct iosramsoft *softp);
130static int	iosram_is_chosen(struct iosramsoft *softp);
131static int	iosram_tunnel_capable(struct iosramsoft *softp);
132static int	iosram_read_toc(struct iosramsoft *softp);
133static void	iosram_init_hashtab(void);
134static void	iosram_update_addrs(struct iosramsoft *softp);
135
136static int	iosram_setup_map(struct iosramsoft *softp);
137static void	iosram_remove_map(struct iosramsoft *softp);
138static int	iosram_add_intr(iosramsoft_t *);
139static int	iosram_remove_intr(iosramsoft_t *);
140
141static void	iosram_add_instance(struct iosramsoft *softp);
142static void	iosram_remove_instance(int instance);
143static int	iosram_switch_tunnel(iosramsoft_t *softp);
144static void	iosram_abort_tswitch();
145
146#if defined(DEBUG)
147/* forward declaractions for debugging */
148static int	iosram_get_keys(iosram_toc_entry_t *buf, uint32_t *len);
149static void	iosram_print_cback();
150static void	iosram_print_state(int);
151static void	iosram_print_flags();
152#endif
153
154
155
156/*
157 * cb_ops
158 */
159static struct cb_ops iosram_cb_ops = {
160	iosram_open,		/* cb_open */
161	iosram_close,		/* cb_close */
162	nodev,			/* cb_strategy */
163	nodev,			/* cb_print */
164	nodev,			/* cb_dump */
165	nodev,			/* cb_read */
166	nodev,			/* cb_write */
167	iosram_ioctl,		/* cb_ioctl */
168	nodev,			/* cb_devmap */
169	nodev,			/* cb_mmap */
170	nodev,			/* cb_segmap */
171	nochpoll,		/* cb_chpoll */
172	ddi_prop_op,		/* cb_prop_op */
173	NULL,			/* cb_stream */
174	(int)(D_NEW | D_MP | D_HOTPLUG)	/* cb_flag */
175};
176
177/*
178 * Declare ops vectors for auto configuration.
179 */
180struct dev_ops  iosram_ops = {
181	DEVO_REV,		/* devo_rev */
182	0,			/* devo_refcnt */
183	iosram_getinfo,		/* devo_getinfo */
184	nulldev,		/* devo_identify */
185	nulldev,		/* devo_probe */
186	iosram_attach,		/* devo_attach */
187	iosram_detach,		/* devo_detach */
188	nodev,			/* devo_reset */
189	&iosram_cb_ops,		/* devo_cb_ops */
190	(struct bus_ops *)NULL,	/* devo_bus_ops */
191	nulldev,		/* devo_power */
192	ddi_quiesce_not_supported,	/* devo_quiesce */
193};
194
195/*
196 * Loadable module support.
197 */
198extern struct mod_ops mod_driverops;
199
200static struct modldrv iosrammodldrv = {
201	&mod_driverops,		/* type of module - driver */
202	"IOSRAM Leaf driver",
203	&iosram_ops,
204};
205
206static struct modlinkage iosrammodlinkage = {
207	MODREV_1,
208	&iosrammodldrv,
209	NULL
210};
211
212
213int
214_init(void)
215{
216	int    error;
217	int	i;
218
219	mutex_init(&iosram_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
220	cv_init(&iosram_tswitch_wait, NULL, CV_DRIVER, NULL);
221	cv_init(&iosram_rw_wait, NULL, CV_DRIVER, NULL);
222#if defined(IOSRAM_LOG)
223	mutex_init(&iosram_log_mutex, NULL, MUTEX_DRIVER, (void *)NULL);
224#endif
225
226	DPRINTF(1, ("_init:IOSRAM\n"));
227
228	for (i = 0; i < IOSRAM_HASHSZ; i++) {
229		iosram_hashtab[i] = NULL;
230	}
231
232	if ((error = ddi_soft_state_init(&iosramsoft_statep,
233	    sizeof (struct iosramsoft), 1)) != 0) {
234		goto failed;
235	}
236	if ((error = mod_install(&iosrammodlinkage)) != 0) {
237		ddi_soft_state_fini(&iosramsoft_statep);
238		goto failed;
239	}
240
241	IOSRAMLOG(0, "_init:IOSRAM ... error:%d  statep:%p\n",
242	    error, iosramsoft_statep, NULL, NULL);
243
244	return (error);
245
246failed:
247	cv_destroy(&iosram_tswitch_wait);
248	cv_destroy(&iosram_rw_wait);
249	mutex_destroy(&iosram_mutex);
250#if defined(IOSRAM_LOG)
251	mutex_destroy(&iosram_log_mutex);
252#endif
253	IOSRAMLOG(0, "_init:IOSRAM ... error:%d  statep:%p\n",
254	    error, iosramsoft_statep, NULL, NULL);
255
256	return (error);
257}
258
259
260int
261_fini(void)
262{
263#ifndef DEBUG
264	return (EBUSY);
265#else /* !DEBUG */
266	int    error;
267
268	if ((error = mod_remove(&iosrammodlinkage)) == 0) {
269		ddi_soft_state_fini(&iosramsoft_statep);
270
271		cv_destroy(&iosram_tswitch_wait);
272		cv_destroy(&iosram_rw_wait);
273		mutex_destroy(&iosram_mutex);
274#if defined(IOSRAM_LOG)
275		mutex_destroy(&iosram_log_mutex);
276#endif
277	}
278	DPRINTF(1, ("_fini:IOSRAM  error:%d\n", error));
279
280	return (error);
281#endif /* !DEBUG */
282}
283
284
285int
286_info(struct modinfo *modinfop)
287{
288	return (mod_info(&iosrammodlinkage, modinfop));
289}
290
291
292static int
293iosram_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
294{
295	int	instance;
296	int	propval;
297	int	length;
298	char	name[32];
299	struct	iosramsoft *softp;
300
301	instance = ddi_get_instance(dip);
302
303	DPRINTF(1, ("iosram(%d): attach dip:%p\n", instance));
304
305	IOSRAMLOG(1, "ATTACH: dip:%p instance %d ... start\n",
306	    dip, instance, NULL, NULL);
307	switch (cmd) {
308	case DDI_ATTACH:
309		break;
310	case DDI_RESUME:
311		if (!(softp = ddi_get_soft_state(iosramsoft_statep,
312		    instance))) {
313			return (DDI_FAILURE);
314		}
315		mutex_enter(&iosram_mutex);
316		mutex_enter(&softp->intr_mutex);
317		if (!softp->suspended) {
318			mutex_exit(&softp->intr_mutex);
319			mutex_exit(&iosram_mutex);
320			return (DDI_FAILURE);
321		}
322		softp->suspended = 0;
323
324		/*
325		 * enable SBBC interrupts if SBBC is mapped in
326		 * restore the value saved during detach
327		 */
328		if (softp->sbbc_region) {
329			ddi_put32(softp->sbbc_handle,
330			    &(softp->sbbc_region->int_enable.reg),
331			    softp->int_enable_sav);
332		}
333
334		/*
335		 * Trigger soft interrupt handler to process any pending
336		 * interrupts.
337		 */
338		if (softp->intr_pending && !softp->intr_busy &&
339		    (softp->softintr_id != NULL)) {
340			ddi_trigger_softintr(softp->softintr_id);
341		}
342
343		mutex_exit(&softp->intr_mutex);
344		mutex_exit(&iosram_mutex);
345
346		return (DDI_SUCCESS);
347
348	default:
349		return (DDI_FAILURE);
350	}
351
352	if (ddi_soft_state_zalloc(iosramsoft_statep, instance) != 0) {
353		return (DDI_FAILURE);
354	}
355
356	if ((softp = ddi_get_soft_state(iosramsoft_statep, instance)) == NULL) {
357			return (DDI_FAILURE);
358	}
359	softp->dip = dip;
360	softp->instance = instance;
361	softp->sbbc_region = NULL;
362
363	/*
364	 * If this instance is not tunnel capable, we don't attach it.
365	 */
366	if (iosram_tunnel_capable(softp) == 0) {
367		DPRINTF(1, ("iosram(%d): not tunnel_capable\n", instance));
368		IOSRAMLOG(1, "ATTACH(%d): not tunnel_capable\n", instance, NULL,
369		    NULL, NULL);
370		goto attach_fail;
371	}
372
373	/*
374	 * Need to create an "interrupt-priorities" property to define the PIL
375	 * to be used with the interrupt service routine.
376	 */
377	if (ddi_getproplen(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
378	    "interrupt-priorities", &length) == DDI_PROP_NOT_FOUND) {
379		DPRINTF(1, ("iosram(%d): creating interrupt priority property",
380		    instance));
381		propval = IOSRAM_PIL;
382		if (ddi_prop_create(DDI_DEV_T_NONE, dip, 0,
383		    "interrupt-priorities", (caddr_t)&propval, sizeof (propval))
384		    != DDI_PROP_SUCCESS) {
385			cmn_err(CE_WARN,
386			    "iosram_attach: failed to create property");
387			goto attach_fail;
388		}
389	}
390
391	/*
392	 * Get interrupts cookies and initialize per-instance mutexes
393	 */
394	if (ddi_get_iblock_cookie(softp->dip, 0, &softp->real_iblk)
395	    != DDI_SUCCESS) {
396		IOSRAMLOG(1, "ATTACH(%d): cannot get soft intr cookie\n",
397		    instance, NULL, NULL, NULL);
398		goto attach_fail;
399	}
400	mutex_init(&softp->intr_mutex, NULL, MUTEX_DRIVER,
401	    (void *)softp->real_iblk);
402
403	/*
404	 * Add this instance to the iosram_instances list so that it can be used
405	 * for tunnel in future.
406	 */
407	mutex_enter(&iosram_mutex);
408	softp->state = IOSRAM_STATE_INIT;
409	iosram_add_instance(softp);
410
411	/*
412	 * If this is the chosen IOSRAM and there is no master IOSRAM yet, then
413	 * let's set this instance as the master.
414	 */
415	if (iosram_master == NULL && iosram_is_chosen(softp)) {
416		iosram_switch_tunnel(softp);
417
418		/*
419		 * XXX Do we need to panic if unable to setup master IOSRAM?
420		 */
421		if (iosram_master == NULL) {
422			cmn_err(CE_WARN,
423			    "iosram(%d): can't setup master tunnel\n",
424			    instance);
425			softp->state = 0;
426			iosram_remove_instance(softp->instance);
427			mutex_exit(&iosram_mutex);
428			mutex_destroy(&softp->intr_mutex);
429			goto attach_fail;
430		}
431	}
432
433	mutex_exit(&iosram_mutex);
434
435	/*
436	 * Create minor node
437	 */
438	(void) sprintf(name, "iosram%d", instance);
439	if (ddi_create_minor_node(dip, name, S_IFCHR, instance, NULL, NULL) ==
440	    DDI_FAILURE) {
441		/*
442		 * Minor node seems to be needed only for debugging purposes.
443		 * Therefore, there is no need to fail this attach request.
444		 * Simply print a message out.
445		 */
446		cmn_err(CE_NOTE, "!iosram(%d): can't create minor node\n",
447		    instance);
448	}
449	ddi_report_dev(dip);
450
451	DPRINTF(1, ("iosram_attach(%d): success.\n", instance));
452	IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... success  softp:%p\n",
453	    dip, instance, softp, NULL);
454
455	return (DDI_SUCCESS);
456
457attach_fail:
458	DPRINTF(1, ("iosram_attach(%d):failed.\n", instance));
459	IOSRAMLOG(1, "ATTACH: dip:%p instance:%d ... failed.\n",
460	    dip, instance, NULL, NULL);
461
462	ddi_soft_state_free(iosramsoft_statep, instance);
463	return (DDI_FAILURE);
464}
465
466
467static int
468iosram_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
469{
470	int			instance;
471	struct iosramsoft	*softp;
472
473	instance = ddi_get_instance(dip);
474	if (!(softp = ddi_get_soft_state(iosramsoft_statep, instance))) {
475		return (DDI_FAILURE);
476	}
477
478	IOSRAMLOG(1, "DETACH: dip:%p instance %d softp:%p\n",
479	    dip, instance, softp, NULL);
480
481	switch (cmd) {
482	case DDI_DETACH:
483		break;
484	case DDI_SUSPEND:
485		mutex_enter(&iosram_mutex);
486		mutex_enter(&softp->intr_mutex);
487		if (softp->suspended) {
488			mutex_exit(&softp->intr_mutex);
489			mutex_exit(&iosram_mutex);
490			return (DDI_FAILURE);
491		}
492		softp->suspended = 1;
493		/*
494		 * Disable SBBC interrupts if SBBC is mapped in
495		 */
496		if (softp->sbbc_region) {
497			/* save current interrupt enable register */
498			softp->int_enable_sav = ddi_get32(softp->sbbc_handle,
499			    &(softp->sbbc_region->int_enable.reg));
500			ddi_put32(softp->sbbc_handle,
501			    &(softp->sbbc_region->int_enable.reg), 0x0);
502		}
503		mutex_exit(&softp->intr_mutex);
504		mutex_exit(&iosram_mutex);
505		return (DDI_SUCCESS);
506
507	default:
508		return (DDI_FAILURE);
509	}
510
511
512	/*
513	 * Indicate that this instance is being detached so that this instance
514	 * does not become a target for tunnel switch in future.
515	 */
516	mutex_enter(&iosram_mutex);
517	softp->state |= IOSRAM_STATE_DETACH;
518
519	/*
520	 * If this instance is currently the master or the target of the tunnel
521	 * switch, then we need to wait and switch tunnel, if necessary.
522	 */
523	if (iosram_master == softp || (softp->state & IOSRAM_STATE_TSWITCH)) {
524		mutex_exit(&iosram_mutex);
525		iosram_switchfrom(instance);
526		mutex_enter(&iosram_mutex);
527	}
528
529	/*
530	 * If the tunnel switch is in progress and we are the master or target
531	 * of tunnel relocation, then we can't detach this instance right now.
532	 */
533	if (softp->state & IOSRAM_STATE_TSWITCH) {
534		softp->state &= ~IOSRAM_STATE_DETACH;
535		mutex_exit(&iosram_mutex);
536		return (DDI_FAILURE);
537	}
538
539	/*
540	 * We can't allow master IOSRAM to be detached as we won't be able to
541	 * communicate otherwise.
542	 */
543	if (iosram_master == softp) {
544		softp->state &= ~IOSRAM_STATE_DETACH;
545		mutex_exit(&iosram_mutex);
546		return (DDI_FAILURE);
547	}
548
549	/*
550	 * Now remove our instance from the iosram_instances list.
551	 */
552	iosram_remove_instance(instance);
553	mutex_exit(&iosram_mutex);
554
555	/*
556	 * Instances should only ever be mapped if they are the master and/or
557	 * participating in a tunnel switch.  Neither should be the case here.
558	 */
559	ASSERT((softp->state & IOSRAM_STATE_MAPPED) == 0);
560
561	/*
562	 * Destroy per-instance mutexes
563	 */
564	mutex_destroy(&softp->intr_mutex);
565
566	ddi_remove_minor_node(dip, NULL);
567
568	/*
569	 * Finally remove our soft state structure
570	 */
571	ddi_soft_state_free(iosramsoft_statep, instance);
572
573	return (DDI_SUCCESS);
574}
575
576
577/* ARGSUSED0 */
578static int
579iosram_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
580		void **result)
581{
582	dev_t			dev = (dev_t)arg;
583	struct iosramsoft	*softp;
584	int			instance, ret;
585
586	instance = getminor(dev);
587
588	IOSRAMLOG(2, "GETINFO: dip:%x instance %d dev:%x infocmd:%x\n",
589	    dip, instance, dev, infocmd);
590
591	switch (infocmd) {
592		case DDI_INFO_DEVT2DEVINFO:
593			softp = ddi_get_soft_state(iosramsoft_statep, instance);
594			if (softp == NULL) {
595				*result = NULL;
596				ret = DDI_FAILURE;
597			} else {
598				*result = softp->dip;
599				ret = DDI_SUCCESS;
600			}
601			break;
602		case DDI_INFO_DEVT2INSTANCE:
603			*result = (void *)(uintptr_t)instance;
604			ret = DDI_SUCCESS;
605			break;
606		default:
607			ret = DDI_FAILURE;
608			break;
609	}
610
611	return (ret);
612}
613
614
615/*ARGSUSED1*/
616static int
617iosram_open(dev_t *dev, int flag, int otype, cred_t *credp)
618{
619	struct iosramsoft	*softp;
620	int			instance;
621
622	instance = getminor(*dev);
623	softp = ddi_get_soft_state(iosramsoft_statep, instance);
624
625	if (softp == NULL) {
626		return (ENXIO);
627	}
628
629	IOSRAMLOG(1, "OPEN: dev:%p otype:%x ... instance:%d softp:%p\n",
630	    *dev, otype, softp->instance, softp);
631
632	return (0);
633}
634
635
636/*ARGSUSED1*/
637static int
638iosram_close(dev_t dev, int flag, int otype, cred_t *credp)
639{
640	struct iosramsoft	*softp;
641	int			instance;
642
643	instance = getminor(dev);
644	softp = ddi_get_soft_state(iosramsoft_statep, instance);
645	if (softp == NULL) {
646		return (ENXIO);
647	}
648
649	IOSRAMLOG(1, "CLOSE: dev:%p otype:%x ... instance:%d softp:%p\n",
650	    dev, otype, softp->instance, softp);
651
652	return (0);
653}
654
655
656int
657iosram_rd(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
658{
659	iosram_chunk_t		*chunkp;
660	uint32_t		chunk_len;
661	uint8_t			*iosramp;
662	ddi_acc_handle_t	handle;
663	int			boff;
664	union {
665		uchar_t	cbuf[UINT32SZ];
666		uint32_t  data;
667	} word;
668
669	int			error = 0;
670	uint8_t			*buf = (uint8_t *)dptr;
671
672	/*
673	 * We try to read from the IOSRAM using double word or word access
674	 * provided both "off" and "buf" are (or can be) double word or word
675	 * aligned.  Othewise, we try to align the "off" to a word boundary and
676	 * then try to read data from the IOSRAM using word access, but store it
677	 * into buf buffer using byte access.
678	 *
679	 * If the leading/trailing portion of the IOSRAM data is not word
680	 * aligned, it will always be copied using byte access.
681	 */
682	IOSRAMLOG(1, "RD: key: 0x%x off:%x len:%x buf:%p\n",
683	    key, off, len, buf);
684
685	/*
686	 * Acquire lock and look for the requested chunk.  If it exists, make
687	 * sure the requested read is within the chunk's bounds and no tunnel
688	 * switch is active.
689	 */
690	mutex_enter(&iosram_mutex);
691	chunkp = iosram_find_chunk(key);
692	chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0;
693
694	if (iosram_master == NULL) {
695		error = EIO;
696	} else if (chunkp == NULL) {
697		error = EINVAL;
698	} else if ((off >= chunk_len) || (len > chunk_len) ||
699	    ((off + len) > chunk_len)) {
700		error = EMSGSIZE;
701	} else if (iosram_tswitch_active) {
702		error = EAGAIN;
703	}
704
705	if (error) {
706		mutex_exit(&iosram_mutex);
707		return (error);
708	}
709
710	/*
711	 * Bump reference count to indicate #thread accessing IOSRAM and release
712	 * the lock.
713	 */
714	iosram_rw_active++;
715#if defined(DEBUG)
716	if (iosram_rw_active > iosram_rw_active_max) {
717		iosram_rw_active_max = iosram_rw_active;
718	}
719#endif
720	mutex_exit(&iosram_mutex);
721
722	IOSRAM_STAT(read);
723	IOSRAM_STAT_ADD(bread, len);
724
725	/* Get starting address and map handle */
726	iosramp = chunkp->basep + off;
727	handle = iosram_handle;
728
729	/*
730	 * Align the off to word boundary and then try reading/writing data
731	 * using double word or word access.
732	 */
733	if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) {
734		int	cnt = UINT32SZ - boff;
735
736		if (cnt > len) {
737			cnt = len;
738		}
739		IOSRAMLOG(2,
740		    "RD: align rep_get8(buf:%p sramp:%p cnt:%x) len:%x\n",
741		    buf, iosramp, cnt, len);
742		ddi_rep_get8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR);
743		buf += cnt;
744		iosramp += cnt;
745		len -= cnt;
746	}
747
748	if ((len >= UINT64SZ) &&
749	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) {
750		/*
751		 * Both source and destination are double word aligned
752		 */
753		int cnt = len/UINT64SZ;
754
755		IOSRAMLOG(2,
756		    "RD: rep_get64(buf:%p sramp:%p cnt:%x) len:%x\n",
757		    buf, iosramp, cnt, len);
758		ddi_rep_get64(handle, (uint64_t *)buf, (uint64_t *)iosramp,
759		    cnt, DDI_DEV_AUTOINCR);
760		iosramp += cnt * UINT64SZ;
761		buf += cnt * UINT64SZ;
762		len -= cnt * UINT64SZ;
763
764		/*
765		 * read remaining data using word and byte access
766		 */
767		if (len >= UINT32SZ) {
768			IOSRAMLOG(2,
769			    "RD: get32(buf:%p sramp:%p) len:%x\n",
770			    buf, iosramp, len, NULL);
771			*(uint32_t *)buf = ddi_get32(handle,
772			    (uint32_t *)iosramp);
773			iosramp += UINT32SZ;
774			buf += UINT32SZ;
775			len -= UINT32SZ;
776		}
777
778		if (len != 0) {
779			ddi_rep_get8(handle, buf, iosramp, len,
780			    DDI_DEV_AUTOINCR);
781		}
782	} else if ((len >= UINT32SZ) &&
783	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) {
784		/*
785		 * Both source and destination are word aligned
786		 */
787		int cnt = len/UINT32SZ;
788
789		IOSRAMLOG(2,
790		    "RD: rep_get32(buf:%p sramp:%p cnt:%x) len:%x\n",
791		    buf, iosramp, cnt, len);
792		ddi_rep_get32(handle, (uint32_t *)buf, (uint32_t *)iosramp,
793		    cnt, DDI_DEV_AUTOINCR);
794		iosramp += cnt * UINT32SZ;
795		buf += cnt * UINT32SZ;
796		len -= cnt * UINT32SZ;
797
798		/*
799		 * copy the remainder using byte access
800		 */
801		if (len != 0) {
802			ddi_rep_get8(handle, buf, iosramp, len,
803			    DDI_DEV_AUTOINCR);
804		}
805	} else if (len != 0) {
806		/*
807		 * We know that the "off" (i.e. iosramp) is at least word
808		 * aligned. We need to read IOSRAM word at a time and copy it
809		 * byte at a time.
810		 */
811		ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0);
812
813		IOSRAMLOG(2,
814		    "RD: unaligned get32(buf:%p sramp:%p) len:%x\n",
815		    buf, iosramp, len, NULL);
816		for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) {
817			word.data =  ddi_get32(handle, (uint32_t *)iosramp);
818			*buf++ = word.cbuf[0];
819			*buf++ = word.cbuf[1];
820			*buf++ = word.cbuf[2];
821			*buf++ = word.cbuf[3];
822		}
823
824		/*
825		 * copy the remaining data using byte access
826		 */
827		if (len != 0) {
828			ddi_rep_get8(handle, buf, iosramp, len,
829			    DDI_DEV_AUTOINCR);
830		}
831	}
832
833	/*
834	 * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and any
835	 * threads are waiting for r/w activity to complete, wake them up.
836	 */
837	mutex_enter(&iosram_mutex);
838	ASSERT(iosram_rw_active > 0);
839
840	if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
841		iosram_rw_wakeup = 0;
842		cv_broadcast(&iosram_rw_wait);
843	}
844	mutex_exit(&iosram_mutex);
845
846	return (error);
847}
848
849
850/*
851 * _iosram_write(key, off, len, dptr, force)
852 *	Internal common routine to write to the IOSRAM.
853 */
854static int
855_iosram_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr, int force)
856{
857	iosram_chunk_t		*chunkp;
858	uint32_t		chunk_len;
859	uint8_t			*iosramp;
860	ddi_acc_handle_t	handle;
861	int			boff;
862	union {
863		uint8_t	cbuf[UINT32SZ];
864		uint32_t data;
865	} word;
866
867	int			error = 0;
868	uint8_t			*buf = (uint8_t *)dptr;
869
870	/*
871	 * We try to write to the IOSRAM using double word or word access
872	 * provided both "off" and "buf" are (or can be) double word or word
873	 * aligned.  Othewise, we try to align the "off" to a word boundary and
874	 * then try to write data to the IOSRAM using word access, but read data
875	 * from the buf buffer using byte access.
876	 *
877	 * If the leading/trailing portion of the IOSRAM data is not word
878	 * aligned, it will always be written using byte access.
879	 */
880	IOSRAMLOG(1, "WR: key: 0x%x off:%x len:%x buf:%p\n",
881	    key, off, len, buf);
882
883	/*
884	 * Acquire lock and look for the requested chunk.  If it exists, make
885	 * sure the requested write is within the chunk's bounds and no tunnel
886	 * switch is active.
887	 */
888	mutex_enter(&iosram_mutex);
889	chunkp = iosram_find_chunk(key);
890	chunk_len = (chunkp != NULL) ? chunkp->toc_data.len : 0;
891
892	if (iosram_master == NULL) {
893		error = EIO;
894	} else if (chunkp == NULL) {
895		error = EINVAL;
896	} else if ((off >= chunk_len) || (len > chunk_len) ||
897	    ((off+len) > chunk_len)) {
898		error = EMSGSIZE;
899	} else if (iosram_tswitch_active && !force) {
900		error = EAGAIN;
901	}
902
903	if (error) {
904		mutex_exit(&iosram_mutex);
905		return (error);
906	}
907
908	/*
909	 * If this is a forced write and there's a tunnel switch in progress,
910	 * abort the switch.
911	 */
912	if (iosram_tswitch_active && force) {
913		cmn_err(CE_NOTE, "!iosram: Aborting tswitch on force_write");
914		iosram_abort_tswitch();
915	}
916
917	/*
918	 * Bump reference count to indicate #thread accessing IOSRAM
919	 * and release the lock.
920	 */
921	iosram_rw_active++;
922#if defined(DEBUG)
923	if (iosram_rw_active > iosram_rw_active_max) {
924		iosram_rw_active_max = iosram_rw_active;
925	}
926#endif
927	mutex_exit(&iosram_mutex);
928
929
930	IOSRAM_STAT(write);
931	IOSRAM_STAT_ADD(bwrite, len);
932
933	/* Get starting address and map handle */
934	iosramp = chunkp->basep + off;
935	handle = iosram_handle;
936
937	/*
938	 * Align the off to word boundary and then try reading/writing
939	 * data using double word or word access.
940	 */
941	if ((boff = ((uintptr_t)iosramp & (UINT32SZ - 1))) != 0) {
942		int	cnt = UINT32SZ - boff;
943
944		if (cnt > len) {
945			cnt = len;
946		}
947		IOSRAMLOG(2,
948		    "WR: align rep_put8(buf:%p sramp:%p cnt:%x) len:%x\n",
949		    buf, iosramp, cnt, len);
950		ddi_rep_put8(handle, buf, iosramp, cnt, DDI_DEV_AUTOINCR);
951		buf += cnt;
952		iosramp += cnt;
953		len -= cnt;
954	}
955
956	if ((len >= UINT64SZ) &&
957	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT64SZ - 1)) == 0)) {
958		/*
959		 * Both source and destination are double word aligned
960		 */
961		int cnt = len/UINT64SZ;
962
963		IOSRAMLOG(2,
964		    "WR: rep_put64(buf:%p sramp:%p cnt:%x) len:%x\n",
965		    buf, iosramp, cnt, len);
966		ddi_rep_put64(handle, (uint64_t *)buf, (uint64_t *)iosramp,
967		    cnt, DDI_DEV_AUTOINCR);
968		iosramp += cnt * UINT64SZ;
969		buf += cnt * UINT64SZ;
970		len -= cnt * UINT64SZ;
971
972		/*
973		 * Copy the remaining data using word & byte access
974		 */
975		if (len >= UINT32SZ) {
976			IOSRAMLOG(2,
977			    "WR: put32(buf:%p sramp:%p) len:%x\n", buf, iosramp,
978			    len, NULL);
979			ddi_put32(handle, (uint32_t *)iosramp,
980			    *(uint32_t *)buf);
981			iosramp += UINT32SZ;
982			buf += UINT32SZ;
983			len -= UINT32SZ;
984		}
985
986		if (len != 0) {
987			ddi_rep_put8(handle, buf, iosramp, len,
988			    DDI_DEV_AUTOINCR);
989		}
990	} else if ((len >= UINT32SZ) &&
991	    ((((uintptr_t)iosramp | (uintptr_t)buf) & (UINT32SZ - 1)) == 0)) {
992		/*
993		 * Both source and destination are word aligned
994		 */
995		int cnt = len/UINT32SZ;
996
997		IOSRAMLOG(2,
998		    "WR: rep_put32(buf:%p sramp:%p cnt:%x) len:%x\n",
999		    buf, iosramp, cnt, len);
1000		ddi_rep_put32(handle, (uint32_t *)buf, (uint32_t *)iosramp,
1001		    cnt, DDI_DEV_AUTOINCR);
1002		iosramp += cnt * UINT32SZ;
1003		buf += cnt * UINT32SZ;
1004		len -= cnt * UINT32SZ;
1005
1006		/*
1007		 * copy the remainder using byte access
1008		 */
1009		if (len != 0) {
1010			ddi_rep_put8(handle, buf, iosramp, len,
1011			    DDI_DEV_AUTOINCR);
1012		}
1013	} else if (len != 0) {
1014		/*
1015		 * We know that the "off" is at least word aligned. We
1016		 * need to read data from buf buffer byte at a time, and
1017		 * write it to the IOSRAM word at a time.
1018		 */
1019
1020		ASSERT(((uintptr_t)iosramp & (UINT32SZ - 1)) == 0);
1021
1022		IOSRAMLOG(2,
1023		    "WR: unaligned put32(buf:%p sramp:%p) len:%x\n",
1024		    buf, iosramp, len, NULL);
1025		for (; len >= UINT32SZ; len -= UINT32SZ, iosramp += UINT32SZ) {
1026			word.cbuf[0] = *buf++;
1027			word.cbuf[1] = *buf++;
1028			word.cbuf[2] = *buf++;
1029			word.cbuf[3] = *buf++;
1030			ddi_put32(handle, (uint32_t *)iosramp, word.data);
1031		}
1032
1033		/*
1034		 * copy the remaining data using byte access
1035		 */
1036		if (len != 0) {
1037			ddi_rep_put8(handle, buf, iosramp,
1038			    len, DDI_DEV_AUTOINCR);
1039		}
1040	}
1041
1042	/*
1043	 * Reacquire mutex lock, decrement refcnt and if refcnt is 0 and
1044	 * any threads are waiting for r/w activity to complete, wake them up.
1045	 */
1046	mutex_enter(&iosram_mutex);
1047	ASSERT(iosram_rw_active > 0);
1048
1049	if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
1050		iosram_rw_wakeup = 0;
1051		cv_broadcast(&iosram_rw_wait);
1052	}
1053	mutex_exit(&iosram_mutex);
1054
1055	return (error);
1056}
1057
1058
1059int
1060iosram_force_write(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
1061{
1062	return (_iosram_write(key, off, len, dptr, 1 /* force */));
1063}
1064
1065
1066int
1067iosram_wr(uint32_t key, uint32_t off, uint32_t len, caddr_t dptr)
1068{
1069	return (_iosram_write(key, off, len, dptr, 0));
1070}
1071
1072
1073/*
1074 * iosram_register(key, handler, arg)
1075 *	Register a handler and an arg for the specified chunk.  This handler
1076 *	will be invoked when an interrupt is received from the other side and
1077 *	the int_pending flag for the corresponding key is marked
1078 *	IOSRAM_INT_TO_DOM.
1079 */
1080/* ARGSUSED */
1081int
1082iosram_register(uint32_t key, void (*handler)(), void *arg)
1083{
1084	struct iosram_chunk	*chunkp;
1085	int			error = 0;
1086
1087	/*
1088	 * Acquire lock and look for the requested chunk.  If it exists, and no
1089	 * other callback is registered, proceed with the registration.
1090	 */
1091	mutex_enter(&iosram_mutex);
1092	chunkp = iosram_find_chunk(key);
1093
1094	if (iosram_master == NULL) {
1095		error = EIO;
1096	} else if (chunkp == NULL) {
1097		error = EINVAL;
1098	} else if (chunkp->cback.handler != NULL) {
1099		error = EBUSY;
1100	} else {
1101		chunkp->cback.busy = 0;
1102		chunkp->cback.unregister = 0;
1103		chunkp->cback.handler = handler;
1104		chunkp->cback.arg = arg;
1105	}
1106	mutex_exit(&iosram_mutex);
1107
1108	IOSRAMLOG(1, "REG: key: 0x%x hdlr:%p arg:%p error:%d\n",
1109	    key, handler, arg, error);
1110
1111	return (error);
1112}
1113
1114
1115/*
1116 * iosram_unregister()
1117 *	Unregister handler associated with the specified chunk.
1118 */
1119int
1120iosram_unregister(uint32_t key)
1121{
1122	struct iosram_chunk	*chunkp;
1123	int			error = 0;
1124
1125	/*
1126	 * Acquire lock and look for the requested chunk.  If it exists and has
1127	 * a callback registered, unregister it.
1128	 */
1129	mutex_enter(&iosram_mutex);
1130	chunkp = iosram_find_chunk(key);
1131
1132	if (iosram_master == NULL) {
1133		error = EIO;
1134	} else if (chunkp == NULL) {
1135		error = EINVAL;
1136	} else if (chunkp->cback.busy) {
1137		/*
1138		 * If the handler is already busy (being invoked), then we flag
1139		 * it so it will be unregistered after the invocation completes.
1140		 */
1141		DPRINTF(1, ("IOSRAM(%d): unregister: delaying unreg k:0x%08x\n",
1142		    iosram_master->instance, key));
1143		chunkp->cback.unregister = 1;
1144	} else if (chunkp->cback.handler != NULL) {
1145		chunkp->cback.handler = NULL;
1146		chunkp->cback.arg = NULL;
1147	}
1148	mutex_exit(&iosram_mutex);
1149
1150	IOSRAMLOG(1, "UNREG: key:%x error:%d\n", key, error, NULL, NULL);
1151	return (error);
1152}
1153
1154
1155/*
1156 * iosram_get_flag():
1157 *	Get data_valid and/or int_pending flags associated with the
1158 *	specified key.
1159 */
1160int
1161iosram_get_flag(uint32_t key, uint8_t *data_valid, uint8_t *int_pending)
1162{
1163	iosram_chunk_t	*chunkp;
1164	iosram_flags_t	flags;
1165	int		error = 0;
1166
1167	/*
1168	 * Acquire lock and look for the requested chunk.  If it exists, and no
1169	 * tunnel switch is in progress, read the chunk's flags.
1170	 */
1171	mutex_enter(&iosram_mutex);
1172	chunkp = iosram_find_chunk(key);
1173
1174	if (iosram_master == NULL) {
1175		error = EIO;
1176	} else if (chunkp == NULL) {
1177		error = EINVAL;
1178	} else if (iosram_tswitch_active) {
1179		error = EAGAIN;
1180	} else {
1181		IOSRAM_STAT(getflag);
1182
1183		/*
1184		 * Read the flags
1185		 */
1186		ddi_rep_get8(iosram_handle, (uint8_t *)&flags,
1187		    (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t),
1188		    DDI_DEV_AUTOINCR);
1189
1190		/*
1191		 * Get each flag value that the caller is interested in.
1192		 */
1193		if (data_valid != NULL) {
1194			*data_valid = flags.data_valid;
1195		}
1196
1197		if (int_pending != NULL) {
1198			*int_pending = flags.int_pending;
1199		}
1200	}
1201	mutex_exit(&iosram_mutex);
1202
1203	IOSRAMLOG(1, "GetFlag key:%x data_valid:%x int_pending:%x error:%d\n",
1204	    key, flags.data_valid, flags.int_pending, error);
1205	return (error);
1206}
1207
1208
1209/*
1210 * iosram_set_flag():
1211 *	Set data_valid and int_pending flags associated with the specified key.
1212 */
1213int
1214iosram_set_flag(uint32_t key, uint8_t data_valid, uint8_t int_pending)
1215{
1216	iosram_chunk_t	*chunkp;
1217	iosram_flags_t	flags;
1218	int		error = 0;
1219
1220	/*
1221	 * Acquire lock and look for the requested chunk.  If it exists, and no
1222	 * tunnel switch is in progress, write the chunk's flags.
1223	 */
1224	mutex_enter(&iosram_mutex);
1225	chunkp = iosram_find_chunk(key);
1226
1227	if (iosram_master == NULL) {
1228		error = EIO;
1229	} else if ((chunkp == NULL) ||
1230	    ((data_valid != IOSRAM_DATA_INVALID) &&
1231	    (data_valid != IOSRAM_DATA_VALID)) ||
1232	    ((int_pending != IOSRAM_INT_NONE) &&
1233	    (int_pending != IOSRAM_INT_TO_SSC) &&
1234	    (int_pending != IOSRAM_INT_TO_DOM))) {
1235		error = EINVAL;
1236	} else if (iosram_tswitch_active) {
1237		error = EAGAIN;
1238	} else {
1239		IOSRAM_STAT(setflag);
1240		flags.data_valid = data_valid;
1241		flags.int_pending = int_pending;
1242		ddi_rep_put8(iosram_handle, (uint8_t *)&flags,
1243		    (uint8_t *)(chunkp->flagsp), sizeof (iosram_flags_t),
1244		    DDI_DEV_AUTOINCR);
1245	}
1246	mutex_exit(&iosram_mutex);
1247
1248	IOSRAMLOG(1, "SetFlag key:%x data_valid:%x int_pending:%x error:%d\n",
1249	    key, flags.data_valid, flags.int_pending, error);
1250	return (error);
1251}
1252
1253
1254/*
1255 * iosram_ctrl()
1256 *	This function provides access to a variety of services not available
1257 *	through the basic API.
1258 */
1259int
1260iosram_ctrl(uint32_t key, uint32_t cmd, void *arg)
1261{
1262	struct iosram_chunk	*chunkp;
1263	int			error = 0;
1264
1265	/*
1266	 * Acquire lock and do some argument sanity checking.
1267	 */
1268	mutex_enter(&iosram_mutex);
1269	chunkp = iosram_find_chunk(key);
1270
1271	if (iosram_master == NULL) {
1272		error = EIO;
1273	} else if (chunkp == NULL) {
1274		error = EINVAL;
1275	}
1276
1277	if (error != 0) {
1278		mutex_exit(&iosram_mutex);
1279		return (error);
1280	}
1281
1282	/*
1283	 * Arguments seem okay so far, so process the command.
1284	 */
1285	switch (cmd) {
1286		case IOSRAM_CMD_CHUNKLEN:
1287			/*
1288			 * Return the length of the chunk indicated by the key.
1289			 */
1290			if (arg == NULL) {
1291				error = EINVAL;
1292				break;
1293			}
1294
1295			*(uint32_t *)arg = chunkp->toc_data.len;
1296			break;
1297
1298		default:
1299			error = ENOTSUP;
1300			break;
1301	}
1302
1303	mutex_exit(&iosram_mutex);
1304	return (error);
1305}
1306
1307
1308/*
1309 * iosram_hdr_ctrl()
1310 *	This function provides an interface for the Mailbox Protocol
1311 *	implementation to use when interacting with the IOSRAM header.
1312 */
1313int
1314iosram_hdr_ctrl(uint32_t cmd, void *arg)
1315{
1316	int	error = 0;
1317
1318	/*
1319	 * Acquire lock and do some argument sanity checking.
1320	 */
1321	mutex_enter(&iosram_mutex);
1322
1323	if (iosram_master == NULL) {
1324		error = EIO;
1325	}
1326
1327	if (error != 0) {
1328		mutex_exit(&iosram_mutex);
1329		return (error);
1330	}
1331
1332	switch (cmd) {
1333		case IOSRAM_HDRCMD_GET_SMS_MBOX_VER:
1334			/*
1335			 * Return the value of the sms_mbox_version field.
1336			 */
1337			if (arg == NULL) {
1338				error = EINVAL;
1339				break;
1340			}
1341
1342			*(uint32_t *)arg = IOSRAM_GET_HDRFIELD32(iosram_master,
1343			    sms_mbox_version);
1344			break;
1345
1346		case IOSRAM_HDRCMD_SET_OS_MBOX_VER:
1347			/*
1348			 * Set the value of the os_mbox_version field.
1349			 */
1350			IOSRAM_SET_HDRFIELD32(iosram_master, os_mbox_version,
1351			    (uint32_t)(uintptr_t)arg);
1352			IOSRAM_SET_HDRFIELD32(iosram_master, os_change_mask,
1353			    IOSRAM_HDRFIELD_OS_MBOX_VER);
1354			iosram_send_intr();
1355			break;
1356
1357		case IOSRAM_HDRCMD_REG_CALLBACK:
1358			iosram_hdrchange_handler = (void (*)())arg;
1359			break;
1360
1361		default:
1362			error = ENOTSUP;
1363			break;
1364	}
1365
1366	mutex_exit(&iosram_mutex);
1367	return (error);
1368}
1369
1370
1371/*
1372 * iosram_softintr()
1373 *	IOSRAM soft interrupt handler
1374 */
1375static uint_t
1376iosram_softintr(caddr_t arg)
1377{
1378	uint32_t	hdr_changes;
1379	iosramsoft_t	*softp = (iosramsoft_t *)arg;
1380	iosram_chunk_t	*chunkp;
1381	void		(*handler)();
1382	int		i;
1383	uint8_t		flag;
1384
1385	DPRINTF(1, ("iosram(%d): in iosram_softintr\n", softp->instance));
1386
1387	IOSRAMLOG(2, "SINTR arg/softp:%p  pending:%d busy:%d\n",
1388	    arg, softp->intr_pending, softp->intr_busy, NULL);
1389
1390	mutex_enter(&iosram_mutex);
1391	mutex_enter(&softp->intr_mutex);
1392
1393	/*
1394	 * Do not process interrupt if interrupt handler is already running or
1395	 * no interrupts are pending.
1396	 */
1397	if (softp->intr_busy || !softp->intr_pending) {
1398		mutex_exit(&softp->intr_mutex);
1399		mutex_exit(&iosram_mutex);
1400		DPRINTF(1, ("IOSRAM(%d): softintr: busy=%d pending=%d\n",
1401		    softp->instance, softp->intr_busy, softp->intr_pending));
1402		return (softp->intr_pending ? DDI_INTR_CLAIMED :
1403		    DDI_INTR_UNCLAIMED);
1404	}
1405
1406	/*
1407	 * It's possible for the SC to send an interrupt on the new master
1408	 * before we are able to set our internal state.  If so, we'll retrigger
1409	 * soft interrupt right after tunnel switch completion.
1410	 */
1411	if (softp->state & IOSRAM_STATE_TSWITCH) {
1412		mutex_exit(&softp->intr_mutex);
1413		mutex_exit(&iosram_mutex);
1414		DPRINTF(1, ("IOSRAM(%d): softintr: doing switch "
1415		    "state=0x%x\n", softp->instance, softp->state));
1416		return (DDI_INTR_CLAIMED);
1417	}
1418
1419	/*
1420	 * Do not process interrupt if we are not the master.
1421	 */
1422	if (!(softp->state & IOSRAM_STATE_MASTER)) {
1423		mutex_exit(&softp->intr_mutex);
1424		mutex_exit(&iosram_mutex);
1425		DPRINTF(1, ("IOSRAM(%d): softintr: no master state=0x%x\n ",
1426		    softp->instance, softp->state));
1427		return (DDI_INTR_CLAIMED);
1428	}
1429
1430	IOSRAM_STAT(sintr_recv);
1431
1432	/*
1433	 * If the driver is suspended, then we should not process any
1434	 * interrupts.  Instead, we trigger a soft interrupt when the driver
1435	 * resumes.
1436	 */
1437	if (softp->suspended) {
1438		mutex_exit(&softp->intr_mutex);
1439		mutex_exit(&iosram_mutex);
1440		DPRINTF(1, ("IOSRAM(%d): softintr: suspended\n",
1441		    softp->instance));
1442		return (DDI_INTR_CLAIMED);
1443	}
1444
1445	/*
1446	 * Indicate that the IOSRAM interrupt handler is busy.  Note that this
1447	 * includes incrementing the reader/writer count, since we don't want
1448	 * any tunnel switches to start up while we're processing callbacks.
1449	 */
1450	softp->intr_busy = 1;
1451	iosram_rw_active++;
1452#if defined(DEBUG)
1453	if (iosram_rw_active > iosram_rw_active_max) {
1454		iosram_rw_active_max = iosram_rw_active;
1455	}
1456#endif
1457
1458	do {
1459		DPRINTF(1, ("IOSRAM(%d): softintr: processing interrupt\n",
1460		    softp->instance));
1461
1462		softp->intr_pending = 0;
1463
1464		mutex_exit(&softp->intr_mutex);
1465
1466		/*
1467		 * Process changes to the IOSRAM header.
1468		 */
1469		hdr_changes = IOSRAM_GET_HDRFIELD32(iosram_master,
1470		    sms_change_mask);
1471		if (hdr_changes != 0) {
1472			int	error;
1473
1474			IOSRAM_SET_HDRFIELD32(iosram_master, sms_change_mask,
1475			    0);
1476			if (hdr_changes & IOSRAM_HDRFIELD_TOC_INDEX) {
1477				/*
1478				 * XXX is it safe to temporarily release the
1479				 * iosram_mutex here?
1480				 */
1481				mutex_exit(&iosram_mutex);
1482				error = iosram_read_toc(iosram_master);
1483				mutex_enter(&iosram_mutex);
1484				if (error) {
1485					cmn_err(CE_WARN, "iosram_read_toc: new"
1486					    " TOC invalid; using old TOC.");
1487				}
1488				iosram_update_addrs(iosram_master);
1489			}
1490
1491			if (iosram_hdrchange_handler != NULL) {
1492				mutex_exit(&iosram_mutex);
1493				iosram_hdrchange_handler();
1494				mutex_enter(&iosram_mutex);
1495			}
1496		}
1497
1498		/*
1499		 * Get data_valid/int_pending flags and generate a callback if
1500		 * applicable.  For now, we read only those flags for which a
1501		 * callback has been registered.  We can optimize reading of
1502		 * flags by reading them all at once and then process them
1503		 * later.
1504		 */
1505		for (i = 0, chunkp = chunks; i < nchunks; i++,
1506		    chunkp++) {
1507#if DEBUG
1508			flag =  ddi_get8(iosram_handle,
1509			    &(chunkp->flagsp->int_pending));
1510			DPRINTF(1, ("IOSRAM(%d): softintr chunk #%d "
1511			    "flag=0x%x handler=%p\n",
1512			    softp->instance, i, (int)flag,
1513			    chunkp->cback.handler));
1514#endif
1515			if ((handler = chunkp->cback.handler) == NULL) {
1516				continue;
1517			}
1518			flag = ddi_get8(iosram_handle,
1519			    &(chunkp->flagsp->int_pending));
1520			if (flag == IOSRAM_INT_TO_DOM) {
1521				DPRINTF(1,
1522				    ("IOSRAM(%d): softintr: invoking handler\n",
1523				    softp->instance));
1524				IOSRAMLOG(1,
1525				    "SINTR invoking hdlr:%p arg:%p index:%d\n",
1526				    handler, chunkp->cback.arg, i, NULL);
1527				IOSRAM_STAT(callbacks);
1528
1529				ddi_put8(iosram_handle,
1530				    &(chunkp->flagsp->int_pending),
1531				    IOSRAM_INT_NONE);
1532				chunkp->cback.busy = 1;
1533				mutex_exit(&iosram_mutex);
1534				(*handler)(chunkp->cback.arg);
1535				mutex_enter(&iosram_mutex);
1536				chunkp->cback.busy = 0;
1537
1538				/*
1539				 * If iosram_unregister was called while the
1540				 * callback was being invoked, complete the
1541				 * unregistration here.
1542				 */
1543				if (chunkp->cback.unregister) {
1544					DPRINTF(1, ("IOSRAM(%d): softintr: "
1545					    "delayed unreg k:0x%08x\n",
1546					    softp->instance,
1547					    chunkp->toc_data.key));
1548					chunkp->cback.handler = NULL;
1549					chunkp->cback.arg = NULL;
1550					chunkp->cback.unregister = 0;
1551				}
1552			}
1553
1554			/*
1555			 * If there's a tunnel switch waiting to run, give it
1556			 * higher priority than these callbacks by bailing out.
1557			 * They'll still be invoked on the new master iosram
1558			 * when the tunnel switch is done.
1559			 */
1560			if (iosram_tswitch_active) {
1561				break;
1562			}
1563		}
1564
1565		mutex_enter(&softp->intr_mutex);
1566
1567	} while (softp->intr_pending && !softp->suspended &&
1568	    !iosram_tswitch_active);
1569
1570	/*
1571	 * Indicate IOSRAM interrupt handler is not BUSY any more
1572	 */
1573	softp->intr_busy = 0;
1574
1575	ASSERT(iosram_rw_active > 0);
1576	if ((--iosram_rw_active == 0) && iosram_rw_wakeup) {
1577		iosram_rw_wakeup = 0;
1578		cv_broadcast(&iosram_rw_wait);
1579	}
1580
1581	mutex_exit(&softp->intr_mutex);
1582	mutex_exit(&iosram_mutex);
1583
1584	DPRINTF(1, ("iosram(%d): softintr exit\n", softp->instance));
1585
1586	return (DDI_INTR_CLAIMED);
1587}
1588
1589
1590/*
1591 * iosram_intr()
1592 *	IOSRAM real interrupt handler
1593 */
1594static uint_t
1595iosram_intr(caddr_t arg)
1596{
1597	iosramsoft_t	*softp = (iosramsoft_t *)arg;
1598	int		result = DDI_INTR_UNCLAIMED;
1599	uint32_t	int_status;
1600
1601	DPRINTF(2, ("iosram(%d): in iosram_intr\n", softp->instance));
1602
1603	mutex_enter(&softp->intr_mutex);
1604
1605	if (softp->sbbc_handle == NULL) {
1606		/*
1607		 * The SBBC registers region is not mapped in.
1608		 * Set the interrupt pending flag here, and process the
1609		 * interrupt after the tunnel switch.
1610		 */
1611		DPRINTF(1, ("IOSRAM(%d): iosram_intr: SBBC not mapped\n",
1612		    softp->instance));
1613		softp->intr_pending = 1;
1614		mutex_exit(&softp->intr_mutex);
1615		return (DDI_INTR_UNCLAIMED);
1616	}
1617
1618	int_status = ddi_get32(softp->sbbc_handle,
1619	    &(softp->sbbc_region->int_status.reg));
1620	DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n", int_status));
1621
1622	if (int_status & IOSRAM_SBBC_INT0) {
1623		result = DDI_INTR_CLAIMED;
1624		DPRINTF(1, ("iosram_intr: int0 detected!\n"));
1625	}
1626
1627	if (int_status & IOSRAM_SBBC_INT1) {
1628		result = DDI_INTR_CLAIMED;
1629		DPRINTF(1, ("iosram_intr: int1 detected!\n"));
1630	}
1631
1632	if (result == DDI_INTR_CLAIMED) {
1633		ddi_put32(softp->sbbc_handle,
1634		    &(softp->sbbc_region->int_status.reg), int_status);
1635		int_status = ddi_get32(softp->sbbc_handle,
1636		    &(softp->sbbc_region->int_status.reg));
1637		DPRINTF(1, ("iosram_intr: int_status = 0x%08x\n",
1638		    int_status));
1639
1640		softp->intr_pending = 1;
1641		/*
1642		 * Trigger soft interrupt if not executing and
1643		 * not suspended.
1644		 */
1645		if (!softp->intr_busy && !softp->suspended &&
1646		    (softp->softintr_id != NULL)) {
1647			DPRINTF(1, ("iosram(%d): trigger softint\n",
1648			    softp->instance));
1649			ddi_trigger_softintr(softp->softintr_id);
1650		}
1651	}
1652
1653	IOSRAM_STAT(intr_recv);
1654
1655	mutex_exit(&softp->intr_mutex);
1656
1657	IOSRAMLOG(2, "INTR arg/softp:%p  pending:%d busy:%d\n",
1658	    arg, softp->intr_pending, softp->intr_busy, NULL);
1659	DPRINTF(1, ("iosram(%d): iosram_intr exit\n", softp->instance));
1660
1661	return (result);
1662}
1663
1664
1665/*
1666 * iosram_send_intr()
1667 *	Send an interrupt to the SSP side via AXQ driver
1668 */
1669int
1670iosram_send_intr()
1671{
1672	IOSRAMLOG(1, "SendIntr called\n", NULL, NULL, NULL, NULL);
1673	IOSRAM_STAT(intr_send);
1674	DPRINTF(1, ("iosram iosram_send_intr invoked\n"));
1675
1676	return (axq_cpu2ssc_intr(0));
1677}
1678
1679
1680#if defined(DEBUG)
1681static void
1682iosram_dummy_cback(void *arg)
1683{
1684	DPRINTF(1, ("iosram_dummy_cback invoked arg:%p\n", arg));
1685}
1686#endif /* DEBUG */
1687
1688
1689/*ARGSUSED1*/
1690static int
1691iosram_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1692		int *rvalp)
1693{
1694	struct iosramsoft	*softp;
1695	int			error = DDI_SUCCESS;
1696
1697	softp = ddi_get_soft_state(iosramsoft_statep, getminor(dev));
1698	if (softp == NULL) {
1699		return (ENXIO);
1700	}
1701	IOSRAMLOG(1, "IOCTL: dev:%p cmd:%x arg:%p ... instance %d\n",
1702	    dev, cmd, arg, softp->instance);
1703
1704	switch (cmd) {
1705#if defined(DEBUG)
1706	case IOSRAM_GET_FLAG:
1707		{
1708		iosram_io_t	req;
1709		uint8_t		data_valid, int_pending;
1710
1711		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
1712			return (EFAULT);
1713		}
1714
1715		DPRINTF(2, ("IOSRAM_GET_FLAG(key:%x\n", req.key));
1716
1717		req.retval = iosram_get_flag(req.key, &data_valid,
1718		    &int_pending);
1719		req.data_valid = (uint32_t)data_valid;
1720		req.int_pending = (uint32_t)int_pending;
1721
1722		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
1723			DPRINTF(1,
1724			    ("IOSRAM_GET_FLAG: can't copyout req.retval (%x)",
1725			    req.retval));
1726			error = EFAULT;
1727		}
1728
1729		return (error);
1730		}
1731
1732	case IOSRAM_SET_FLAG:
1733		{
1734		iosram_io_t	req;
1735
1736		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
1737			return (EFAULT);
1738		}
1739
1740		DPRINTF(2, ("IOSRAM_SET_FLAG(key:%x data_valid:%x "
1741		    "int_pending:%x\n", req.key, req.data_valid,
1742		    req.int_pending));
1743
1744		req.retval = iosram_set_flag(req.key, req.data_valid,
1745		    req.int_pending);
1746
1747		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
1748			DPRINTF(1, ("IOSRAM_SET_FLAG: can't copyout req.retval"
1749			    " (%x)\n", req.retval));
1750			error = EFAULT;
1751		}
1752
1753		return (error);
1754		}
1755
1756	case IOSRAM_RD:
1757		{
1758		caddr_t		bufp;
1759		int		len;
1760		iosram_io_t	req;
1761
1762		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
1763			return (EFAULT);
1764		}
1765
1766		DPRINTF(2, ("IOSRAM_RD(k:%x o:%x len:%x bufp:%p\n", req.key,
1767		    req.off, req.len, (void *)(uintptr_t)req.bufp));
1768
1769		len = req.len;
1770		bufp = kmem_alloc(len, KM_SLEEP);
1771
1772		req.retval = iosram_rd(req.key, req.off, req.len, bufp);
1773
1774		if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, len, mode)) {
1775			DPRINTF(1, ("IOSRAM_RD: copyout(%p, %p,%x,%x) failed\n",
1776			    bufp, (void *)(uintptr_t)req.bufp, len, mode));
1777			error = EFAULT;
1778		} else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
1779			DPRINTF(1, ("IOSRAM_RD: can't copyout retval (%x)\n",
1780			    req.retval));
1781			error = EFAULT;
1782		}
1783
1784		kmem_free(bufp, len);
1785		return (error);
1786		}
1787
1788	case IOSRAM_WR:
1789		{
1790		caddr_t		bufp;
1791		iosram_io_t	req;
1792		int		len;
1793
1794		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
1795			return (EFAULT);
1796		}
1797
1798		DPRINTF(2, ("IOSRAM_WR(k:%x o:%x len:%x bufp:%p\n",
1799		    req.key, req.off, req.len, req.bufp));
1800		len = req.len;
1801		bufp = kmem_alloc(len, KM_SLEEP);
1802		if (ddi_copyin((void *)(uintptr_t)req.bufp, bufp, len, mode)) {
1803			error = EFAULT;
1804		} else {
1805			req.retval = iosram_wr(req.key, req.off, req.len,
1806			    bufp);
1807
1808			if (ddi_copyout(&req, (void *)arg, sizeof (req),
1809			    mode)) {
1810				error = EFAULT;
1811			}
1812		}
1813		kmem_free(bufp, len);
1814		return (error);
1815		}
1816
1817	case IOSRAM_TOC:
1818		{
1819		caddr_t		bufp;
1820		int		len;
1821		iosram_io_t	req;
1822
1823		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
1824			return (EFAULT);
1825		}
1826
1827		DPRINTF(2, ("IOSRAM_TOC (req.bufp:%x req.len:%x) \n",
1828		    req.bufp, req.len));
1829
1830		len = req.len;
1831		bufp = kmem_alloc(len, KM_SLEEP);
1832
1833		req.retval = iosram_get_keys((iosram_toc_entry_t *)bufp,
1834		    &req.len);
1835
1836		if (ddi_copyout(bufp, (void *)(uintptr_t)req.bufp, req.len,
1837		    mode)) {
1838			DPRINTF(1,
1839			    ("IOSRAM_TOC: copyout(%p, %p,%x,%x) failed\n",
1840			    bufp, (void *)(uintptr_t)req.bufp, req.len, mode));
1841			error = EFAULT;
1842		} else if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
1843			DPRINTF(1, ("IOSRAM_TOC: can't copyout retval (%x)\n",
1844			    req.retval));
1845			error = EFAULT;
1846		}
1847		kmem_free(bufp, len);
1848		return (error);
1849		}
1850
1851	case IOSRAM_SEND_INTR:
1852		{
1853		DPRINTF(2, ("IOSRAM_SEND_INTR\n"));
1854
1855		switch ((int)arg) {
1856		case 0x11:
1857		case 0x22:
1858		case 0x44:
1859		case 0x88:
1860			ddi_put32(softp->sbbc_handle,
1861			    &(softp->sbbc_region->int_enable.reg), (int)arg);
1862			DPRINTF(1, ("Wrote 0x%x to int_enable.reg\n",
1863			    (int)arg));
1864			break;
1865		case 0xBB:
1866			ddi_put32(softp->sbbc_handle,
1867			    &(softp->sbbc_region->p0_int_gen.reg), 1);
1868			DPRINTF(1, ("Wrote 1 to p0_int_gen.reg\n"));
1869			break;
1870		default:
1871			error = iosram_send_intr();
1872		}
1873
1874		return (error);
1875		}
1876
1877	case IOSRAM_PRINT_CBACK:
1878		iosram_print_cback();
1879		break;
1880
1881	case IOSRAM_PRINT_STATE:
1882		iosram_print_state((int)arg);
1883		break;
1884
1885#if IOSRAM_STATS
1886	case IOSRAM_PRINT_STATS:
1887		iosram_print_stats();
1888		break;
1889#endif
1890
1891#if IOSRAM_LOG
1892	case IOSRAM_PRINT_LOG:
1893		iosram_print_log((int)arg);
1894		break;
1895#endif
1896
1897	case IOSRAM_TUNNEL_SWITCH:
1898		error = iosram_switchfrom((int)arg);
1899		break;
1900
1901	case IOSRAM_PRINT_FLAGS:
1902		iosram_print_flags();
1903		break;
1904
1905	case IOSRAM_REG_CBACK:
1906		{
1907		iosram_io_t	req;
1908
1909		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
1910			return (EFAULT);
1911		}
1912
1913		DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key));
1914
1915		req.retval = iosram_register(req.key, iosram_dummy_cback,
1916		    (void *)(uintptr_t)req.key);
1917		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
1918			error = EFAULT;
1919		}
1920
1921		return (error);
1922		}
1923
1924	case IOSRAM_UNREG_CBACK:
1925		{
1926		iosram_io_t	req;
1927
1928		if (ddi_copyin((void *)arg, &req, sizeof (req), mode)) {
1929			return (EFAULT);
1930		}
1931
1932		DPRINTF(2, ("IOSRAM_REG_CBACK(k:%x)\n", req.key));
1933
1934		req.retval = iosram_unregister(req.key);
1935		if (ddi_copyout(&req, (void *)arg, sizeof (req), mode)) {
1936			error = EFAULT;
1937		}
1938
1939		return (error);
1940		}
1941
1942	case IOSRAM_SEMA_ACQUIRE:
1943	{
1944		DPRINTF(1, ("IOSRAM_SEMA_ACQUIRE\n"));
1945		error = iosram_sema_acquire(NULL);
1946		return (error);
1947	}
1948
1949	case IOSRAM_SEMA_RELEASE:
1950	{
1951		DPRINTF(1, ("IOSRAM_SEMA_RELEASE\n"));
1952		error = iosram_sema_release();
1953		return (error);
1954	}
1955
1956#endif /* DEBUG */
1957
1958	default:
1959		DPRINTF(1, ("iosram_ioctl: Illegal command %x\n", cmd));
1960		error = ENOTTY;
1961	}
1962
1963	return (error);
1964}
1965
1966
1967/*
1968 * iosram_switch_tunnel(softp)
1969 *	Switch master tunnel to the specified instance
1970 *	Must be called while holding iosram_mutex
1971 */
1972/*ARGSUSED*/
1973static int
1974iosram_switch_tunnel(iosramsoft_t *softp)
1975{
1976#ifdef DEBUG
1977	int		instance = softp->instance;
1978#endif
1979	int		error = 0;
1980	iosramsoft_t	*prev_master;
1981
1982	ASSERT(mutex_owned(&iosram_mutex));
1983
1984	DPRINTF(1, ("tunnel switch new master:%p (%d) current master:%p (%d)\n",
1985	    softp, instance, iosram_master,
1986	    ((iosram_master) ? iosram_master->instance : -1)));
1987	IOSRAMLOG(1, "TSWTCH: new_master:%p (%p) iosram_master:%p (%d)\n",
1988	    softp, instance, iosram_master,
1989	    ((iosram_master) ? iosram_master->instance : -1));
1990
1991	if (softp == NULL || (softp->state & IOSRAM_STATE_DETACH)) {
1992		return (ENXIO);
1993	}
1994	if (iosram_master == softp) {
1995		return (0);
1996	}
1997
1998
1999	/*
2000	 * We protect against the softp structure being deallocated by setting
2001	 * the IOSRAM_STATE_TSWITCH state flag. The detach routine will check
2002	 * for this flag and if set, it will wait for this flag to be reset or
2003	 * refuse the detach operation.
2004	 */
2005	iosram_new_master = softp;
2006	softp->state |= IOSRAM_STATE_TSWITCH;
2007	prev_master = iosram_master;
2008	if (prev_master) {
2009		prev_master->state |= IOSRAM_STATE_TSWITCH;
2010	}
2011	mutex_exit(&iosram_mutex);
2012
2013	/*
2014	 * Map the target IOSRAM, read the TOC, and register interrupts if not
2015	 * already done.
2016	 */
2017	DPRINTF(1, ("iosram(%d): mapping IOSRAM and SBBC\n",
2018	    softp->instance));
2019	IOSRAMLOG(1, "TSWTCH: mapping instance:%d  softp:%p\n",
2020	    instance, softp, NULL, NULL);
2021
2022	if (iosram_setup_map(softp) != DDI_SUCCESS) {
2023		error = ENXIO;
2024	} else if ((chunks == NULL) && (iosram_read_toc(softp) != 0)) {
2025		iosram_remove_map(softp);
2026		error = EINVAL;
2027	} else if (iosram_add_intr(softp) != DDI_SUCCESS) {
2028		/*
2029		 * If there was no previous master, purge the TOC data that
2030		 * iosram_read_toc() created.
2031		 */
2032		if ((prev_master == NULL) && (chunks != NULL)) {
2033			kmem_free(chunks, nchunks * sizeof (iosram_chunk_t));
2034			chunks = NULL;
2035			nchunks = 0;
2036			iosram_init_hashtab();
2037		}
2038		iosram_remove_map(softp);
2039		error = ENXIO;
2040	}
2041
2042	/*
2043	 * If we are asked to abort tunnel switch, do so now, before invoking
2044	 * the OBP callback.
2045	 */
2046	if (iosram_tswitch_aborted) {
2047
2048		/*
2049		 * Once the tunnel switch is aborted, this thread should not
2050		 * resume.  If it does, we simply log a message.  We can't unmap
2051		 * the new master IOSRAM as it may be accessed in
2052		 * iosram_abort_tswitch(). It will be unmapped when it is
2053		 * detached.
2054		 */
2055		IOSRAMLOG(1,
2056		    "TSWTCH: aborted (pre OBP cback). Thread resumed.\n",
2057		    NULL, NULL, NULL, NULL);
2058		error = EIO;
2059	}
2060
2061	if (error) {
2062		IOSRAMLOG(1,
2063		    "TSWTCH: map failed instance:%d  softp:%p error:%x\n",
2064		    instance, softp, error, NULL);
2065		goto done;
2066	}
2067
2068	if (prev_master != NULL) {
2069		int	result;
2070
2071		/*
2072		 * Now invoke the OBP interface to do the tunnel switch.
2073		 */
2074		result = prom_starcat_switch_tunnel(softp->portid,
2075		    OBP_TSWITCH_REQREPLY);
2076		if (result != 0) {
2077			error = EIO;
2078		}
2079		IOSRAMLOG(1,
2080		    "TSWTCH: OBP tswitch portid:%x result:%x error:%x\n",
2081		    softp->portid, result, error, NULL);
2082		IOSRAM_STAT(tswitch);
2083		iosram_tswitch_tstamp = ddi_get_lbolt();
2084	}
2085
2086	mutex_enter(&iosram_mutex);
2087	if (iosram_tswitch_aborted) {
2088		/*
2089		 * Tunnel switch aborted.  This thread should not resume.
2090		 * For now, we simply log a message, but don't unmap any
2091		 * IOSRAM at this stage as it may be accessed within the
2092		 * isoram_abort_tswitch(). The IOSRAM will be unmapped
2093		 * when that instance is detached.
2094		 */
2095		if (iosram_tswitch_aborted) {
2096			IOSRAMLOG(1,
2097			    "TSWTCH: aborted (post OBP cback). Thread"
2098			    " resumed.\n", NULL, NULL, NULL, NULL);
2099			error = EIO;
2100			mutex_exit(&iosram_mutex);
2101		}
2102	} else if (error) {
2103		/*
2104		 * Tunnel switch failed.  Continue using previous tunnel.
2105		 * However, unmap new (target) IOSRAM.
2106		 */
2107		iosram_new_master = NULL;
2108		mutex_exit(&iosram_mutex);
2109		iosram_remove_intr(softp);
2110		iosram_remove_map(softp);
2111	} else {
2112		/*
2113		 * Tunnel switch was successful.  Set the new master.
2114		 * Also unmap old master IOSRAM and remove any interrupts
2115		 * associated with that.
2116		 *
2117		 * Note that a call to iosram_force_write() allows access
2118		 * to the IOSRAM while tunnel switch is in progress.  That
2119		 * means we need to set the new master before unmapping
2120		 * the old master.
2121		 */
2122		iosram_set_master(softp);
2123		iosram_new_master = NULL;
2124		mutex_exit(&iosram_mutex);
2125
2126		if (prev_master) {
2127			IOSRAMLOG(1, "TSWTCH: unmapping prev_master:%p (%d)\n",
2128			    prev_master, prev_master->instance, NULL, NULL);
2129			iosram_remove_intr(prev_master);
2130			iosram_remove_map(prev_master);
2131		}
2132	}
2133
2134done:
2135	mutex_enter(&iosram_mutex);
2136
2137	/*
2138	 * Clear the tunnel switch flag on the source and destination
2139	 * instances.
2140	 */
2141	if (prev_master) {
2142		prev_master->state &= ~IOSRAM_STATE_TSWITCH;
2143	}
2144	softp->state &= ~IOSRAM_STATE_TSWITCH;
2145
2146	/*
2147	 * Since incoming interrupts could get lost during a tunnel switch,
2148	 * trigger a soft interrupt just in case.  No harm other than a bit
2149	 * of wasted effort will be caused if no interrupts were dropped.
2150	 */
2151	mutex_enter(&softp->intr_mutex);
2152	iosram_master->intr_pending = 1;
2153	if ((iosram_master->softintr_id != NULL) &&
2154	    (iosram_master->intr_busy == 0)) {
2155		ddi_trigger_softintr(iosram_master->softintr_id);
2156	}
2157	mutex_exit(&softp->intr_mutex);
2158
2159	IOSRAMLOG(1, "TSWTCH: done error:%d iosram_master:%p instance:%d\n",
2160	    error, iosram_master,
2161	    (iosram_master) ? iosram_master->instance : -1, NULL);
2162
2163	return (error);
2164}
2165
2166
2167/*
2168 * iosram_abort_tswitch()
2169 * Must be called while holding iosram_mutex.
2170 */
2171static void
2172iosram_abort_tswitch()
2173{
2174	uint32_t  master_valid, new_master_valid;
2175
2176	ASSERT(mutex_owned(&iosram_mutex));
2177
2178	if ((!iosram_tswitch_active) || iosram_tswitch_aborted) {
2179		return;
2180	}
2181
2182	ASSERT(iosram_master != NULL);
2183
2184	IOSRAMLOG(1, "ABORT: iosram_master:%p (%d) iosram_new_master:%p (%d)\n",
2185	    iosram_master, iosram_master->instance, iosram_new_master,
2186	    (iosram_new_master == NULL) ? -1 : iosram_new_master->instance);
2187
2188	/*
2189	 * The first call to iosram_force_write() in the middle of tunnel switch
2190	 * will get here. We lookup IOSRAM VALID location and setup appropriate
2191	 * master, if one is still valid.  We also set iosram_tswitch_aborted to
2192	 * prevent reentering this code and to catch if the OBP callback thread
2193	 * somehow resumes.
2194	 */
2195	iosram_tswitch_aborted = 1;
2196
2197	if ((iosram_new_master == NULL) ||
2198	    (iosram_new_master = iosram_master)) {
2199		/*
2200		 * New master hasn't been selected yet, or OBP callback
2201		 * succeeded and we already selected new IOSRAM as master, but
2202		 * system crashed in the middle of unmapping previous master or
2203		 * cleaning up state.  Use the existing master.
2204		 */
2205		ASSERT(iosram_master->iosramp != NULL);
2206		ASSERT(IOSRAM_GET_HDRFIELD32(iosram_master, status) ==
2207		    IOSRAM_VALID);
2208		IOSRAMLOG(1, "ABORT: master (%d) already determined.\n",
2209		    iosram_master->instance, NULL, NULL, NULL);
2210
2211		return;
2212	}
2213
2214	/*
2215	 * System crashed in the middle of tunnel switch and we know that the
2216	 * new target has not been marked master yet.  That means, the old
2217	 * master should still be mapped.  We need to abort the tunnel switch
2218	 * and setup a valid master, if possible, so that we can write to the
2219	 * IOSRAM.
2220	 *
2221	 * We select a new master based upon the IOSRAM header status fields in
2222	 * the previous master IOSRAM and the target IOSRAM as follows:
2223	 *
2224	 *	iosram_master	iosram-tswitch
2225	 * 	(Prev Master)	(New Target)	Decision
2226	 *	---------------	---------------	-----------
2227	 *	  VALID		  don't care	prev master
2228	 *	  INTRANSIT	  INVALID	prev master
2229	 *	  INTRANSIT	  INTRANSIT	prev master
2230	 *	  INTRANSIT	  VALID		new target
2231	 *	  INVALID	  INVALID	shouldn't ever happen
2232	 *	  INVALID	  INTRANSIT	shouldn't ever happen
2233	 *	  INVALID	  VALID		new target
2234	 */
2235
2236	master_valid = (iosram_master->iosramp != NULL) ?
2237	    IOSRAM_GET_HDRFIELD32(iosram_master, status) : IOSRAM_INVALID;
2238	new_master_valid = (iosram_new_master->iosramp != NULL) ?
2239	    IOSRAM_GET_HDRFIELD32(iosram_new_master, status) : IOSRAM_INVALID;
2240
2241	if (master_valid == IOSRAM_VALID) {
2242		/* EMPTY */
2243		/*
2244		 * OBP hasn't been called yet or, if it has, it hasn't started
2245		 * copying yet.  Use the existing master.  Note that the new
2246		 * master may not be mapped yet.
2247		 */
2248		IOSRAMLOG(1, "ABORT: prev master(%d) is VALID\n",
2249		    iosram_master->instance, NULL, NULL, NULL);
2250	} else if (master_valid == IOSRAM_INTRANSIT) {
2251		/*
2252		 * The system crashed after OBP started processing the tunnel
2253		 * switch but before the iosram driver determined that it was
2254		 * complete.  Use the new master if it has been marked valid,
2255		 * meaning that OBP finished copying data to it, or the old
2256		 * master otherwise.
2257		 */
2258		IOSRAMLOG(1, "ABORT: prev master(%d) is INTRANSIT\n",
2259		    iosram_master->instance, NULL, NULL, NULL);
2260
2261		if (new_master_valid == IOSRAM_VALID) {
2262			iosram_set_master(iosram_new_master);
2263			IOSRAMLOG(1, "ABORT: new master(%d) is VALID\n",
2264			    iosram_new_master->instance, NULL, NULL,
2265			    NULL);
2266		} else {
2267			prom_starcat_switch_tunnel(iosram_master->portid,
2268			    OBP_TSWITCH_NOREPLY);
2269
2270			IOSRAMLOG(1, "ABORT: new master(%d) is INVALID\n",
2271			    iosram_new_master->instance, NULL, NULL,
2272			    NULL);
2273		}
2274	} else {
2275		/*
2276		 * The system crashed after OBP marked the old master INVALID,
2277		 * which means the new master is the way to go.
2278		 */
2279		IOSRAMLOG(1, "ABORT: prev master(%d) is INVALID\n",
2280		    iosram_master->instance, NULL, NULL, NULL);
2281
2282		ASSERT(new_master_valid == IOSRAM_VALID);
2283
2284		iosram_set_master(iosram_new_master);
2285	}
2286
2287	IOSRAMLOG(1, "ABORT: Instance %d selected as master\n",
2288	    iosram_master->instance, NULL, NULL, NULL);
2289}
2290
2291
2292/*
2293 * iosram_switchfrom(instance)
2294 *	Switch master tunnel away from the specified instance
2295 */
2296/*ARGSUSED*/
2297int
2298iosram_switchfrom(int instance)
2299{
2300	struct iosramsoft	*softp;
2301	int			error = 0;
2302	int			count;
2303	clock_t			current_tstamp;
2304	clock_t			tstamp_interval;
2305	struct iosramsoft	*last_master = NULL;
2306	static int		last_master_instance = -1;
2307
2308	IOSRAMLOG(1, "SwtchFrom: instance:%d  iosram_master:%p (%d)\n",
2309	    instance, iosram_master,
2310	    ((iosram_master) ? iosram_master->instance : -1), NULL);
2311
2312	mutex_enter(&iosram_mutex);
2313
2314	/*
2315	 * Wait if another tunnel switch is in progress
2316	 */
2317	for (count = 0; iosram_tswitch_active && count < IOSRAM_TSWITCH_RETRY;
2318	    count++) {
2319		iosram_tswitch_wakeup = 1;
2320		cv_wait(&iosram_tswitch_wait, &iosram_mutex);
2321	}
2322
2323	if (iosram_tswitch_active) {
2324		mutex_exit(&iosram_mutex);
2325		return (EAGAIN);
2326	}
2327
2328	/*
2329	 * Check if the specified instance holds the tunnel. If not,
2330	 * then we are done.
2331	 */
2332	if ((iosram_master == NULL) || (iosram_master->instance != instance)) {
2333		mutex_exit(&iosram_mutex);
2334		return (0);
2335	}
2336
2337	/*
2338	 * Before beginning the tunnel switch process, wait for any outstanding
2339	 * read/write activity to complete.
2340	 */
2341	iosram_tswitch_active = 1;
2342	while (iosram_rw_active) {
2343		iosram_rw_wakeup = 1;
2344		cv_wait(&iosram_rw_wait, &iosram_mutex);
2345	}
2346
2347	/*
2348	 * If a previous tunnel switch just completed, we have to make sure
2349	 * HWAD has enough time to find the new tunnel before we switch
2350	 * away from it.  Otherwise, OBP's mailbox message to OSD will never
2351	 * get through.  Just to be paranoid about synchronization of lbolt
2352	 * across different CPUs, make sure the current attempt isn't noted
2353	 * as starting _before_ the last tunnel switch completed.
2354	 */
2355	current_tstamp = ddi_get_lbolt();
2356	if (current_tstamp > iosram_tswitch_tstamp) {
2357		tstamp_interval = current_tstamp - iosram_tswitch_tstamp;
2358	} else {
2359		tstamp_interval = 0;
2360	}
2361	if (drv_hztousec(tstamp_interval) < IOSRAM_TSWITCH_DELAY_US) {
2362		mutex_exit(&iosram_mutex);
2363		delay(drv_usectohz(IOSRAM_TSWITCH_DELAY_US) - tstamp_interval);
2364		mutex_enter(&iosram_mutex);
2365	}
2366
2367	/*
2368	 * The specified instance holds the tunnel.  We need to move it to some
2369	 * other IOSRAM.  Try out all possible IOSRAMs listed in
2370	 * iosram_instances.  For now, we always search from the first entry.
2371	 * In future, it may be desirable to start where we left off.
2372	 */
2373	for (softp = iosram_instances; softp != NULL; softp = softp->next) {
2374		if (iosram_tswitch_aborted) {
2375			break;
2376		}
2377
2378		/* we can't switch _to_ the instance we're switching _from_ */
2379		if (softp->instance == instance) {
2380			continue;
2381		}
2382
2383		/* skip over instances being detached */
2384		if (softp->state & IOSRAM_STATE_DETACH) {
2385			continue;
2386		}
2387
2388		/*
2389		 * Try to avoid reverting to the last instance we switched away
2390		 * from, as we expect that one to be detached eventually.  Keep
2391		 * track of it, though, so we can go ahead and try switching to
2392		 * it if no other viable candidates are found.
2393		 */
2394		if (softp->instance == last_master_instance) {
2395			last_master = softp;
2396			continue;
2397		}
2398
2399		/*
2400		 * Do the tunnel switch.  If successful, record the instance of
2401		 * the master we just left behind so we can try to avoid
2402		 * reverting to it next time.
2403		 */
2404		if (iosram_switch_tunnel(softp) == 0) {
2405			last_master_instance = instance;
2406			break;
2407		}
2408	}
2409
2410	/*
2411	 * If we failed to switch the tunnel, but we skipped over an instance
2412	 * that had previously been switched out of because we expected it to be
2413	 * detached, go ahead and try it anyway (unless the tswitch was aborted
2414	 * or the instance we skipped is finally being detached).
2415	 */
2416	if ((softp == NULL) && (last_master != NULL) &&
2417	    !iosram_tswitch_aborted &&
2418	    !(last_master->state & IOSRAM_STATE_DETACH)) {
2419		if (iosram_switch_tunnel(last_master) == 0) {
2420			softp = last_master;
2421			last_master_instance = instance;
2422		}
2423	}
2424
2425	if ((softp == NULL) || (iosram_tswitch_aborted)) {
2426		error = EIO;
2427	}
2428
2429	/*
2430	 * If there are additional tunnel switches queued up waiting for this
2431	 * one to complete, wake them up.
2432	 */
2433	if (iosram_tswitch_wakeup) {
2434		iosram_tswitch_wakeup = 0;
2435		cv_broadcast(&iosram_tswitch_wait);
2436	}
2437	iosram_tswitch_active = 0;
2438	mutex_exit(&iosram_mutex);
2439	return (error);
2440}
2441
2442
2443/*
2444 * iosram_tunnel_capable(softp)
2445 *	Check if this IOSRAM instance is tunnel-capable by looing at
2446 *	"tunnel-capable" property.
2447 */
2448static int
2449iosram_tunnel_capable(struct iosramsoft *softp)
2450{
2451	int	proplen;
2452	int	tunnel_capable;
2453
2454	/*
2455	 * Look up IOSRAM_TUNNELOK_PROP property, if any.
2456	 */
2457	proplen = sizeof (tunnel_capable);
2458	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, softp->dip,
2459	    DDI_PROP_DONTPASS, IOSRAM_TUNNELOK_PROP, (caddr_t)&tunnel_capable,
2460	    &proplen) != DDI_PROP_SUCCESS) {
2461		tunnel_capable = 0;
2462	}
2463	return (tunnel_capable);
2464}
2465
2466
2467static int
2468iosram_sbbc_setup_map(struct iosramsoft *softp)
2469{
2470	int				rv;
2471	struct ddi_device_acc_attr	attr;
2472	dev_info_t			*dip = softp->dip;
2473	uint32_t			sema_val;
2474
2475	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
2476	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
2477	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
2478
2479	mutex_enter(&iosram_mutex);
2480	mutex_enter(&softp->intr_mutex);
2481
2482	/*
2483	 * Map SBBC region in
2484	 */
2485	if ((rv = ddi_regs_map_setup(dip, IOSRAM_SBBC_MAP_INDEX,
2486	    (caddr_t *)&softp->sbbc_region,
2487	    IOSRAM_SBBC_MAP_OFFSET, sizeof (iosram_sbbc_region_t),
2488	    &attr, &softp->sbbc_handle)) != DDI_SUCCESS) {
2489		DPRINTF(1, ("Failed to map SBBC region.\n"));
2490		mutex_exit(&softp->intr_mutex);
2491		mutex_exit(&iosram_mutex);
2492		return (rv);
2493	}
2494
2495	/*
2496	 * Disable SBBC interrupts. SBBC interrupts are enabled
2497	 * once the interrupt handler is registered.
2498	 */
2499	ddi_put32(softp->sbbc_handle,
2500	    &(softp->sbbc_region->int_enable.reg), 0x0);
2501
2502	/*
2503	 * Clear hardware semaphore value if appropriate.
2504	 * When the first SBBC is mapped in by the IOSRAM driver,
2505	 * the value of the semaphore should be initialized only
2506	 * if it is not held by SMS. For subsequent SBBC's, the
2507	 * semaphore will be always initialized.
2508	 */
2509	sema_val = IOSRAM_SEMA_RD(softp);
2510
2511	if (!iosram_master) {
2512		/* the first SBBC is being mapped in */
2513		if (!(IOSRAM_SEMA_IS_HELD(sema_val) &&
2514		    IOSRAM_SEMA_GET_IDX(sema_val) == IOSRAM_SEMA_SMS_IDX)) {
2515			/* not held by SMS, we clear the semaphore */
2516			IOSRAM_SEMA_WR(softp, 0);
2517		}
2518	} else {
2519		/* not the first SBBC, we clear the semaphore */
2520		IOSRAM_SEMA_WR(softp, 0);
2521	}
2522
2523	mutex_exit(&softp->intr_mutex);
2524	mutex_exit(&iosram_mutex);
2525	return (0);
2526}
2527
2528
2529static int
2530iosram_setup_map(struct iosramsoft *softp)
2531{
2532	int				instance = softp->instance;
2533	dev_info_t			*dip = softp->dip;
2534	int				portid;
2535	int				proplen;
2536	caddr_t				propvalue;
2537	struct ddi_device_acc_attr	attr;
2538
2539	attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
2540	attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
2541	attr.devacc_attr_endian_flags = DDI_NEVERSWAP_ACC;
2542
2543	/*
2544	 * Lookup IOSRAM_REG_PROP property to find out our IOSRAM length
2545	 */
2546	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
2547	    DDI_PROP_DONTPASS, IOSRAM_REG_PROP, (caddr_t)&propvalue,
2548	    &proplen) != DDI_PROP_SUCCESS) {
2549		cmn_err(CE_WARN, "iosram(%d): can't find register property.\n",
2550		    instance);
2551		return (DDI_FAILURE);
2552	} else {
2553		iosram_reg_t	*regprop = (iosram_reg_t *)propvalue;
2554
2555		DPRINTF(1, ("SetupMap(%d): Got reg prop: %x %x %x\n",
2556		    instance, regprop->addr_hi,
2557		    regprop->addr_lo, regprop->size));
2558
2559		softp->iosramlen = regprop->size;
2560
2561		kmem_free(propvalue, proplen);
2562	}
2563	DPRINTF(1, ("SetupMap(%d): IOSRAM length: 0x%x\n", instance,
2564	    softp->iosramlen));
2565	softp->handle = NULL;
2566
2567	/*
2568	 * To minimize boot time, we map the entire IOSRAM as opposed to
2569	 * mapping individual chunk via ddi_regs_map_setup() call.
2570	 */
2571	if (ddi_regs_map_setup(dip, 0, (caddr_t *)&softp->iosramp,
2572	    0x0, softp->iosramlen, &attr, &softp->handle) != DDI_SUCCESS) {
2573		cmn_err(CE_WARN, "iosram(%d): failed to map IOSRAM len:%x\n",
2574		    instance, softp->iosramlen);
2575		iosram_remove_map(softp);
2576		return (DDI_FAILURE);
2577	}
2578
2579	/*
2580	 * Lookup PORTID property on my parent hierarchy
2581	 */
2582	proplen = sizeof (portid);
2583	if (ddi_getlongprop_buf(DDI_DEV_T_ANY, dip,
2584	    0, IOSRAM_PORTID_PROP, (caddr_t)&portid,
2585	    &proplen) != DDI_PROP_SUCCESS) {
2586		cmn_err(CE_WARN, "iosram(%d): can't find portid property.\n",
2587		    instance);
2588		iosram_remove_map(softp);
2589		return (DDI_FAILURE);
2590	}
2591	softp->portid = portid;
2592
2593	if (iosram_sbbc_setup_map(softp) != DDI_SUCCESS) {
2594		cmn_err(CE_WARN, "iosram(%d): can't map SBBC region.\n",
2595		    instance);
2596		iosram_remove_map(softp);
2597		return (DDI_FAILURE);
2598	}
2599
2600	mutex_enter(&iosram_mutex);
2601	softp->state |= IOSRAM_STATE_MAPPED;
2602	mutex_exit(&iosram_mutex);
2603
2604	return (DDI_SUCCESS);
2605}
2606
2607
2608static void
2609iosram_remove_map(struct iosramsoft *softp)
2610{
2611	mutex_enter(&iosram_mutex);
2612
2613	ASSERT((softp->state & IOSRAM_STATE_MASTER) == 0);
2614
2615	if (softp->handle) {
2616		ddi_regs_map_free(&softp->handle);
2617		softp->handle = NULL;
2618	}
2619	softp->iosramp = NULL;
2620
2621	/*
2622	 * Umap SBBC registers region. Shared with handler for SBBC
2623	 * interrupts, take intr_mutex.
2624	 */
2625	mutex_enter(&softp->intr_mutex);
2626	if (softp->sbbc_region) {
2627		ddi_regs_map_free(&softp->sbbc_handle);
2628		softp->sbbc_region = NULL;
2629	}
2630	mutex_exit(&softp->intr_mutex);
2631
2632	softp->state &= ~IOSRAM_STATE_MAPPED;
2633
2634	mutex_exit(&iosram_mutex);
2635}
2636
2637
2638/*
2639 * iosram_is_chosen(struct iosramsoft *softp)
2640 *
2641 *	Looks up "chosen" node property to
2642 *	determine if it is the chosen IOSRAM.
2643 */
2644static int
2645iosram_is_chosen(struct iosramsoft *softp)
2646{
2647	char		chosen_iosram[MAXNAMELEN];
2648	char		pn[MAXNAMELEN];
2649	int		nodeid;
2650	int		chosen;
2651	pnode_t		dnode;
2652
2653	/*
2654	 * Get /chosen node info. prom interface will handle errors.
2655	 */
2656	dnode = prom_chosennode();
2657
2658	/*
2659	 * Look for the "iosram" property on the chosen node with a prom
2660	 * interface as ddi_find_devinfo() couldn't be used (calls
2661	 * ddi_walk_devs() that creates one extra lock on the device tree).
2662	 */
2663	if (prom_getprop(dnode, IOSRAM_CHOSEN_PROP, (caddr_t)&nodeid) <= 0) {
2664		/*
2665		 * Can't find IOSRAM_CHOSEN_PROP property under chosen node
2666		 */
2667		cmn_err(CE_WARN,
2668		    "iosram(%d): can't find chosen iosram property\n",
2669		    softp->instance);
2670		return (0);
2671	}
2672
2673	DPRINTF(1, ("iosram(%d): Got '%x' for chosen '%s' property\n",
2674	    softp->instance, nodeid, IOSRAM_CHOSEN_PROP));
2675
2676	/*
2677	 * get the full OBP pathname of this node
2678	 */
2679	if (prom_phandle_to_path((phandle_t)nodeid, chosen_iosram,
2680	    sizeof (chosen_iosram)) < 0) {
2681		cmn_err(CE_NOTE, "prom_phandle_to_path(%x) failed\n", nodeid);
2682		return (0);
2683	}
2684	DPRINTF(1, ("iosram(%d): prom_phandle_to_path(%x) is '%s'\n",
2685	    softp->instance, nodeid, chosen_iosram));
2686
2687	(void) ddi_pathname(softp->dip, pn);
2688	DPRINTF(1, ("iosram(%d): ddi_pathname(%p) is '%s'\n",
2689	    softp->instance, softp->dip, pn));
2690
2691	chosen = (strcmp(chosen_iosram, pn) == 0) ? 1 : 0;
2692	DPRINTF(1, ("iosram(%d): ... %s\n", softp->instance,
2693	    chosen ? "MASTER" : "SLAVE"));
2694	IOSRAMLOG(1, "iosram(%d): ... %s\n", softp->instance,
2695	    (chosen ? "MASTER" : "SLAVE"), NULL, NULL);
2696
2697	return (chosen);
2698}
2699
2700
2701/*
2702 * iosram_set_master(struct iosramsoft *softp)
2703 *
2704 *	Set master tunnel to the specified IOSRAM
2705 *	Must be called while holding iosram_mutex.
2706 */
2707static void
2708iosram_set_master(struct iosramsoft *softp)
2709{
2710	ASSERT(mutex_owned(&iosram_mutex));
2711	ASSERT(softp != NULL);
2712	ASSERT(softp->state & IOSRAM_STATE_MAPPED);
2713	ASSERT(IOSRAM_GET_HDRFIELD32(softp, status) == IOSRAM_VALID);
2714
2715	/*
2716	 * Clear MASTER flag on any previous IOSRAM master, if any
2717	 */
2718	if (iosram_master && (iosram_master != softp)) {
2719		iosram_master->state &= ~IOSRAM_STATE_MASTER;
2720	}
2721
2722	/*
2723	 * Setup new IOSRAM master
2724	 */
2725	iosram_update_addrs(softp);
2726	iosram_handle = softp->handle;
2727	softp->state |= IOSRAM_STATE_MASTER;
2728	softp->tswitch_ok++;
2729	iosram_master = softp;
2730
2731	IOSRAMLOG(1, "SETMASTER: softp:%p instance:%d\n", softp,
2732	    softp->instance, NULL, NULL);
2733}
2734
2735
2736/*
2737 * iosram_read_toc()
2738 *
2739 *	Read the TOC from an IOSRAM instance that has been mapped in.
2740 *	If the TOC is flawed or the IOSRAM isn't valid, return an error.
2741 */
2742static int
2743iosram_read_toc(struct iosramsoft *softp)
2744{
2745	int			i;
2746	int			instance = softp->instance;
2747	uint8_t			*toc_entryp;
2748	iosram_flags_t		*flagsp = NULL;
2749	int			new_nchunks;
2750	iosram_chunk_t		*new_chunks;
2751	iosram_chunk_t		*chunkp;
2752	iosram_chunk_t		*old_chunkp;
2753	iosram_toc_entry_t	index;
2754
2755	/*
2756	 * Never try to read the TOC out of an unmapped IOSRAM.
2757	 */
2758	ASSERT(softp->state & IOSRAM_STATE_MAPPED);
2759
2760	mutex_enter(&iosram_mutex);
2761
2762	/*
2763	 * Check to make sure this IOSRAM is marked valid.  Return
2764	 * an error if it isn't.
2765	 */
2766	if (IOSRAM_GET_HDRFIELD32(softp, status) != IOSRAM_VALID) {
2767		DPRINTF(1, ("iosram_read_toc(%d): IOSRAM not flagged valid\n",
2768		    instance));
2769		mutex_exit(&iosram_mutex);
2770		return (EINVAL);
2771	}
2772
2773	/*
2774	 * Get the location of the TOC.
2775	 */
2776	toc_entryp = softp->iosramp + IOSRAM_GET_HDRFIELD32(softp, toc_offset);
2777
2778	/*
2779	 * Read the index entry from the TOC and make sure it looks correct.
2780	 */
2781	ddi_rep_get8(softp->handle, (uint8_t *)&index, toc_entryp,
2782	    sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR);
2783	if ((index.key != IOSRAM_INDEX_KEY) ||
2784	    (index.off != IOSRAM_INDEX_OFF)) {
2785		cmn_err(CE_WARN, "iosram(%d): invalid TOC index.\n", instance);
2786		mutex_exit(&iosram_mutex);
2787		return (EINVAL);
2788	}
2789
2790	/*
2791	 * Allocate storage for the new chunks array and initialize it with data
2792	 * from the TOC and callback data from the corresponding old chunk, if
2793	 * it exists.
2794	 */
2795	new_nchunks = index.len - 1;
2796	new_chunks = (iosram_chunk_t *)kmem_zalloc(new_nchunks *
2797	    sizeof (iosram_chunk_t), KM_SLEEP);
2798	for (i = 0, chunkp = new_chunks; i < new_nchunks; i++, chunkp++) {
2799		toc_entryp += sizeof (iosram_toc_entry_t);
2800		ddi_rep_get8(softp->handle, (uint8_t *)&(chunkp->toc_data),
2801		    toc_entryp, sizeof (iosram_toc_entry_t), DDI_DEV_AUTOINCR);
2802		chunkp->hash = NULL;
2803		if ((chunkp->toc_data.off < softp->iosramlen) &&
2804		    (chunkp->toc_data.len <= softp->iosramlen) &&
2805		    ((chunkp->toc_data.off + chunkp->toc_data.len) <=
2806		    softp->iosramlen)) {
2807			chunkp->basep = softp->iosramp + chunkp->toc_data.off;
2808			DPRINTF(1,
2809			    ("iosram_read_toc(%d): k:%x o:%x l:%x p:%x\n",
2810			    instance, chunkp->toc_data.key,
2811			    chunkp->toc_data.off, chunkp->toc_data.len,
2812			    chunkp->basep));
2813		} else {
2814			cmn_err(CE_WARN, "iosram(%d): TOC entry %d"
2815			    "out of range... off:%x  len:%x\n",
2816			    instance, i + 1, chunkp->toc_data.off,
2817			    chunkp->toc_data.len);
2818			kmem_free(new_chunks, new_nchunks *
2819			    sizeof (iosram_chunk_t));
2820			mutex_exit(&iosram_mutex);
2821			return (EINVAL);
2822		}
2823
2824		/*
2825		 * Note the existence of the flags chunk, which is required in
2826		 * a correct TOC.
2827		 */
2828		if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
2829			flagsp = (iosram_flags_t *)chunkp->basep;
2830		}
2831
2832		/*
2833		 * If there was an entry for this chunk in the old list, copy
2834		 * the callback data from old to new storage.
2835		 */
2836		if ((nchunks > 0) &&
2837		    ((old_chunkp = iosram_find_chunk(chunkp->toc_data.key)) !=
2838		    NULL)) {
2839			bcopy(&(old_chunkp->cback), &(chunkp->cback),
2840			    sizeof (iosram_cback_t));
2841		}
2842	}
2843	/*
2844	 * The TOC is malformed if there is no entry for the flags chunk.
2845	 */
2846	if (flagsp == NULL) {
2847		kmem_free(new_chunks, new_nchunks * sizeof (iosram_chunk_t));
2848		mutex_exit(&iosram_mutex);
2849		return (EINVAL);
2850	}
2851
2852	/*
2853	 * Free any memory that is no longer needed and install the new data
2854	 * as current data.
2855	 */
2856	if (chunks != NULL) {
2857		kmem_free(chunks, nchunks * sizeof (iosram_chunk_t));
2858	}
2859	chunks = new_chunks;
2860	nchunks = new_nchunks;
2861	iosram_init_hashtab();
2862
2863	mutex_exit(&iosram_mutex);
2864	return (0);
2865}
2866
2867
2868/*
2869 * iosram_init_hashtab()
2870 *
2871 *	Initialize the hash table and populate it with the IOSRAM
2872 *	chunks previously read from the TOC.  The caller must hold the
2873 *	ioram_mutex lock.
2874 */
2875static void
2876iosram_init_hashtab(void)
2877{
2878	int		i, bucket;
2879	iosram_chunk_t	*chunkp;
2880
2881	ASSERT(mutex_owned(&iosram_mutex));
2882
2883	for (i = 0; i < IOSRAM_HASHSZ; i++) {
2884		iosram_hashtab[i] = NULL;
2885	}
2886
2887	if (chunks) {
2888		for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
2889			/*
2890			 * Hide the flags chunk by leaving it out of the hash
2891			 * table.
2892			 */
2893			if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
2894				continue;
2895			}
2896
2897			/*
2898			 * Add the current chunk to the hash table.
2899			 */
2900			bucket = IOSRAM_HASH(chunkp->toc_data.key);
2901			chunkp->hash = iosram_hashtab[bucket];
2902			iosram_hashtab[bucket] = chunkp;
2903		}
2904	}
2905}
2906
2907
2908/*
2909 * iosram_update_addrs()
2910 *
2911 *	Process the chunk list, updating each chunk's basep, which is a pointer
2912 *	to the beginning of the chunk's memory in kvaddr space.  Record the
2913 *	basep value of the flags chunk to speed up flag access.  The caller
2914 *	must hold the iosram_mutex lock.
2915 */
2916static void
2917iosram_update_addrs(struct iosramsoft *softp)
2918{
2919	int		i;
2920	iosram_flags_t	*flagsp;
2921	iosram_chunk_t	*chunkp;
2922
2923	ASSERT(mutex_owned(&iosram_mutex));
2924
2925	/*
2926	 * First go through all of the chunks updating their base pointers and
2927	 * looking for the flags chunk.
2928	 */
2929	for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
2930		chunkp->basep = softp->iosramp + chunkp->toc_data.off;
2931		if (chunkp->toc_data.key == IOSRAM_FLAGS_KEY) {
2932			flagsp = (iosram_flags_t *)(chunkp->basep);
2933			DPRINTF(1,
2934			    ("iosram_update_addrs flags: o:0x%08x p:%p",
2935			    chunkp->toc_data.off, flagsp));
2936		}
2937	}
2938
2939	/*
2940	 * Now, go through and update each chunk's flags pointer.  This can't be
2941	 * done in the first loop because we don't have the address of the flags
2942	 * chunk yet.
2943	 */
2944	for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
2945		chunkp->flagsp = flagsp++;
2946		DPRINTF(1, ("iosram_update_addrs: k:0x%x f:%p\n",
2947		    chunkp->toc_data.key, chunkp->flagsp));
2948	}
2949}
2950
2951/*
2952 * iosram_find_chunk(key)
2953 *
2954 *	Return a pointer to iosram_chunk structure corresponding to the
2955 *	"key" IOSRAM chunk.  The caller must hold the iosram_mutex lock.
2956 */
2957static iosram_chunk_t *
2958iosram_find_chunk(uint32_t key)
2959{
2960	iosram_chunk_t	*chunkp;
2961	int		index = IOSRAM_HASH(key);
2962
2963	ASSERT(mutex_owned(&iosram_mutex));
2964
2965	for (chunkp = iosram_hashtab[index]; chunkp; chunkp = chunkp->hash) {
2966		if (chunkp->toc_data.key == key) {
2967			break;
2968		}
2969	}
2970
2971	return (chunkp);
2972}
2973
2974
2975/*
2976 * iosram_add_intr(iosramsoft_t *)
2977 */
2978static int
2979iosram_add_intr(iosramsoft_t *softp)
2980{
2981	IOSRAMLOG(2, "ADDINTR: softp:%p  instance:%d\n",
2982	    softp, softp->instance, NULL, NULL);
2983
2984	if (ddi_add_softintr(softp->dip, DDI_SOFTINT_MED,
2985	    &softp->softintr_id, &softp->soft_iblk, NULL,
2986	    iosram_softintr, (caddr_t)softp) != DDI_SUCCESS) {
2987		cmn_err(CE_WARN,
2988		    "iosram(%d): Can't register softintr.\n",
2989		    softp->instance);
2990		return (DDI_FAILURE);
2991	}
2992
2993	if (ddi_add_intr(softp->dip, 0, &softp->real_iblk, NULL,
2994	    iosram_intr, (caddr_t)softp) != DDI_SUCCESS) {
2995		cmn_err(CE_WARN,
2996		    "iosram(%d): Can't register intr"
2997		    " handler.\n", softp->instance);
2998		ddi_remove_softintr(softp->softintr_id);
2999		return (DDI_FAILURE);
3000	}
3001
3002	/*
3003	 * Enable SBBC interrupts
3004	 */
3005	ddi_put32(softp->sbbc_handle, &(softp->sbbc_region->int_enable.reg),
3006	    IOSRAM_SBBC_INT0|IOSRAM_SBBC_INT1);
3007
3008	return (DDI_SUCCESS);
3009}
3010
3011
3012/*
3013 * iosram_remove_intr(iosramsoft_t *)
3014 */
3015static int
3016iosram_remove_intr(iosramsoft_t *softp)
3017{
3018	IOSRAMLOG(2, "REMINTR: softp:%p  instance:%d\n",
3019	    softp, softp->instance, NULL, NULL);
3020
3021	/*
3022	 * Disable SBBC interrupts if SBBC is mapped in
3023	 */
3024	if (softp->sbbc_region) {
3025		ddi_put32(softp->sbbc_handle,
3026		    &(softp->sbbc_region->int_enable.reg), 0);
3027	}
3028
3029	/*
3030	 * Remove SBBC interrupt handler
3031	 */
3032	ddi_remove_intr(softp->dip, 0, softp->real_iblk);
3033
3034	/*
3035	 * Remove soft interrupt handler
3036	 */
3037	mutex_enter(&iosram_mutex);
3038	if (softp->softintr_id != NULL) {
3039		ddi_remove_softintr(softp->softintr_id);
3040		softp->softintr_id = NULL;
3041	}
3042	mutex_exit(&iosram_mutex);
3043
3044	return (0);
3045}
3046
3047
3048/*
3049 * iosram_add_instance(iosramsoft_t *)
3050 * Must be called while holding iosram_mutex
3051 */
3052static void
3053iosram_add_instance(iosramsoft_t *new_softp)
3054{
3055#ifdef DEBUG
3056	int		instance = new_softp->instance;
3057	iosramsoft_t	*softp;
3058#endif
3059
3060	ASSERT(mutex_owned(&iosram_mutex));
3061
3062#if defined(DEBUG)
3063	/* Verify that this instance is not in the list */
3064	for (softp = iosram_instances; softp != NULL; softp = softp->next) {
3065		ASSERT(softp->instance != instance);
3066	}
3067#endif
3068
3069	/*
3070	 * Add this instance to the list
3071	 */
3072	if (iosram_instances != NULL) {
3073		iosram_instances->prev = new_softp;
3074	}
3075	new_softp->next = iosram_instances;
3076	new_softp->prev = NULL;
3077	iosram_instances = new_softp;
3078}
3079
3080
3081/*
3082 * iosram_remove_instance(int instance)
3083 * Must be called while holding iosram_mutex
3084 */
3085static void
3086iosram_remove_instance(int instance)
3087{
3088	iosramsoft_t *softp;
3089
3090	/*
3091	 * Remove specified instance from the iosram_instances list so that
3092	 * it can't be chosen for tunnel in future.
3093	 */
3094	ASSERT(mutex_owned(&iosram_mutex));
3095
3096	for (softp = iosram_instances; softp != NULL; softp = softp->next) {
3097		if (softp->instance == instance) {
3098			if (softp->next != NULL) {
3099				softp->next->prev = softp->prev;
3100			}
3101			if (softp->prev != NULL) {
3102				softp->prev->next = softp->next;
3103			}
3104			if (iosram_instances == softp) {
3105				iosram_instances = softp->next;
3106			}
3107
3108			return;
3109		}
3110	}
3111}
3112
3113
3114/*
3115 * iosram_sema_acquire: Acquire hardware semaphore.
3116 * Return 0 if the semaphore could be acquired, or one of the following
3117 * possible values:
3118 * EAGAIN: there is a tunnel switch in progress
3119 * EBUSY: the semaphore was already "held"
3120 * ENXIO:  an IO error occured (e.g. SBBC not mapped)
3121 * If old_value is not NULL, the location it points to will be updated
3122 * with the semaphore value read when attempting to acquire it.
3123 */
3124int
3125iosram_sema_acquire(uint32_t *old_value)
3126{
3127	struct iosramsoft	*softp;
3128	int			rv;
3129	uint32_t		sema_val;
3130
3131	DPRINTF(2, ("IOSRAM: in iosram_sema_acquire\n"));
3132
3133	mutex_enter(&iosram_mutex);
3134
3135	/*
3136	 * Disallow access if there is a tunnel switch in progress.
3137	 */
3138	if (iosram_tswitch_active) {
3139		mutex_exit(&iosram_mutex);
3140		return (EAGAIN);
3141	}
3142
3143	/*
3144	 * Use current master IOSRAM for operation, fail if none is
3145	 * currently active.
3146	 */
3147	if ((softp = iosram_master) == NULL) {
3148		mutex_exit(&iosram_mutex);
3149		DPRINTF(1, ("IOSRAM: iosram_sema_acquire: no master\n"));
3150		return (ENXIO);
3151	}
3152
3153	mutex_enter(&softp->intr_mutex);
3154
3155	/*
3156	 * Fail if SBBC region has not been mapped. This shouldn't
3157	 * happen if we have a master IOSRAM, but we double-check.
3158	 */
3159	if (softp->sbbc_region == NULL) {
3160		mutex_exit(&softp->intr_mutex);
3161		mutex_exit(&iosram_mutex);
3162		DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: "
3163		    "SBBC not mapped\n", softp->instance));
3164		return (ENXIO);
3165	}
3166
3167	/* read semaphore value */
3168	sema_val = IOSRAM_SEMA_RD(softp);
3169	if (old_value != NULL)
3170		*old_value = sema_val;
3171
3172	if (IOSRAM_SEMA_IS_HELD(sema_val)) {
3173		/* semaphore was held by someone else */
3174		rv = EBUSY;
3175	} else {
3176		/* semaphore was not held, we just acquired it */
3177		rv = 0;
3178	}
3179
3180	mutex_exit(&softp->intr_mutex);
3181	mutex_exit(&iosram_mutex);
3182
3183	DPRINTF(1, ("IOSRAM(%d): iosram_sema_acquire: "
3184	    "old value=0x%x rv=%d\n", softp->instance, sema_val, rv));
3185
3186	return (rv);
3187}
3188
3189
3190/*
3191 * iosram_sema_release: Release hardware semaphore.
3192 * This function will "release" the hardware semaphore, and return 0 on
3193 * success. If an error occured, one of the following values will be
3194 * returned:
3195 * EAGAIN: there is a tunnel switch in progress
3196 * ENXIO:  an IO error occured (e.g. SBBC not mapped)
3197 */
3198int
3199iosram_sema_release(void)
3200{
3201	struct iosramsoft	*softp;
3202
3203	DPRINTF(2, ("IOSRAM: in iosram_sema_release\n"));
3204
3205	mutex_enter(&iosram_mutex);
3206
3207	/*
3208	 * Disallow access if there is a tunnel switch in progress.
3209	 */
3210	if (iosram_tswitch_active) {
3211		mutex_exit(&iosram_mutex);
3212		return (EAGAIN);
3213	}
3214
3215	/*
3216	 * Use current master IOSRAM for operation, fail if none is
3217	 * currently active.
3218	 */
3219	if ((softp = iosram_master) == NULL) {
3220		mutex_exit(&iosram_mutex);
3221		DPRINTF(1, ("IOSRAM: iosram_sema_release: no master\n"));
3222		return (ENXIO);
3223	}
3224
3225	mutex_enter(&softp->intr_mutex);
3226
3227	/*
3228	 * Fail if SBBC region has not been mapped in. This shouldn't
3229	 * happen if we have a master IOSRAM, but we double-check.
3230	 */
3231	if (softp->sbbc_region == NULL) {
3232		mutex_exit(&softp->intr_mutex);
3233		mutex_exit(&iosram_mutex);
3234		DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: "
3235		    "SBBC not mapped\n", softp->instance));
3236		return (ENXIO);
3237	}
3238
3239	/* Release semaphore by clearing our semaphore register */
3240	IOSRAM_SEMA_WR(softp, 0);
3241
3242	mutex_exit(&softp->intr_mutex);
3243	mutex_exit(&iosram_mutex);
3244
3245	DPRINTF(1, ("IOSRAM(%d): iosram_sema_release: success\n",
3246	    softp->instance));
3247
3248	return (0);
3249}
3250
3251
3252#if defined(IOSRAM_LOG)
3253void
3254iosram_log(caddr_t fmt, intptr_t a1, intptr_t a2, intptr_t a3, intptr_t a4)
3255{
3256	uint32_t	seq;
3257	iosram_log_t	*logp;
3258
3259	mutex_enter(&iosram_log_mutex);
3260
3261	seq = iosram_logseq++;
3262	logp = &iosram_logbuf[seq % IOSRAM_MAXLOG];
3263	logp->seq = seq;
3264	logp->tstamp = lbolt;
3265	logp->fmt = fmt;
3266	logp->arg1 = a1;
3267	logp->arg2 = a2;
3268	logp->arg3 = a3;
3269	logp->arg4 = a4;
3270
3271	mutex_exit(&iosram_log_mutex);
3272
3273	if (iosram_log_print) {
3274		cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp);
3275		if (logp->fmt) {
3276			cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2,
3277			    logp->arg3, logp->arg4);
3278			if (logp->fmt[strlen(logp->fmt)-1] != '\n') {
3279				cmn_err(CE_CONT, "\n");
3280			}
3281		} else {
3282			cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n",
3283			    logp->fmt, logp->arg1, logp->arg2, logp->arg3,
3284			    logp->arg4);
3285		}
3286	}
3287}
3288#endif /* IOSRAM_LOG */
3289
3290
3291#if defined(DEBUG)
3292/*
3293 * iosram_get_keys(buf, len)
3294 *	Return IOSRAM TOC in the specified buffer
3295 */
3296static int
3297iosram_get_keys(iosram_toc_entry_t *bufp, uint32_t *len)
3298{
3299	struct iosram_chunk	*chunkp;
3300	int			error = 0;
3301	int			i;
3302	int			cnt = (*len) / sizeof (iosram_toc_entry_t);
3303
3304	IOSRAMLOG(2, "iosram_get_keys(bufp:%p *len:%x)\n", bufp, *len, NULL,
3305	    NULL);
3306
3307	/*
3308	 * Copy data while holding the lock to prevent any data
3309	 * corruption or invalid pointer dereferencing.
3310	 */
3311	mutex_enter(&iosram_mutex);
3312
3313	if (iosram_master == NULL) {
3314		error = EIO;
3315	} else {
3316		for (i = 0, chunkp = chunks; i < nchunks && i < cnt;
3317		    i++, chunkp++) {
3318			bufp[i].key = chunkp->toc_data.key;
3319			bufp[i].off = chunkp->toc_data.off;
3320			bufp[i].len = chunkp->toc_data.len;
3321			bufp[i].unused = chunkp->toc_data.unused;
3322		}
3323		*len = i * sizeof (iosram_toc_entry_t);
3324	}
3325
3326	mutex_exit(&iosram_mutex);
3327	return (error);
3328}
3329
3330
3331/*
3332 * iosram_print_state(instance)
3333 */
3334static void
3335iosram_print_state(int instance)
3336{
3337	struct iosramsoft	*softp;
3338	char			pn[MAXNAMELEN];
3339
3340	if (instance < 0) {
3341		softp = iosram_master;
3342	} else {
3343		softp = ddi_get_soft_state(iosramsoft_statep, instance);
3344	}
3345
3346	if (softp == NULL) {
3347		cmn_err(CE_CONT, "iosram_print_state: Can't find instance %d\n",
3348		    instance);
3349		return;
3350	}
3351	instance = softp->instance;
3352
3353	mutex_enter(&iosram_mutex);
3354	mutex_enter(&softp->intr_mutex);
3355
3356	cmn_err(CE_CONT, "iosram_print_state(%d): ... %s\n", instance,
3357	    ((softp == iosram_master) ? "MASTER" : "SLAVE"));
3358
3359	(void) ddi_pathname(softp->dip, pn);
3360	cmn_err(CE_CONT, "  pathname:%s\n", pn);
3361	cmn_err(CE_CONT, "  instance:%d  portid:%d iosramlen:0x%x\n",
3362	    softp->instance, softp->portid, softp->iosramlen);
3363	cmn_err(CE_CONT, "  softp:%p  handle:%p  iosramp:%p\n", softp,
3364	    softp->handle, softp->iosramp);
3365	cmn_err(CE_CONT, "  state:0x%x  tswitch_ok:%x  tswitch_fail:%x\n",
3366	    softp->state, softp->tswitch_ok, softp->tswitch_fail);
3367	cmn_err(CE_CONT, "  softintr_id:%p  intr_busy:%x  intr_pending:%x\n",
3368	    softp->softintr_id, softp->intr_busy, softp->intr_pending);
3369
3370	mutex_exit(&softp->intr_mutex);
3371	mutex_exit(&iosram_mutex);
3372}
3373
3374
3375/*
3376 * iosram_print_stats()
3377 */
3378static void
3379iosram_print_stats()
3380{
3381	uint32_t	calls;
3382
3383	cmn_err(CE_CONT, "iosram_stats:\n");
3384	calls = iosram_stats.read;
3385	cmn_err(CE_CONT, " read  ... calls:%x  bytes:%lx  avg_sz:%x\n",
3386	    calls, iosram_stats.bread,
3387	    (uint32_t)((calls != 0) ? (iosram_stats.bread/calls) : 0));
3388
3389	calls = iosram_stats.write;
3390	cmn_err(CE_CONT, " write ... calls:%x  bytes:%lx  avg_sz:%x\n",
3391	    calls, iosram_stats.bwrite,
3392	    (uint32_t)((calls != 0) ? (iosram_stats.bwrite/calls) : 0));
3393
3394	cmn_err(CE_CONT, " intr recv (real:%x  soft:%x)  sent:%x  cback:%x\n",
3395	    iosram_stats.intr_recv, iosram_stats.sintr_recv,
3396	    iosram_stats.intr_send, iosram_stats.callbacks);
3397
3398	cmn_err(CE_CONT, " tswitch: %x  getflag:%x  setflag:%x\n",
3399	    iosram_stats.tswitch, iosram_stats.getflag,
3400	    iosram_stats.setflag);
3401
3402	cmn_err(CE_CONT, " iosram_rw_active_max: %x\n", iosram_rw_active_max);
3403}
3404
3405
3406static void
3407iosram_print_cback()
3408{
3409	iosram_chunk_t	*chunkp;
3410	int		i;
3411
3412	/*
3413	 * Print callback handlers
3414	 */
3415	mutex_enter(&iosram_mutex);
3416
3417	cmn_err(CE_CONT, "IOSRAM callbacks:\n");
3418	for (i = 0, chunkp = chunks; i < nchunks; i++, chunkp++) {
3419		if (chunkp->cback.handler) {
3420			cmn_err(CE_CONT, "  %2d: key:0x%x  hdlr:%p  arg:%p "
3421			    "busy:%d unreg:%d\n", i, chunkp->toc_data.key,
3422			    chunkp->cback.handler, chunkp->cback.arg,
3423			    chunkp->cback.busy, chunkp->cback.unregister);
3424		}
3425	}
3426	mutex_exit(&iosram_mutex);
3427}
3428
3429
3430static void
3431iosram_print_flags()
3432{
3433	int		i;
3434	uint32_t	*keys;
3435	iosram_flags_t	*flags;
3436
3437	mutex_enter(&iosram_mutex);
3438
3439	if (iosram_master == NULL) {
3440		mutex_exit(&iosram_mutex);
3441		cmn_err(CE_CONT, "IOSRAM Flags: not accessible\n");
3442		return;
3443	}
3444
3445	keys = kmem_alloc(nchunks * sizeof (uint32_t), KM_SLEEP);
3446	flags = kmem_alloc(nchunks * sizeof (iosram_flags_t), KM_SLEEP);
3447
3448	for (i = 0; i < nchunks; i++) {
3449		keys[i] = chunks[i].toc_data.key;
3450		ddi_rep_get8(iosram_handle, (uint8_t *)&(flags[i]),
3451		    (uint8_t *)(chunks[i].flagsp), sizeof (iosram_flags_t),
3452		    DDI_DEV_AUTOINCR);
3453	}
3454
3455	mutex_exit(&iosram_mutex);
3456
3457	cmn_err(CE_CONT, "IOSRAM Flags:\n");
3458	for (i = 0; i < nchunks; i++) {
3459		cmn_err(CE_CONT,
3460		    "  %2d: key: 0x%x  data_valid:%x  int_pending:%x\n",
3461		    i, keys[i], flags[i].data_valid, flags[i].int_pending);
3462	}
3463
3464	kmem_free(keys, nchunks * sizeof (uint32_t));
3465	kmem_free(flags, nchunks * sizeof (iosram_flags_t));
3466}
3467
3468
3469/*PRINTFLIKE1*/
3470static void
3471iosram_dprintf(const char *fmt, ...)
3472{
3473	char	msg_buf[256];
3474	va_list	adx;
3475
3476	va_start(adx, fmt);
3477	vsprintf(msg_buf, fmt, adx);
3478	va_end(adx);
3479
3480	cmn_err(CE_CONT, "%s", msg_buf);
3481}
3482#endif /* DEBUG */
3483
3484
3485#if IOSRAM_LOG
3486/*
3487 * iosram_print_log(int cnt)
3488 *	Print last few entries of the IOSRAM log in reverse order
3489 */
3490static void
3491iosram_print_log(int cnt)
3492{
3493	int	i;
3494
3495	if (cnt <= 0) {
3496		cnt = 20;
3497	} else if (cnt > IOSRAM_MAXLOG) {
3498		cnt = IOSRAM_MAXLOG;
3499	}
3500
3501
3502	cmn_err(CE_CONT,
3503	    "\niosram_logseq: 0x%x  lbolt: %lx  iosram_log_level:%x\n",
3504	    iosram_logseq, lbolt, iosram_log_level);
3505	cmn_err(CE_CONT, "iosram_logbuf: %p  max entries:0x%x\n",
3506	    iosram_logbuf, IOSRAM_MAXLOG);
3507	for (i = iosram_logseq;  --i >= 0 && --cnt >= 0; ) {
3508		iosram_log_t	*logp;
3509
3510		mutex_enter(&iosram_log_mutex);
3511
3512		logp = &iosram_logbuf[i %IOSRAM_MAXLOG];
3513		cmn_err(CE_CONT, "#%x @%lx ", logp->seq, logp->tstamp);
3514
3515		if (logp->fmt) {
3516			cmn_err(CE_CONT, logp->fmt, logp->arg1, logp->arg2,
3517			    logp->arg3, logp->arg4);
3518			if (logp->fmt[strlen(logp->fmt)-1] != '\n') {
3519				cmn_err(CE_CONT, "\n");
3520			}
3521		} else {
3522			cmn_err(CE_CONT, "fmt:%p args: %lx %lx %lx %lx\n",
3523			    logp->fmt, logp->arg1, logp->arg2,
3524			    logp->arg3, logp->arg4);
3525		}
3526
3527		mutex_exit(&iosram_log_mutex);
3528	}
3529}
3530#endif	/* IOSRAM_LOG */
3531