xref: /illumos-gate/usr/src/uts/sun4u/serengeti/io/sgsbbc_mailbox.c (revision 07d06da50d310a325b457d6330165aebab1e0064)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
23*07d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel /*
2803831d35Sstevel  * Interface for Serengeti IOSRAM mailbox
2903831d35Sstevel  * OS <-> SC communication protocol
3003831d35Sstevel  */
3103831d35Sstevel 
3203831d35Sstevel #include <sys/types.h>
3303831d35Sstevel #include <sys/systm.h>
3403831d35Sstevel #include <sys/ddi.h>
3503831d35Sstevel #include <sys/sunddi.h>
3603831d35Sstevel #include <sys/kmem.h>
3703831d35Sstevel #include <sys/uadmin.h>
3803831d35Sstevel #include <sys/machsystm.h>
3903831d35Sstevel #include <sys/disp.h>
4003831d35Sstevel #include <sys/taskq.h>
4103831d35Sstevel 
4203831d35Sstevel #include <sys/sgevents.h>
4303831d35Sstevel #include <sys/sgsbbc_priv.h>
4403831d35Sstevel #include <sys/sgsbbc_iosram_priv.h>
4503831d35Sstevel #include <sys/sgsbbc_mailbox_priv.h>
4603831d35Sstevel #include <sys/plat_ecc_unum.h>
4703831d35Sstevel #include <sys/plat_ecc_dimm.h>
4803831d35Sstevel #include <sys/serengeti.h>
4903831d35Sstevel #include <sys/fm/util.h>
5003831d35Sstevel #include <sys/promif.h>
5103831d35Sstevel #include <sys/plat_datapath.h>
5203831d35Sstevel 
5303831d35Sstevel sbbc_mailbox_t	*master_mbox = NULL;
5403831d35Sstevel 
5503831d35Sstevel /*
5603831d35Sstevel  * Panic Shutdown event support
5703831d35Sstevel  */
5803831d35Sstevel static	kmutex_t	panic_hdlr_lock;
5903831d35Sstevel 
6003831d35Sstevel /*
6103831d35Sstevel  * The ID of the soft interrupt which triggers the bringing down of a Domain
6203831d35Sstevel  * when a PANIC_SHUTDOWN event is received.
6303831d35Sstevel  */
6403831d35Sstevel static ddi_softintr_t	panic_softintr_id = 0;
6503831d35Sstevel 
6603831d35Sstevel static sg_panic_shutdown_t	panic_payload;
6703831d35Sstevel static sbbc_msg_t		panic_payload_msg;
6803831d35Sstevel 
6903831d35Sstevel /*
7003831d35Sstevel  * A queue for making sure outgoing messages are in order as ScApp
7103831d35Sstevel  * does not support interleaving messages.
7203831d35Sstevel  */
7303831d35Sstevel static kcondvar_t	outbox_queue;
7403831d35Sstevel static kmutex_t		outbox_queue_lock;
7503831d35Sstevel 
7603831d35Sstevel /*
7703831d35Sstevel  * Handle unsolicited capability message.
7803831d35Sstevel  */
7903831d35Sstevel static plat_capability_data_t	cap_payload;
8003831d35Sstevel static sbbc_msg_t		cap_payload_msg;
8103831d35Sstevel static kmutex_t			cap_msg_hdlr_lock;
8203831d35Sstevel 
8303831d35Sstevel /*
8403831d35Sstevel  * Datapath error and fault messages arrive unsolicited.  The message data
8503831d35Sstevel  * is contained in a plat_datapath_info_t structure.
8603831d35Sstevel  */
8703831d35Sstevel typedef struct {
8803831d35Sstevel 	uint8_t		type;		/* CDS, DX, CP */
8903831d35Sstevel 	uint8_t		pad;		/* for alignment */
9003831d35Sstevel 	uint16_t	cpuid;		/* Safari ID of base CPU */
9103831d35Sstevel 	uint32_t	t_value;	/* SERD timeout threshold (seconds) */
9203831d35Sstevel } plat_datapath_info_t;
9303831d35Sstevel 
9403831d35Sstevel /*
9503831d35Sstevel  * Unsolicited datapath error messages are processed via a soft interrupt,
9603831d35Sstevel  * triggered in unsolicited interrupt processing.
9703831d35Sstevel  */
9803831d35Sstevel static	ddi_softintr_t		dp_softintr_id = 0;
9903831d35Sstevel static	kmutex_t		dp_hdlr_lock;
10003831d35Sstevel 
10103831d35Sstevel static	plat_datapath_info_t	dp_payload;
10203831d35Sstevel static	sbbc_msg_t		dp_payload_msg;
10303831d35Sstevel 
10403831d35Sstevel static char *dperrtype[] = {
10503831d35Sstevel 	DP_ERROR_CDS,
10603831d35Sstevel 	DP_ERROR_DX,
10703831d35Sstevel 	DP_ERROR_RP
10803831d35Sstevel };
10903831d35Sstevel 
11003831d35Sstevel /*
11103831d35Sstevel  * Variable indicating if we are already processing requests.
11203831d35Sstevel  * Setting this value must be protected by outbox_queue_lock.
11303831d35Sstevel  */
11403831d35Sstevel static int		outbox_busy = 0;
11503831d35Sstevel 
11603831d35Sstevel /*
11703831d35Sstevel  * local stuff
11803831d35Sstevel  */
11903831d35Sstevel static int sbbc_mbox_send_msg(sbbc_msg_t *, int, uint_t, time_t, clock_t);
12003831d35Sstevel static int sbbc_mbox_recv_msg();
12103831d35Sstevel static int mbox_write(struct sbbc_mbox_header *,
12203831d35Sstevel 	struct sbbc_fragment *, sbbc_msg_t *);
12303831d35Sstevel static int mbox_read(struct sbbc_mbox_header *, struct sbbc_fragment *,
12403831d35Sstevel 	sbbc_msg_t *);
12503831d35Sstevel static int mbox_has_free_space(struct sbbc_mbox_header *);
12603831d35Sstevel static void mbox_skip_next_msg(struct sbbc_mbox_header *);
12703831d35Sstevel static int mbox_read_header(uint32_t, struct sbbc_mbox_header *);
12803831d35Sstevel static void mbox_update_header(uint32_t, struct sbbc_mbox_header *);
12903831d35Sstevel static int mbox_read_frag(struct sbbc_mbox_header *, struct sbbc_fragment *);
13003831d35Sstevel static struct sbbc_msg_waiter *mbox_find_waiter(uint16_t, uint32_t);
13103831d35Sstevel static void wakeup_next(void);
13203831d35Sstevel static uint_t sbbc_panic_shutdown_handler(char *arg);
13303831d35Sstevel static uint_t sbbc_do_fast_shutdown(char *arg);
13403831d35Sstevel static void sbbc_mbox_post_reg(sbbc_softstate_t *softsp);
13503831d35Sstevel static uint_t cap_ecc_msg_handler(char *);
13603831d35Sstevel static uint_t sbbc_datapath_error_msg_handler(char *arg);
13703831d35Sstevel static uint_t sbbc_datapath_fault_msg_handler(char *arg);
13803831d35Sstevel static uint_t sbbc_dp_trans_event(char *arg);
13903831d35Sstevel 
14003831d35Sstevel 
14103831d35Sstevel /*
14203831d35Sstevel  * Interrupt handlers
14303831d35Sstevel  */
14403831d35Sstevel static int sbbc_mbox_msgin(void);
14503831d35Sstevel static int sbbc_mbox_msgout(void);
14603831d35Sstevel static int sbbc_mbox_spacein(void);
14703831d35Sstevel static int sbbc_mbox_spaceout(void);
14803831d35Sstevel 
14903831d35Sstevel /*
15003831d35Sstevel  * ECC event mailbox message taskq and parameters
15103831d35Sstevel  */
15203831d35Sstevel static taskq_t	*sbbc_ecc_mbox_taskq = NULL;
15303831d35Sstevel static int	sbbc_ecc_mbox_taskq_errs = 0;
15403831d35Sstevel static int	sbbc_ecc_mbox_send_errs = 0;
15503831d35Sstevel static int	sbbc_ecc_mbox_inval_errs = 0;
15603831d35Sstevel static int	sbbc_ecc_mbox_other_errs = 0;
15703831d35Sstevel int	sbbc_ecc_mbox_err_throttle = ECC_MBOX_TASKQ_ERR_THROTTLE;
15803831d35Sstevel 
15903831d35Sstevel /*
16003831d35Sstevel  * Called when SBBC driver is loaded
16103831d35Sstevel  * Initialise global mailbox stuff, etc
16203831d35Sstevel  */
16303831d35Sstevel void
16403831d35Sstevel sbbc_mbox_init()
16503831d35Sstevel {
16603831d35Sstevel 	int	i;
16703831d35Sstevel 
16803831d35Sstevel 	master_mbox = kmem_zalloc(sizeof (sbbc_mailbox_t), KM_NOSLEEP);
16903831d35Sstevel 	if (master_mbox == NULL) {
17003831d35Sstevel 		cmn_err(CE_PANIC, "Can't allocate memory for mailbox\n");
17103831d35Sstevel 	}
17203831d35Sstevel 
17303831d35Sstevel 	/*
17403831d35Sstevel 	 * mutex'es for the wait-lists
17503831d35Sstevel 	 */
17603831d35Sstevel 	for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
17703831d35Sstevel 		mutex_init(&master_mbox->mbox_wait_lock[i],
17803831d35Sstevel 			NULL, MUTEX_DEFAULT, NULL);
17903831d35Sstevel 		master_mbox->mbox_wait_list[i] = NULL;
18003831d35Sstevel 	}
18103831d35Sstevel 
18203831d35Sstevel 	for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++)
18303831d35Sstevel 		master_mbox->intrs[i] = NULL;
18403831d35Sstevel 
18503831d35Sstevel 	/*
18603831d35Sstevel 	 * Two mailbox channels SC -> OS , read-only
18703831d35Sstevel 	 *			OS -> SC, read/write
18803831d35Sstevel 	 */
18903831d35Sstevel 	master_mbox->mbox_in = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
19003831d35Sstevel 	if (master_mbox->mbox_in == NULL) {
19103831d35Sstevel 		cmn_err(CE_PANIC,
19203831d35Sstevel 			"Can't allocate memory for inbound mailbox\n");
19303831d35Sstevel 	}
19403831d35Sstevel 
19503831d35Sstevel 	master_mbox->mbox_out = kmem_zalloc(sizeof (sbbc_mbox_t), KM_NOSLEEP);
19603831d35Sstevel 	if (master_mbox->mbox_out == NULL) {
19703831d35Sstevel 		cmn_err(CE_PANIC,
19803831d35Sstevel 			"Can't allocate memory for outbound mailbox\n");
19903831d35Sstevel 	}
20003831d35Sstevel 
20103831d35Sstevel 	mutex_init(&master_mbox->mbox_in->mb_lock, NULL,
20203831d35Sstevel 		MUTEX_DEFAULT, NULL);
20303831d35Sstevel 	mutex_init(&master_mbox->mbox_out->mb_lock, NULL,
20403831d35Sstevel 		MUTEX_DEFAULT, NULL);
20503831d35Sstevel 
20603831d35Sstevel 	/*
20703831d35Sstevel 	 * Add PANIC_SHUTDOWN Event mutex
20803831d35Sstevel 	 */
20903831d35Sstevel 	mutex_init(&panic_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
21003831d35Sstevel 
21103831d35Sstevel 	/* Initialize datapath error message handler mutex */
21203831d35Sstevel 	mutex_init(&dp_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
21303831d35Sstevel 
21403831d35Sstevel 	/* Initialize capability message handler event mutex */
21503831d35Sstevel 	mutex_init(&cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
21603831d35Sstevel 
21703831d35Sstevel 	/*
21803831d35Sstevel 	 * NOT USED YET
21903831d35Sstevel 	 */
22003831d35Sstevel 	master_mbox->mbox_in->mb_type =
22103831d35Sstevel 		master_mbox->mbox_out->mb_type = 0;
22203831d35Sstevel 
22303831d35Sstevel 	cv_init(&outbox_queue, NULL, CV_DEFAULT, NULL);
22403831d35Sstevel 	mutex_init(&outbox_queue_lock, NULL, MUTEX_DEFAULT, NULL);
22503831d35Sstevel 
22603831d35Sstevel }
22703831d35Sstevel 
22803831d35Sstevel /*
22903831d35Sstevel  * called when the SBBC driver is unloaded
23003831d35Sstevel  */
23103831d35Sstevel void
23203831d35Sstevel sbbc_mbox_fini()
23303831d35Sstevel {
23403831d35Sstevel 	int	i;
23503831d35Sstevel 	int	err;
23603831d35Sstevel 
23703831d35Sstevel 	/*
23803831d35Sstevel 	 * destroy ECC event mailbox taskq
23903831d35Sstevel 	 */
24003831d35Sstevel 	if (sbbc_ecc_mbox_taskq != NULL) {
24103831d35Sstevel 		taskq_destroy(sbbc_ecc_mbox_taskq);
24203831d35Sstevel 		sbbc_ecc_mbox_taskq = NULL;
24303831d35Sstevel 		sbbc_ecc_mbox_taskq_errs = 0;
24403831d35Sstevel 	}
24503831d35Sstevel 
24603831d35Sstevel 	/*
24703831d35Sstevel 	 * unregister interrupts
24803831d35Sstevel 	 */
24903831d35Sstevel 	(void) iosram_unreg_intr(SBBC_MAILBOX_IN);
25003831d35Sstevel 	(void) iosram_unreg_intr(SBBC_MAILBOX_IN);
25103831d35Sstevel 	(void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_IN);
25203831d35Sstevel 	(void) iosram_unreg_intr(SBBC_MAILBOX_SPACE_OUT);
25303831d35Sstevel 
25403831d35Sstevel 	/*
25503831d35Sstevel 	 * Remove Panic Shutdown and Datapath Error event support.
25603831d35Sstevel 	 *
25703831d35Sstevel 	 * NOTE: If we have not added the soft interrupt handlers for these
25803831d35Sstevel 	 * then we know that we have not registered the event handlers either.
25903831d35Sstevel 	 */
26003831d35Sstevel 	if (panic_softintr_id != 0) {
26103831d35Sstevel 		ddi_remove_softintr(panic_softintr_id);
26203831d35Sstevel 
26303831d35Sstevel 		err = sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
26403831d35Sstevel 			sbbc_panic_shutdown_handler);
26503831d35Sstevel 		if (err != 0) {
26603831d35Sstevel 			cmn_err(CE_WARN, "Failed to unreg Panic Shutdown "
26703831d35Sstevel 				"handler. Err=%d", err);
26803831d35Sstevel 		}
26903831d35Sstevel 	}
27003831d35Sstevel 	if (dp_softintr_id != 0) {
27103831d35Sstevel 		ddi_remove_softintr(dp_softintr_id);
27203831d35Sstevel 
27303831d35Sstevel 		err = sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
27403831d35Sstevel 			sbbc_datapath_error_msg_handler);
27503831d35Sstevel 		err |= sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
27603831d35Sstevel 			sbbc_datapath_fault_msg_handler);
27703831d35Sstevel 		if (err != 0) {
27803831d35Sstevel 			cmn_err(CE_WARN, "Failed to unreg Datapath Error "
27903831d35Sstevel 				"handler. Err=%d", err);
28003831d35Sstevel 		}
28103831d35Sstevel 	}
28203831d35Sstevel 
28303831d35Sstevel 	/*
28403831d35Sstevel 	 * destroy all its mutex'es, lists etc
28503831d35Sstevel 	 */
28603831d35Sstevel 
28703831d35Sstevel 	/*
28803831d35Sstevel 	 * mutex'es for the wait-lists
28903831d35Sstevel 	 */
29003831d35Sstevel 	for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
29103831d35Sstevel 		mutex_destroy(&master_mbox->mbox_wait_lock[i]);
29203831d35Sstevel 	}
29303831d35Sstevel 
29403831d35Sstevel 	mutex_destroy(&master_mbox->mbox_in->mb_lock);
29503831d35Sstevel 	mutex_destroy(&master_mbox->mbox_out->mb_lock);
29603831d35Sstevel 
29703831d35Sstevel 	mutex_destroy(&panic_hdlr_lock);
29803831d35Sstevel 	mutex_destroy(&dp_hdlr_lock);
29903831d35Sstevel 
30003831d35Sstevel 	kmem_free(master_mbox->mbox_in, sizeof (sbbc_mbox_t));
30103831d35Sstevel 	kmem_free(master_mbox->mbox_out, sizeof (sbbc_mbox_t));
30203831d35Sstevel 	kmem_free(master_mbox, sizeof (sbbc_mailbox_t));
30303831d35Sstevel 
30403831d35Sstevel 	cv_destroy(&outbox_queue);
30503831d35Sstevel 	mutex_destroy(&outbox_queue_lock);
30603831d35Sstevel 
30703831d35Sstevel 	err = sbbc_mbox_unreg_intr(INFO_MBOX, cap_ecc_msg_handler);
30803831d35Sstevel 	if (err != 0) {
30903831d35Sstevel 		cmn_err(CE_WARN, "Failed to unregister capability message "
31003831d35Sstevel 		    "handler. Err=%d", err);
31103831d35Sstevel 	}
31203831d35Sstevel 
31303831d35Sstevel 	mutex_destroy(&cap_msg_hdlr_lock);
31403831d35Sstevel }
31503831d35Sstevel 
31603831d35Sstevel /*
31703831d35Sstevel  * Update iosram_sbbc to the new softstate after a tunnel switch.
31803831d35Sstevel  * Move software interrupts from the old dip to the new dip.
31903831d35Sstevel  */
32003831d35Sstevel int
32103831d35Sstevel sbbc_mbox_switch(sbbc_softstate_t *softsp)
32203831d35Sstevel {
32303831d35Sstevel 	sbbc_intrs_t	*intr;
32403831d35Sstevel 	int		msg_type;
32503831d35Sstevel 	int		rc = 0;
32603831d35Sstevel 	int		err;
32703831d35Sstevel 
32803831d35Sstevel 	if (master_mbox == NULL)
32903831d35Sstevel 		return (ENXIO);
33003831d35Sstevel 
33103831d35Sstevel 	ASSERT(MUTEX_HELD(&master_iosram->iosram_lock));
33203831d35Sstevel 
33303831d35Sstevel 	for (msg_type = 0; msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
33403831d35Sstevel 
33503831d35Sstevel 		for (intr = master_mbox->intrs[msg_type]; intr != NULL;
33603831d35Sstevel 			intr = intr->sbbc_intr_next) {
33703831d35Sstevel 
33803831d35Sstevel 			if (intr->sbbc_intr_id) {
33903831d35Sstevel 				ddi_remove_softintr(intr->sbbc_intr_id);
34003831d35Sstevel 
34103831d35Sstevel 				if (ddi_add_softintr(softsp->dip,
34203831d35Sstevel 					DDI_SOFTINT_HIGH,
34303831d35Sstevel 					&intr->sbbc_intr_id, NULL, NULL,
34403831d35Sstevel 					intr->sbbc_handler, intr->sbbc_arg)
34503831d35Sstevel 					!= DDI_SUCCESS) {
34603831d35Sstevel 
34703831d35Sstevel 					cmn_err(CE_WARN,
34803831d35Sstevel 						"Can't add SBBC mailbox "
34903831d35Sstevel 						"softint for msg_type %x\n",
35003831d35Sstevel 							msg_type);
35103831d35Sstevel 					rc = ENXIO;
35203831d35Sstevel 				}
35303831d35Sstevel 			}
35403831d35Sstevel 		}
35503831d35Sstevel 	}
35603831d35Sstevel 
35703831d35Sstevel 	/*
35803831d35Sstevel 	 * Add PANIC_SHUTDOWN Event handler
35903831d35Sstevel 	 */
36003831d35Sstevel 	if (panic_softintr_id) {
36103831d35Sstevel 		ddi_remove_softintr(panic_softintr_id);
36203831d35Sstevel 
36303831d35Sstevel 		err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
36403831d35Sstevel 			&panic_softintr_id, NULL, NULL,
36503831d35Sstevel 			sbbc_do_fast_shutdown, NULL);
36603831d35Sstevel 
36703831d35Sstevel 		if (err != DDI_SUCCESS) {
36803831d35Sstevel 			cmn_err(CE_WARN, "Failed to register Panic "
36903831d35Sstevel 				"Shutdown handler. Err=%d", err);
37003831d35Sstevel 			(void) sbbc_mbox_unreg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
37103831d35Sstevel 				sbbc_panic_shutdown_handler);
37203831d35Sstevel 			rc = ENXIO;
37303831d35Sstevel 		}
37403831d35Sstevel 
37503831d35Sstevel 	}
37603831d35Sstevel 	/*
37703831d35Sstevel 	 * Add Datapath Error Event handler
37803831d35Sstevel 	 */
37903831d35Sstevel 	if (dp_softintr_id) {
38003831d35Sstevel 		ddi_remove_softintr(dp_softintr_id);
38103831d35Sstevel 
38203831d35Sstevel 		err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW,
38303831d35Sstevel 			&dp_softintr_id, NULL, NULL,
38403831d35Sstevel 			sbbc_dp_trans_event, NULL);
38503831d35Sstevel 
38603831d35Sstevel 		if (err != DDI_SUCCESS) {
38703831d35Sstevel 			cmn_err(CE_WARN, "Failed to register Datapath "
38803831d35Sstevel 				"Error Event handler. Err=%d", err);
38903831d35Sstevel 			(void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_ERROR,
39003831d35Sstevel 				sbbc_datapath_error_msg_handler);
39103831d35Sstevel 			(void) sbbc_mbox_unreg_intr(MBOX_EVENT_DP_FAULT,
39203831d35Sstevel 				sbbc_datapath_fault_msg_handler);
39303831d35Sstevel 			rc = ENXIO;
39403831d35Sstevel 		}
39503831d35Sstevel 
39603831d35Sstevel 	}
39703831d35Sstevel 
39803831d35Sstevel 	return (rc);
39903831d35Sstevel }
40003831d35Sstevel 
40103831d35Sstevel /*
40203831d35Sstevel  * Called when the IOSRAM tunnel is created for the 'chosen' node.
40303831d35Sstevel  *
40403831d35Sstevel  * Read the mailbox header from the IOSRAM
40503831d35Sstevel  * tunnel[SBBC_MAILBOX_KEY]
40603831d35Sstevel  * Register the mailbox interrupt handlers
40703831d35Sstevel  * for messages in/space etc
40803831d35Sstevel  */
40903831d35Sstevel int
41003831d35Sstevel sbbc_mbox_create(sbbc_softstate_t *softsp)
41103831d35Sstevel {
41203831d35Sstevel 	struct sbbc_mbox_header	header;
41303831d35Sstevel 
41403831d35Sstevel 	int	i;
41503831d35Sstevel 	int	err;
41603831d35Sstevel 	int	rc = 0;
41703831d35Sstevel 
41803831d35Sstevel 	/*
41903831d35Sstevel 	 * This function should only be called once when
42003831d35Sstevel 	 * the chosen node is initialized.
42103831d35Sstevel 	 */
42203831d35Sstevel 	ASSERT(MUTEX_HELD(&chosen_lock));
42303831d35Sstevel 
42403831d35Sstevel 	if (master_mbox == NULL)
42503831d35Sstevel 		return (ENXIO);
42603831d35Sstevel 
42703831d35Sstevel 	/*
42803831d35Sstevel 	 * read the header at offset 0
42903831d35Sstevel 	 * check magic/version etc
43003831d35Sstevel 	 */
43103831d35Sstevel 	if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)&header,
43203831d35Sstevel 	    sizeof (struct sbbc_mbox_header))) {
43303831d35Sstevel 
43403831d35Sstevel 		return (rc);
43503831d35Sstevel 	}
43603831d35Sstevel 
43703831d35Sstevel 	/*
43803831d35Sstevel 	 * add the interrupt handlers for the mailbox
43903831d35Sstevel 	 * interrupts
44003831d35Sstevel 	 */
44103831d35Sstevel 	for (i = 0; i < MBOX_INTRS; i++) {
44203831d35Sstevel 		sbbc_intrfunc_t		intr_handler;
44303831d35Sstevel 		uint_t 			*state;
44403831d35Sstevel 		kmutex_t 		*lock;
44503831d35Sstevel 		uint32_t		intr_num;
44603831d35Sstevel 
44703831d35Sstevel 		switch (i) {
44803831d35Sstevel 		case MBOX_MSGIN_INTR:
44903831d35Sstevel 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgin;
45003831d35Sstevel 			intr_num = SBBC_MAILBOX_IN;
45103831d35Sstevel 			break;
45203831d35Sstevel 		case MBOX_MSGOUT_INTR:
45303831d35Sstevel 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_msgout;
45403831d35Sstevel 			intr_num = SBBC_MAILBOX_OUT;
45503831d35Sstevel 			break;
45603831d35Sstevel 		case MBOX_SPACEIN_INTR:
45703831d35Sstevel 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spacein;
45803831d35Sstevel 			intr_num = SBBC_MAILBOX_SPACE_IN;
45903831d35Sstevel 			break;
46003831d35Sstevel 		case MBOX_SPACEOUT_INTR:
46103831d35Sstevel 			intr_handler = (sbbc_intrfunc_t)sbbc_mbox_spaceout;
46203831d35Sstevel 			intr_num = SBBC_MAILBOX_SPACE_OUT;
46303831d35Sstevel 			break;
46403831d35Sstevel 		}
46503831d35Sstevel 		state = (uint_t *)&master_mbox->intr_state[i].mbox_intr_state;
46603831d35Sstevel 		lock = &master_mbox->intr_state[i].mbox_intr_lock;
46703831d35Sstevel 		if (iosram_reg_intr(intr_num, intr_handler, (caddr_t)NULL,
46803831d35Sstevel 			state, lock)) {
46903831d35Sstevel 
47003831d35Sstevel 			cmn_err(CE_WARN,
47103831d35Sstevel 				"Can't register Mailbox interrupts \n");
47203831d35Sstevel 		}
47303831d35Sstevel 	}
47403831d35Sstevel 
47503831d35Sstevel 	/*
47603831d35Sstevel 	 * Add PANIC_SHUTDOWN Event handler
47703831d35Sstevel 	 */
47803831d35Sstevel 	panic_payload_msg.msg_buf = (caddr_t)&panic_payload;
47903831d35Sstevel 	panic_payload_msg.msg_len = sizeof (panic_payload);
48003831d35Sstevel 
48103831d35Sstevel 	err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &panic_softintr_id,
48203831d35Sstevel 		NULL, NULL, sbbc_do_fast_shutdown, NULL);
48303831d35Sstevel 
48403831d35Sstevel 	if (err == DDI_SUCCESS) {
48503831d35Sstevel 		err = sbbc_mbox_reg_intr(MBOX_EVENT_PANIC_SHUTDOWN,
48603831d35Sstevel 			sbbc_panic_shutdown_handler, &panic_payload_msg,
48703831d35Sstevel 			NULL, &panic_hdlr_lock);
48803831d35Sstevel 		if (err != 0) {
48903831d35Sstevel 			cmn_err(CE_WARN, "Failed to register Panic "
49003831d35Sstevel 				"Shutdown handler. Err=%d", err);
49103831d35Sstevel 		}
49203831d35Sstevel 
49303831d35Sstevel 	} else {
49403831d35Sstevel 		cmn_err(CE_WARN, "Failed to add Panic Shutdown "
49503831d35Sstevel 			"softintr handler");
49603831d35Sstevel 	}
49703831d35Sstevel 
49803831d35Sstevel 	/*
49903831d35Sstevel 	 * Add Unsolicited Datapath Error Events handler
50003831d35Sstevel 	 */
50103831d35Sstevel 	dp_payload_msg.msg_buf = (caddr_t)&dp_payload;
50203831d35Sstevel 	dp_payload_msg.msg_len = sizeof (dp_payload);
50303831d35Sstevel 
50403831d35Sstevel 	err = ddi_add_softintr(softsp->dip, DDI_SOFTINT_LOW, &dp_softintr_id,
50503831d35Sstevel 		NULL, NULL, sbbc_dp_trans_event, NULL);
50603831d35Sstevel 
50703831d35Sstevel 	if (err == DDI_SUCCESS) {
50803831d35Sstevel 		err = sbbc_mbox_reg_intr(MBOX_EVENT_DP_ERROR,
50903831d35Sstevel 			sbbc_datapath_error_msg_handler, &dp_payload_msg,
51003831d35Sstevel 			NULL, &dp_hdlr_lock);
51103831d35Sstevel 		err |= sbbc_mbox_reg_intr(MBOX_EVENT_DP_FAULT,
51203831d35Sstevel 			sbbc_datapath_fault_msg_handler, &dp_payload_msg,
51303831d35Sstevel 			NULL, &dp_hdlr_lock);
51403831d35Sstevel 		if (err != 0) {
51503831d35Sstevel 			cmn_err(CE_WARN, "Failed to register Datapath "
51603831d35Sstevel 				"error handler. Err=%d", err);
51703831d35Sstevel 		}
51803831d35Sstevel 
51903831d35Sstevel 	} else {
52003831d35Sstevel 		cmn_err(CE_WARN, "Failed to add Datapath error "
52103831d35Sstevel 			"softintr handler");
52203831d35Sstevel 	}
52303831d35Sstevel 
52403831d35Sstevel 	/*
52503831d35Sstevel 	 * Register an interrupt handler with the sgbbc driver for the
52603831d35Sstevel 	 * unsolicited INFO_MBOX response for the capability bitmap.
52703831d35Sstevel 	 * This message is expected whenever the SC is (re)booted or
52803831d35Sstevel 	 * failed over.
52903831d35Sstevel 	 */
53003831d35Sstevel 	cap_payload_msg.msg_buf = (caddr_t)&cap_payload;
53103831d35Sstevel 	cap_payload_msg.msg_len = sizeof (cap_payload);
53203831d35Sstevel 
53303831d35Sstevel 	err = sbbc_mbox_reg_intr(INFO_MBOX, cap_ecc_msg_handler,
53403831d35Sstevel 	    &cap_payload_msg, NULL, &cap_msg_hdlr_lock);
53503831d35Sstevel 	if (err != 0) {
53603831d35Sstevel 		cmn_err(CE_WARN, "Failed to register capability message"
53703831d35Sstevel 		    " handler with Err=%d", err);
53803831d35Sstevel 	}
53903831d35Sstevel 
54003831d35Sstevel 	/*
54103831d35Sstevel 	 * Now is the opportunity to register
54203831d35Sstevel 	 * the deferred mbox intrs.
54303831d35Sstevel 	 */
54403831d35Sstevel 	sbbc_mbox_post_reg(softsp);
54503831d35Sstevel 
54603831d35Sstevel 	return (rc);
54703831d35Sstevel }
54803831d35Sstevel 
54903831d35Sstevel /*
55003831d35Sstevel  * Called when chosen IOSRAM is initialized
55103831d35Sstevel  * to register the deferred mbox intrs.
55203831d35Sstevel  */
55303831d35Sstevel static void
55403831d35Sstevel sbbc_mbox_post_reg(sbbc_softstate_t *softsp)
55503831d35Sstevel {
55603831d35Sstevel 	uint32_t msg_type;
55703831d35Sstevel 	sbbc_intrs_t	*intr;
55803831d35Sstevel 
55903831d35Sstevel 	ASSERT(master_mbox);
56003831d35Sstevel 	for (msg_type = 0;  msg_type < SBBC_MBOX_MSG_TYPES; msg_type++) {
56103831d35Sstevel 		intr = master_mbox->intrs[msg_type];
56203831d35Sstevel 		while (intr != NULL) {
56303831d35Sstevel 			if (!intr->registered) {
56403831d35Sstevel 				SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_post_reg: "
56503831d35Sstevel 					"postreg for msgtype=%x\n", msg_type);
56603831d35Sstevel 				if (ddi_add_softintr(softsp->dip,
56703831d35Sstevel 					DDI_SOFTINT_HIGH, &intr->sbbc_intr_id,
56803831d35Sstevel 					NULL, NULL, intr->sbbc_handler,
56903831d35Sstevel 					(caddr_t)intr->sbbc_arg)
57003831d35Sstevel 						!= DDI_SUCCESS) {
57103831d35Sstevel 					cmn_err(CE_WARN, "Can't add SBBC "
57203831d35Sstevel 						"deferred mailbox softint \n");
57303831d35Sstevel 				} else
57403831d35Sstevel 					intr->registered = 1;
57503831d35Sstevel 			}
57603831d35Sstevel 			intr = intr->sbbc_intr_next;
57703831d35Sstevel 		}
57803831d35Sstevel 	}
57903831d35Sstevel }
58003831d35Sstevel 
58103831d35Sstevel /*
58203831d35Sstevel  * Register a handler for a message type
58303831d35Sstevel  * NB NB NB
58403831d35Sstevel  * arg must be either NULL or the address of a sbbc_fragment
58503831d35Sstevel  * pointer
58603831d35Sstevel  */
58703831d35Sstevel int
58803831d35Sstevel sbbc_mbox_reg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler,
58903831d35Sstevel 		sbbc_msg_t *arg, uint_t *state, kmutex_t *lock)
59003831d35Sstevel {
59103831d35Sstevel 	sbbc_intrs_t	*intr, *previntr;
59203831d35Sstevel 	int		rc = 0;
59303831d35Sstevel 
59403831d35Sstevel 	/*
59503831d35Sstevel 	 * Validate arguments
59603831d35Sstevel 	 */
59703831d35Sstevel 	if (msg_type >= SBBC_MBOX_MSG_TYPES)
59803831d35Sstevel 		return (EINVAL);
59903831d35Sstevel 
60003831d35Sstevel 	/*
60103831d35Sstevel 	 * Verify that we have already set up the master sbbc
60203831d35Sstevel 	 */
60303831d35Sstevel 	if (master_iosram == NULL || master_mbox == NULL)
60403831d35Sstevel 		return (ENXIO);
60503831d35Sstevel 
60603831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
60703831d35Sstevel 	msg_type &= SBBC_MSG_TYPE_MASK;
60803831d35Sstevel 	previntr = intr = master_mbox->intrs[msg_type];
60903831d35Sstevel 
61003831d35Sstevel 	/* Find the end of the link list */
61103831d35Sstevel 	while (intr != NULL && intr->sbbc_handler != intr_handler) {
61203831d35Sstevel 
61303831d35Sstevel 		previntr = intr;
61403831d35Sstevel 		intr = intr->sbbc_intr_next;
61503831d35Sstevel 	}
61603831d35Sstevel 
61703831d35Sstevel 	/* Return if the handler has been registered */
61803831d35Sstevel 	if (intr != NULL) {
61903831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
62003831d35Sstevel 		return (EBUSY);
62103831d35Sstevel 	}
62203831d35Sstevel 
62303831d35Sstevel 	/*
62403831d35Sstevel 	 * The requested handler has not been installed.
62503831d35Sstevel 	 * Allocate some memory.
62603831d35Sstevel 	 */
62703831d35Sstevel 	intr = kmem_zalloc(sizeof (sbbc_intrs_t), KM_SLEEP);
62803831d35Sstevel 
62903831d35Sstevel 	intr->sbbc_handler  = intr_handler;
63003831d35Sstevel 	intr->sbbc_arg = (caddr_t)arg;
63103831d35Sstevel 	intr->sbbc_intr_state = state;
63203831d35Sstevel 	intr->sbbc_intr_lock = lock;
63303831d35Sstevel 	intr->sbbc_intr_next = NULL;
63403831d35Sstevel 	/* not registered yet */
63503831d35Sstevel 	intr->registered = 0;
63603831d35Sstevel 
63703831d35Sstevel 	if (previntr != NULL)
63803831d35Sstevel 		previntr->sbbc_intr_next = intr;
63903831d35Sstevel 	else
64003831d35Sstevel 		master_mbox->intrs[msg_type] = intr;
64103831d35Sstevel 
64203831d35Sstevel 	/*
64303831d35Sstevel 	 * register only if the chosen IOSRAM is
64403831d35Sstevel 	 * initialized, otherwise defer the registration
64503831d35Sstevel 	 * until IOSRAM initialization.
64603831d35Sstevel 	 */
64703831d35Sstevel 	if (master_iosram->iosram_sbbc) {
64803831d35Sstevel 		if (ddi_add_softintr(master_iosram->iosram_sbbc->dip,
64903831d35Sstevel 			DDI_SOFTINT_HIGH,
65003831d35Sstevel 			&intr->sbbc_intr_id, NULL, NULL,
65103831d35Sstevel 			intr_handler, (caddr_t)arg) != DDI_SUCCESS) {
65203831d35Sstevel 			cmn_err(CE_WARN, "Can't add SBBC mailbox softint \n");
65303831d35Sstevel 			rc = ENXIO;
65403831d35Sstevel 		} else
65503831d35Sstevel 			intr->registered = 1;
65603831d35Sstevel 	} else {
65703831d35Sstevel 		SGSBBC_DBG_INTR(CE_CONT, "sbbc_mbox_reg_intr: "
65803831d35Sstevel 				"deferring msg=%x registration\n", msg_type);
65903831d35Sstevel 	}
66003831d35Sstevel 
66103831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
66203831d35Sstevel 
66303831d35Sstevel 	return (rc);
66403831d35Sstevel }
66503831d35Sstevel 
66603831d35Sstevel /*
66703831d35Sstevel  * Unregister a handler for a message type
66803831d35Sstevel  */
66903831d35Sstevel int
67003831d35Sstevel sbbc_mbox_unreg_intr(uint32_t msg_type, sbbc_intrfunc_t intr_handler)
67103831d35Sstevel {
67203831d35Sstevel 	sbbc_intrs_t		*intr, *previntr, *nextintr;
67303831d35Sstevel 
67403831d35Sstevel 	/*
67503831d35Sstevel 	 * Verify that we have already set up the master sbbc
67603831d35Sstevel 	 */
67703831d35Sstevel 	if (master_iosram == NULL || master_mbox == NULL)
67803831d35Sstevel 		return (ENXIO);
67903831d35Sstevel 
68003831d35Sstevel 	msg_type &= SBBC_MSG_TYPE_MASK;
68103831d35Sstevel 
68203831d35Sstevel 	if (msg_type >= SBBC_MBOX_MSG_TYPES ||
68303831d35Sstevel 		intr_handler == (sbbc_intrfunc_t)NULL) {
68403831d35Sstevel 
68503831d35Sstevel 		return (EINVAL);
68603831d35Sstevel 	}
68703831d35Sstevel 
68803831d35Sstevel 	mutex_enter(&master_iosram->iosram_lock);
68903831d35Sstevel 
69003831d35Sstevel 	previntr = intr = master_mbox->intrs[msg_type];
69103831d35Sstevel 
69203831d35Sstevel 	/*
69303831d35Sstevel 	 * No handlers installed
69403831d35Sstevel 	 */
69503831d35Sstevel 	if (intr == NULL) {
69603831d35Sstevel 		mutex_exit(&master_iosram->iosram_lock);
69703831d35Sstevel 		return (EINVAL);
69803831d35Sstevel 	}
69903831d35Sstevel 
70003831d35Sstevel 	while (intr != NULL) {
70103831d35Sstevel 
70203831d35Sstevel 		/* Save the next pointer */
70303831d35Sstevel 		nextintr = intr->sbbc_intr_next;
70403831d35Sstevel 
70503831d35Sstevel 		/* Found a match.  Remove it from the link list */
70603831d35Sstevel 		if (intr->sbbc_handler == intr_handler) {
70703831d35Sstevel 
70803831d35Sstevel 			if (intr->sbbc_intr_id)
70903831d35Sstevel 				ddi_remove_softintr(intr->sbbc_intr_id);
71003831d35Sstevel 
71103831d35Sstevel 			kmem_free(intr, sizeof (sbbc_intrs_t));
71203831d35Sstevel 
71303831d35Sstevel 			if (previntr != master_mbox->intrs[msg_type])
71403831d35Sstevel 				previntr->sbbc_intr_next = nextintr;
71503831d35Sstevel 			else
71603831d35Sstevel 				master_mbox->intrs[msg_type] = nextintr;
71703831d35Sstevel 
71803831d35Sstevel 			break;
71903831d35Sstevel 		}
72003831d35Sstevel 
72103831d35Sstevel 		/* update pointers */
72203831d35Sstevel 		previntr = intr;
72303831d35Sstevel 		intr = nextintr;
72403831d35Sstevel 	}
72503831d35Sstevel 
72603831d35Sstevel 	mutex_exit(&master_iosram->iosram_lock);
72703831d35Sstevel 
72803831d35Sstevel 	return (0);
72903831d35Sstevel }
73003831d35Sstevel /*
73103831d35Sstevel  * Interrupt handlers - one for each mailbox
73203831d35Sstevel  * interrupt type
73303831d35Sstevel  */
73403831d35Sstevel 
73503831d35Sstevel /*
73603831d35Sstevel  * mailbox message received
73703831d35Sstevel  */
73803831d35Sstevel static int
73903831d35Sstevel sbbc_mbox_msgin()
74003831d35Sstevel {
74103831d35Sstevel 	mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
74203831d35Sstevel 	master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_state =
74303831d35Sstevel 		SBBC_INTR_RUNNING;
74403831d35Sstevel 	mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].mbox_intr_lock);
74503831d35Sstevel 
74603831d35Sstevel 	/*
74703831d35Sstevel 	 * We are only locking the InBox here, not the whole
74803831d35Sstevel 	 * mailbox. This is based on the assumption of
74903831d35Sstevel 	 * complete separation of mailboxes - outbox is
75003831d35Sstevel 	 * read/write, inbox is read-only.
75103831d35Sstevel 	 * We only ever update the producer for the
75203831d35Sstevel 	 * outbox and the consumer for the inbox.
75303831d35Sstevel 	 */
75403831d35Sstevel 	mutex_enter(&master_mbox->mbox_in->mb_lock);
75503831d35Sstevel 
75603831d35Sstevel 	for (;;) {
75703831d35Sstevel 		/*
75803831d35Sstevel 		 * Get as many incoming messages as possible
75903831d35Sstevel 		 */
76003831d35Sstevel 		while (sbbc_mbox_recv_msg() == 0)
76103831d35Sstevel 			/* empty */;
76203831d35Sstevel 
76303831d35Sstevel 		/*
76403831d35Sstevel 		 * send interrupt to SC to let it know that
76503831d35Sstevel 		 * space is available over here
76603831d35Sstevel 		 */
76703831d35Sstevel 		(void) iosram_send_intr(SBBC_MAILBOX_SPACE_IN);
76803831d35Sstevel 
76903831d35Sstevel 		mutex_enter(&master_mbox->intr_state[MBOX_MSGIN_INTR].
77003831d35Sstevel 			mbox_intr_lock);
77103831d35Sstevel 		/*
77203831d35Sstevel 		 * Read the inbox one more time to see if new messages
77303831d35Sstevel 		 * has come in after we exit the loop.
77403831d35Sstevel 		 */
77503831d35Sstevel 		if (sbbc_mbox_recv_msg() == 0) {
77603831d35Sstevel 			mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
77703831d35Sstevel 				mbox_intr_lock);
77803831d35Sstevel 		} else {
77903831d35Sstevel 			master_mbox->intr_state[MBOX_MSGIN_INTR].
78003831d35Sstevel 				mbox_intr_state = SBBC_INTR_IDLE;
78103831d35Sstevel 			mutex_exit(&master_mbox->intr_state[MBOX_MSGIN_INTR].
78203831d35Sstevel 				mbox_intr_lock);
78303831d35Sstevel 			break;
78403831d35Sstevel 		}
78503831d35Sstevel 	}
78603831d35Sstevel 
78703831d35Sstevel 	mutex_exit(&master_mbox->mbox_in->mb_lock);
78803831d35Sstevel 
78903831d35Sstevel 	return (DDI_INTR_CLAIMED);
79003831d35Sstevel }
79103831d35Sstevel 
79203831d35Sstevel /*
79303831d35Sstevel  * mailbox message sent
79403831d35Sstevel  */
79503831d35Sstevel static int
79603831d35Sstevel sbbc_mbox_msgout()
79703831d35Sstevel {
79803831d35Sstevel 	/*
79903831d35Sstevel 	 * Should never get this
80003831d35Sstevel 	 */
80103831d35Sstevel 
80203831d35Sstevel 	return (DDI_INTR_CLAIMED);
80303831d35Sstevel }
80403831d35Sstevel 
80503831d35Sstevel /*
80603831d35Sstevel  * space in the inbox
80703831d35Sstevel  */
80803831d35Sstevel static int
80903831d35Sstevel sbbc_mbox_spacein()
81003831d35Sstevel {
81103831d35Sstevel 	/*
81203831d35Sstevel 	 * Should never get this
81303831d35Sstevel 	 */
81403831d35Sstevel 
81503831d35Sstevel 	return (DDI_INTR_CLAIMED);
81603831d35Sstevel }
81703831d35Sstevel 
81803831d35Sstevel /*
81903831d35Sstevel  * space in the outbox
82003831d35Sstevel  */
82103831d35Sstevel static int
82203831d35Sstevel sbbc_mbox_spaceout()
82303831d35Sstevel {
82403831d35Sstevel 	/*
82503831d35Sstevel 	 * cv_broadcast() the threads waiting on the
82603831d35Sstevel 	 * outbox's mb_full
82703831d35Sstevel 	 */
82803831d35Sstevel 
82903831d35Sstevel 	mutex_enter(&master_mbox->mbox_out->mb_lock);
83003831d35Sstevel 
83103831d35Sstevel 	cv_broadcast(&master_mbox->mbox_out->mb_full);
83203831d35Sstevel 
83303831d35Sstevel 	mutex_exit(&master_mbox->mbox_out->mb_lock);
83403831d35Sstevel 
83503831d35Sstevel 	return (DDI_INTR_CLAIMED);
83603831d35Sstevel }
83703831d35Sstevel 
83803831d35Sstevel /*
83903831d35Sstevel  * Client Interface
84003831d35Sstevel  *
84103831d35Sstevel  * The main interface will be
84203831d35Sstevel  *
84303831d35Sstevel  * sbbc_mbox_request_response(sbbc_msg_t *request,
84403831d35Sstevel  * 			sbbc_msg_t *response, time_t wait_time)
84503831d35Sstevel  *
84603831d35Sstevel  * 1) the client calls request_response
84703831d35Sstevel  * 2) a new unique msg ID is assigned for that msg
84803831d35Sstevel  * 3) if there is space available in the outbox
84903831d35Sstevel  *    - the request msg is written to the mbox_out mailbox
85003831d35Sstevel  *	and the mailbox info updated.
85103831d35Sstevel  *    - allocate a sbbc_msg_waiter struct for this
85203831d35Sstevel  *	message, initialise the w_cv condvar.
85303831d35Sstevel  *    - get the mailbox mbox_wait_lock mutex for this
85403831d35Sstevel  *      message type
85503831d35Sstevel  *    - the response msg is put on the mbox_wait_list for
85603831d35Sstevel  *	that message type to await the SC's response
85703831d35Sstevel  *    - wait on the w_cv condvar protected by the
85803831d35Sstevel  *	mbox_wait_lock
85903831d35Sstevel  *    - SBBC_MAILBOX_OUT interrupt is sent to the SC
86003831d35Sstevel  *
86103831d35Sstevel  * 4) if no space in the outbox,
86203831d35Sstevel  *    - the request message blocks waiting
86303831d35Sstevel  *	for a SBBC_MAILBOX_SPACE_OUT interrupt
86403831d35Sstevel  *      It will block on the mailbox mb_full condvar.
86503831d35Sstevel  *    - go to (3) above
86603831d35Sstevel  * 5) When we get a SBBC_MAILBOX_IN interrupt.
86703831d35Sstevel  *    - read the message ID of the next message (FIFO)
86803831d35Sstevel  *    - find that ID on the wait list
86903831d35Sstevel  *    - no wait list entry => unsolicited message. If theres
87003831d35Sstevel  *      a handler, trigger it
87103831d35Sstevel  *    - if someone is waiting, read the message in from
87203831d35Sstevel  *	SRAM, handling fragmentation, wraparound, etc
87303831d35Sstevel  *    - if the whole message has been read, signal
87403831d35Sstevel  *	the waiter
87503831d35Sstevel  *    - read next message until mailbox empty
87603831d35Sstevel  *    - send SBBC_MAILBOX_SPACE_IN interrupt to the SC
87703831d35Sstevel  *
87803831d35Sstevel  * 6) If a response is required and none is received, the client
87903831d35Sstevel  *	will timeout after <wait_time> seconds and the message
88003831d35Sstevel  *	status will be set to ETIMEDOUT.
88103831d35Sstevel  */
88203831d35Sstevel int
88303831d35Sstevel sbbc_mbox_request_response(sbbc_msg_t *request,
88403831d35Sstevel 		sbbc_msg_t *response, time_t wait_time)
88503831d35Sstevel {
88603831d35Sstevel 
88703831d35Sstevel 	struct sbbc_msg_waiter	*waiter;
88803831d35Sstevel 	uint_t			msg_id;
88903831d35Sstevel 	int			rc = 0;
89003831d35Sstevel 	int			flags;
89103831d35Sstevel 	uint16_t		msg_type;
89203831d35Sstevel 	clock_t			stop_time;
89303831d35Sstevel 	clock_t			clockleft;
89403831d35Sstevel 	kmutex_t		*mbox_wait_lock;
89503831d35Sstevel 	kmutex_t		*mb_lock;
89603831d35Sstevel 	static fn_t		f = "sbbc_mbox_request_response";
89703831d35Sstevel 
89803831d35Sstevel 	if ((request == NULL) ||
89903831d35Sstevel 		(request->msg_type.type >= SBBC_MBOX_MSG_TYPES) ||
90003831d35Sstevel 		((response != NULL) &&
90103831d35Sstevel 		(response->msg_type.type >= SBBC_MBOX_MSG_TYPES)))
90203831d35Sstevel 		return (EINVAL);
90303831d35Sstevel 
90403831d35Sstevel 	msg_type = request->msg_type.type;
90503831d35Sstevel 
90603831d35Sstevel 	/*
90703831d35Sstevel 	 * Verify that we have already set up the master sbbc
90803831d35Sstevel 	 */
90903831d35Sstevel 	if (master_mbox == NULL)
91003831d35Sstevel 		return (ENXIO);
91103831d35Sstevel 	mbox_wait_lock = &master_mbox->mbox_wait_lock[msg_type];
91203831d35Sstevel 
91303831d35Sstevel 	flags = WAIT_FOR_REPLY|WAIT_FOR_SPACE;
91403831d35Sstevel 
91503831d35Sstevel 	/*
91603831d35Sstevel 	 * We want to place a lower limit on the shortest amount of time we
91703831d35Sstevel 	 * will wait before timing out while communicating with the SC via
91803831d35Sstevel 	 * the mailbox.
91903831d35Sstevel 	 */
92003831d35Sstevel 	if (wait_time < sbbc_mbox_min_timeout)
92103831d35Sstevel 		wait_time = sbbc_mbox_default_timeout;
92203831d35Sstevel 
92303831d35Sstevel 	stop_time = ddi_get_lbolt() + wait_time * drv_usectohz(MICROSEC);
92403831d35Sstevel 
92503831d35Sstevel 	/*
92603831d35Sstevel 	 * If there is a message being processed, sleep until it is our turn.
92703831d35Sstevel 	 */
92803831d35Sstevel 	mutex_enter(&outbox_queue_lock);
92903831d35Sstevel 
93003831d35Sstevel 	/*
93103831d35Sstevel 	 * allocate an ID for this message, let it wrap
93203831d35Sstevel 	 * around transparently.
93303831d35Sstevel 	 * msg_id == 0 is unsolicited message
93403831d35Sstevel 	 */
93503831d35Sstevel 	msg_id = ++(master_mbox->mbox_msg_id);
93603831d35Sstevel 	if (msg_id == 0)
93703831d35Sstevel 		msg_id = ++(master_mbox->mbox_msg_id);
93803831d35Sstevel 
93903831d35Sstevel 	SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, msg_len = 0x%x\n",
94003831d35Sstevel 		f, msg_id, request->msg_len);
94103831d35Sstevel 
94203831d35Sstevel 	/*
94303831d35Sstevel 	 * A new message can actually grab the lock before the thread
94403831d35Sstevel 	 * that has just been signaled.  Therefore, we need to double
94503831d35Sstevel 	 * check to make sure that outbox_busy is not already set
94603831d35Sstevel 	 * after we wake up.
94703831d35Sstevel 	 *
94803831d35Sstevel 	 * Potentially this could mean starvation for certain unfortunate
94903831d35Sstevel 	 * threads that keep getting woken up and putting back to sleep.
95003831d35Sstevel 	 * But the window of such contention is very small to begin with.
95103831d35Sstevel 	 */
95203831d35Sstevel 	while (outbox_busy) {
95303831d35Sstevel 
95403831d35Sstevel 		clockleft = cv_timedwait(&outbox_queue, &outbox_queue_lock,
95503831d35Sstevel 			stop_time);
95603831d35Sstevel 
95703831d35Sstevel 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up\n", f, msg_id);
95803831d35Sstevel 
95903831d35Sstevel 		/*
96003831d35Sstevel 		 * If we have timed out, set status to ETIMEOUT and return.
96103831d35Sstevel 		 */
96203831d35Sstevel 		if (clockleft < 0) {
96303831d35Sstevel 			SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
96403831d35Sstevel 				f, msg_id);
96503831d35Sstevel 			cmn_err(CE_NOTE,
96603831d35Sstevel 				"Timed out obtaining SBBC outbox lock");
96703831d35Sstevel 			request->msg_status = ETIMEDOUT;
96803831d35Sstevel 			if (response != NULL)
96903831d35Sstevel 				response->msg_status = ETIMEDOUT;
97003831d35Sstevel 			mutex_exit(&outbox_queue_lock);
97103831d35Sstevel 			return (ETIMEDOUT);
97203831d35Sstevel 		}
97303831d35Sstevel 	}
97403831d35Sstevel 
97503831d35Sstevel 	outbox_busy = 1;
97603831d35Sstevel 	mutex_exit(&outbox_queue_lock);
97703831d35Sstevel 
97803831d35Sstevel 	/*
97903831d35Sstevel 	 * We are only locking the OutBox from here, not the whole
98003831d35Sstevel 	 * mailbox. This is based on the assumption of
98103831d35Sstevel 	 * complete separation of mailboxes - outbox is
98203831d35Sstevel 	 * read/write, inbox is read-only.
98303831d35Sstevel 	 * We only ever update the producer for the
98403831d35Sstevel 	 * outbox and the consumer for the inbox.
98503831d35Sstevel 	 */
98603831d35Sstevel 	mb_lock = &master_mbox->mbox_out->mb_lock;
98703831d35Sstevel 	mutex_enter(mb_lock);
98803831d35Sstevel 
98903831d35Sstevel 	/*
99003831d35Sstevel 	 * No response expected ? Just send the message and return
99103831d35Sstevel 	 */
99203831d35Sstevel 	if (response == NULL) {
99303831d35Sstevel 		rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time,
99403831d35Sstevel 			stop_time);
99503831d35Sstevel 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
99603831d35Sstevel 		    f, msg_id, rc);
99703831d35Sstevel 
99803831d35Sstevel 		wakeup_next();
99903831d35Sstevel 
100003831d35Sstevel 		mutex_exit(mb_lock);
100103831d35Sstevel 		request->msg_status = rc;
100203831d35Sstevel 		return (rc);
100303831d35Sstevel 	}
100403831d35Sstevel 
100503831d35Sstevel 	/*
100603831d35Sstevel 	 * allocate/initialise a waiter
100703831d35Sstevel 	 */
100803831d35Sstevel 	waiter = kmem_zalloc(sizeof (struct sbbc_msg_waiter), KM_NOSLEEP);
100903831d35Sstevel 
101003831d35Sstevel 	if (waiter == (struct sbbc_msg_waiter *)NULL) {
101103831d35Sstevel 		cmn_err(CE_WARN, "SBBC Mailbox can't allocate waiter\n");
101203831d35Sstevel 
101303831d35Sstevel 		wakeup_next();
101403831d35Sstevel 
101503831d35Sstevel 		mutex_exit(mb_lock);
101603831d35Sstevel 		return (ENOMEM);
101703831d35Sstevel 	}
101803831d35Sstevel 
101903831d35Sstevel 	waiter->w_id = 0;	/* Until we get an ID from the send */
102003831d35Sstevel 	waiter->w_msg = response;
102103831d35Sstevel 	waiter->w_msg->msg_status = EINPROGRESS;
102203831d35Sstevel 
102303831d35Sstevel 	cv_init(&waiter->w_cv, NULL, CV_DEFAULT, NULL);
102403831d35Sstevel 
102503831d35Sstevel 	rc = sbbc_mbox_send_msg(request, flags, msg_id, wait_time, stop_time);
102603831d35Sstevel 
102703831d35Sstevel 	wakeup_next();
102803831d35Sstevel 
102903831d35Sstevel 	if (rc != 0) {
103003831d35Sstevel 
103103831d35Sstevel 		request->msg_status = response->msg_status = rc;
103203831d35Sstevel 		mutex_exit(mb_lock);
103303831d35Sstevel 
103403831d35Sstevel 		/* Free the waiter */
103503831d35Sstevel 		cv_destroy(&waiter->w_cv);
103603831d35Sstevel 		kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
103703831d35Sstevel 
103803831d35Sstevel 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x send rc = %d\n",
103903831d35Sstevel 		    f, msg_id, rc);
104003831d35Sstevel 
104103831d35Sstevel 		return (rc);
104203831d35Sstevel 	}
104303831d35Sstevel 
104403831d35Sstevel 	waiter->w_id = msg_id;
104503831d35Sstevel 
104603831d35Sstevel 	/*
104703831d35Sstevel 	 * Lock this waiter list and add the waiter
104803831d35Sstevel 	 */
104903831d35Sstevel 	mutex_enter(mbox_wait_lock);
105003831d35Sstevel 
105103831d35Sstevel 	if (master_mbox->mbox_wait_list[msg_type] == NULL) {
105203831d35Sstevel 		master_mbox->mbox_wait_list[msg_type] = waiter;
105303831d35Sstevel 		waiter->w_next = NULL;
105403831d35Sstevel 	} else {
105503831d35Sstevel 		struct sbbc_msg_waiter	*tmp;
105603831d35Sstevel 		tmp = master_mbox->mbox_wait_list[msg_type];
105703831d35Sstevel 		master_mbox->mbox_wait_list[msg_type] = waiter;
105803831d35Sstevel 		waiter->w_next = tmp;
105903831d35Sstevel 	}
106003831d35Sstevel 
106103831d35Sstevel 	mutex_exit(mb_lock);
106203831d35Sstevel 
106303831d35Sstevel 	/*
106403831d35Sstevel 	 * wait here for a response to our message
106503831d35Sstevel 	 * holding the mbox_wait_lock for the list ensures
106603831d35Sstevel 	 * that the interrupt handler can't get in before
106703831d35Sstevel 	 * we block.
106803831d35Sstevel 	 * NOTE: We use the request msg_type for the
106903831d35Sstevel 	 *	 the wait_list. This ensures that  the
107003831d35Sstevel 	 *	 msg_type won't change.
107103831d35Sstevel 	 */
107203831d35Sstevel 	clockleft = cv_timedwait(&waiter->w_cv, mbox_wait_lock, stop_time);
107303831d35Sstevel 
107403831d35Sstevel 	SGSBBC_DBG_MBOX("%s: msg_id = 0x%x is woken up for response\n",
107503831d35Sstevel 		f, msg_id);
107603831d35Sstevel 
107703831d35Sstevel 	/*
107803831d35Sstevel 	 * If we have timed out, set msg_status to ETIMEDOUT,
107903831d35Sstevel 	 * and remove the waiter from the waiter list.
108003831d35Sstevel 	 */
108103831d35Sstevel 	if (clockleft < 0) {
108203831d35Sstevel 		/*
108303831d35Sstevel 		 * Remove the waiter from the waiter list.
108403831d35Sstevel 		 * If we can't find the waiter in the list,
108503831d35Sstevel 		 * 1. msg_status == EINPROGRESS
108603831d35Sstevel 		 *    It is being processed.  We will give it
108703831d35Sstevel 		 *    a chance to finish.
108803831d35Sstevel 		 * 2. msg_status != EINPROGRESS
108903831d35Sstevel 		 *    It is done processing.  We can safely
109003831d35Sstevel 		 *    remove it.
109103831d35Sstevel 		 * If we can find the waiter, it has timed out.
109203831d35Sstevel 		 */
109303831d35Sstevel 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x has timed out\n",
109403831d35Sstevel 			f, msg_id);
109503831d35Sstevel 		if (mbox_find_waiter(msg_type, msg_id) == NULL) {
109603831d35Sstevel 			if (waiter->w_msg->msg_status == EINPROGRESS) {
109703831d35Sstevel 				SGSBBC_DBG_MBOX("%s: Waiting for msg_id = 0x%x "
109803831d35Sstevel 					"complete.\n", f, msg_id);
109903831d35Sstevel 				cv_wait(&waiter->w_cv, mbox_wait_lock);
110003831d35Sstevel 			}
110103831d35Sstevel 		} else {
110203831d35Sstevel 			SGSBBC_DBG_MBOX("%s: setting msg_id = 0x%x "
110303831d35Sstevel 				"to ETIMEDOUT\n", f, msg_id);
110403831d35Sstevel 			cmn_err(CE_NOTE, "Timed out waiting for SC response");
110503831d35Sstevel 			rc = waiter->w_msg->msg_status = ETIMEDOUT;
110603831d35Sstevel 		}
110703831d35Sstevel 	}
110803831d35Sstevel 
110903831d35Sstevel 	/*
111003831d35Sstevel 	 * lose the waiter
111103831d35Sstevel 	 */
111203831d35Sstevel 	cv_destroy(&waiter->w_cv);
111303831d35Sstevel 	kmem_free(waiter, sizeof (struct sbbc_msg_waiter));
111403831d35Sstevel 
111503831d35Sstevel 	mutex_exit(mbox_wait_lock);
111603831d35Sstevel 
111703831d35Sstevel 	return (rc);
111803831d35Sstevel 
111903831d35Sstevel }
112003831d35Sstevel 
112103831d35Sstevel static void
112203831d35Sstevel wakeup_next()
112303831d35Sstevel {
112403831d35Sstevel 	/*
112503831d35Sstevel 	 * Done sending the current message or encounter an error.
112603831d35Sstevel 	 * Wake up the one request in the outbox_queue.
112703831d35Sstevel 	 */
112803831d35Sstevel 	mutex_enter(&outbox_queue_lock);
112903831d35Sstevel 	outbox_busy = 0;
113003831d35Sstevel 	cv_signal(&outbox_queue);
113103831d35Sstevel 	mutex_exit(&outbox_queue_lock);
113203831d35Sstevel }
113303831d35Sstevel 
113403831d35Sstevel 
113503831d35Sstevel /* ARGSUSED */
113603831d35Sstevel int
113703831d35Sstevel sbbc_mbox_send_msg(sbbc_msg_t *msg, int flags, uint_t msg_id,
113803831d35Sstevel 	time_t wait_time, clock_t stop_time)
113903831d35Sstevel {
114003831d35Sstevel 	struct sbbc_mbox_header	header;
114103831d35Sstevel 	struct sbbc_fragment	frag;
114203831d35Sstevel 	int			rc = 0;
114303831d35Sstevel 	int			bytes_written;
114403831d35Sstevel 	uint32_t		intr_enabled;
114503831d35Sstevel 	clock_t			clockleft;
114603831d35Sstevel 	static fn_t		f = "sbbc_mbox_send_msg";
114703831d35Sstevel 
114803831d35Sstevel 	/*
114903831d35Sstevel 	 * First check that the SC has enabled its mailbox
115003831d35Sstevel 	 */
115103831d35Sstevel 	rc = iosram_read(SBBC_INTR_SC_ENABLED_KEY, 0,
115203831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled));
115303831d35Sstevel 
115403831d35Sstevel 	if (rc)
115503831d35Sstevel 		return (rc);
115603831d35Sstevel 
115703831d35Sstevel 	if (!(intr_enabled & SBBC_MAILBOX_OUT))
115803831d35Sstevel 		return (ENOTSUP);
115903831d35Sstevel 
116003831d35Sstevel 	/*
116103831d35Sstevel 	 * read the mailbox header
116203831d35Sstevel 	 */
116303831d35Sstevel 	if (rc = mbox_read_header(SBBC_OUTBOX, &header))
116403831d35Sstevel 		return (rc);
116503831d35Sstevel 
116603831d35Sstevel 	/*
116703831d35Sstevel 	 * Allocate/initialise a fragment for this message
116803831d35Sstevel 	 */
116903831d35Sstevel 	frag.f_id = msg_id;
117003831d35Sstevel 	frag.f_type = msg->msg_type;
117103831d35Sstevel 	frag.f_status = 0;
117203831d35Sstevel 	frag.f_total_len = msg->msg_len;
117303831d35Sstevel 	frag.f_frag_offset = 0;
117403831d35Sstevel 	/*
117503831d35Sstevel 	 * Throw in the message data
117603831d35Sstevel 	 */
117703831d35Sstevel 	bcopy(&msg->msg_data, &frag.f_data, sizeof (msg->msg_data));
117803831d35Sstevel 
117903831d35Sstevel 	/*
118003831d35Sstevel 	 * If not enough space is available
118103831d35Sstevel 	 * write what we can and wait for
118203831d35Sstevel 	 * an interrupt to tell us that more
118303831d35Sstevel 	 * space is available
118403831d35Sstevel 	 */
118503831d35Sstevel 
118603831d35Sstevel 	bytes_written = 0;
118703831d35Sstevel 	do {
118803831d35Sstevel 		rc = mbox_write(&header, &frag, msg);
118903831d35Sstevel 
119003831d35Sstevel 		if (rc != 0 && rc != ENOSPC) {
119103831d35Sstevel 			return (rc);
119203831d35Sstevel 		}
119303831d35Sstevel 
119403831d35Sstevel 		if (rc == 0) {
119503831d35Sstevel 			/*
119603831d35Sstevel 			 * Always tell the SC when there is a message.
119703831d35Sstevel 			 * Ignore returned value as not being able to
119803831d35Sstevel 			 * signal the SC about space available does
119903831d35Sstevel 			 * not stop the SC from processing input.
120003831d35Sstevel 			 */
120103831d35Sstevel 			(void) iosram_send_intr(SBBC_MAILBOX_OUT);
120203831d35Sstevel 		}
120303831d35Sstevel 
120403831d35Sstevel 		bytes_written += frag.f_frag_len;
120503831d35Sstevel 		frag.f_frag_offset += frag.f_frag_len;
120603831d35Sstevel 		if ((bytes_written < msg->msg_len) || (rc == ENOSPC)) {
120703831d35Sstevel 
120803831d35Sstevel 			if (mbox_has_free_space(&header) <=
120903831d35Sstevel 				sizeof (struct sbbc_fragment)) {
121003831d35Sstevel 
121103831d35Sstevel 				int tmprc;
121203831d35Sstevel 
121303831d35Sstevel 				clockleft = cv_timedwait(
121403831d35Sstevel 					&master_mbox->mbox_out->mb_full,
121503831d35Sstevel 					&master_mbox->mbox_out->mb_lock,
121603831d35Sstevel 					stop_time);
121703831d35Sstevel 
121803831d35Sstevel 				/* Return ETIMEDOUT if we timed out */
121903831d35Sstevel 				if (clockleft < 0) {
122003831d35Sstevel 					SGSBBC_DBG_MBOX("%s: msg_id = 0x%x "
122103831d35Sstevel 						"has timed out\n", f, msg_id);
122203831d35Sstevel 					cmn_err(CE_NOTE,
122303831d35Sstevel 						"Timed out sending message "
122403831d35Sstevel 						"to SC");
122503831d35Sstevel 					return (ETIMEDOUT);
122603831d35Sstevel 				}
122703831d35Sstevel 
122803831d35Sstevel 				/* Read updated header from IOSRAM */
122903831d35Sstevel 				if (tmprc = mbox_read_header(SBBC_OUTBOX,
123003831d35Sstevel 				    &header)) {
123103831d35Sstevel 
123203831d35Sstevel 					return (tmprc);
123303831d35Sstevel 				}
123403831d35Sstevel 			}
123503831d35Sstevel 		}
123603831d35Sstevel 
123703831d35Sstevel 		SGSBBC_DBG_MBOX("%s: msg_id = 0x%x, bytes_written = 0x%x, "
123803831d35Sstevel 			"msg_len = 0x%x\n", f,
123903831d35Sstevel 				msg_id, bytes_written, msg->msg_len);
124003831d35Sstevel 	} while ((bytes_written < msg->msg_len) || (rc == ENOSPC));
124103831d35Sstevel 
124203831d35Sstevel 	/*
124303831d35Sstevel 	 * this could be a spurious interrupt
124403831d35Sstevel 	 * as the SC may be merrily readings its
124503831d35Sstevel 	 * mail even as send, but what can you do ? No
124603831d35Sstevel 	 * synchronization method between SC <-> OS
124703831d35Sstevel 	 * SRAM data eaters means that this is inevitable.
124803831d35Sstevel 	 * It would take a bigger brain to fix this.
124903831d35Sstevel 	 *
125003831d35Sstevel 	 */
125103831d35Sstevel 	(void) iosram_send_intr(SBBC_MAILBOX_OUT);
125203831d35Sstevel 
125303831d35Sstevel 	return (rc);
125403831d35Sstevel }
125503831d35Sstevel 
125603831d35Sstevel 
125703831d35Sstevel /*
125803831d35Sstevel  * get next message
125903831d35Sstevel  * Read the next message from SRAM
126003831d35Sstevel  * Check if theres an entry on the wait queue
126103831d35Sstevel  * for this message
126203831d35Sstevel  * If yes, read the message in and signal
126303831d35Sstevel  * the waiter (if all the message has been received)
126403831d35Sstevel  * No, its unsolicited, if theres a handler installed for
126503831d35Sstevel  * this message type trigger it, otherwise toss
126603831d35Sstevel  * the message
126703831d35Sstevel  */
126803831d35Sstevel int
126903831d35Sstevel sbbc_mbox_recv_msg()
127003831d35Sstevel {
127103831d35Sstevel 	struct sbbc_mbox_header	header;
127203831d35Sstevel 	struct sbbc_fragment	frag;
127303831d35Sstevel 	sbbc_msg_t		tmpmsg;	/* Temporary msg storage */
127403831d35Sstevel 	int			rc = 0, i, first_hdlr, last_hdlr;
127503831d35Sstevel 	uint32_t		intr_enabled;
127603831d35Sstevel 	sbbc_intrs_t		*intr;
127703831d35Sstevel 	struct sbbc_msg_waiter	*waiter;
127803831d35Sstevel 	uint16_t		type;	/* frag.f_type.type */
127903831d35Sstevel 	uint32_t		f_id;	/* frag.f_id */
128003831d35Sstevel 	uint32_t		f_frag_offset, f_frag_len;
128103831d35Sstevel 	kmutex_t		*mbox_wait_lock;
128203831d35Sstevel 	static fn_t		f = "sbbc_mbox_recv_msg";
128303831d35Sstevel 
128403831d35Sstevel 	/*
128503831d35Sstevel 	 * First check that the OS has enabled its mailbox
128603831d35Sstevel 	 */
128703831d35Sstevel 	rc = iosram_read(SBBC_SC_INTR_ENABLED_KEY, 0,
128803831d35Sstevel 		(caddr_t)&intr_enabled, sizeof (intr_enabled));
128903831d35Sstevel 
129003831d35Sstevel 	if (rc) {
129103831d35Sstevel 		return (rc);
129203831d35Sstevel 	}
129303831d35Sstevel 
129403831d35Sstevel 	if (!(intr_enabled & SBBC_MAILBOX_IN))
129503831d35Sstevel 		return (ENOTSUP);
129603831d35Sstevel 
129703831d35Sstevel 	/*
129803831d35Sstevel 	 * read the mailbox header
129903831d35Sstevel 	 */
130003831d35Sstevel 	if (rc = mbox_read_header(SBBC_INBOX, &header))
130103831d35Sstevel 		return (rc);
130203831d35Sstevel 
130303831d35Sstevel 	/*
130403831d35Sstevel 	 * check if any messages available. If
130503831d35Sstevel 	 * consumer == producer then no more
130603831d35Sstevel 	 * messages
130703831d35Sstevel 	 */
130803831d35Sstevel 	if ((header.mailboxes[SBBC_INBOX].mbox_consumer ==
130903831d35Sstevel 		header.mailboxes[SBBC_INBOX].mbox_producer)) {
131003831d35Sstevel 
131103831d35Sstevel 		return (-1);
131203831d35Sstevel 	}
131303831d35Sstevel 
131403831d35Sstevel 	/*
131503831d35Sstevel 	 * read the fragment header for this message
131603831d35Sstevel 	 */
131703831d35Sstevel 	if (rc = mbox_read_frag(&header, &frag)) {
131803831d35Sstevel 
131903831d35Sstevel 		return (rc);
132003831d35Sstevel 	}
132103831d35Sstevel 
132203831d35Sstevel 	/* Save to local variable for easy reading */
132303831d35Sstevel 	type = frag.f_type.type;
132403831d35Sstevel 	f_id = frag.f_id;
132503831d35Sstevel 
132603831d35Sstevel 	SGSBBC_DBG_MBOX("%s: f_id = 0x%x\n", f, f_id);
132703831d35Sstevel 
132803831d35Sstevel 	/*
132903831d35Sstevel 	 * check the message type. If its invalid, we will
133003831d35Sstevel 	 * just toss the message
133103831d35Sstevel 	 */
133203831d35Sstevel 	if (type >= SBBC_MBOX_MSG_TYPES) {
133303831d35Sstevel 		goto done;
133403831d35Sstevel 	}
133503831d35Sstevel 
133603831d35Sstevel 	/*
133703831d35Sstevel 	 * if theres no waiters for this message type, and theres
133803831d35Sstevel 	 * no message handler installed, toss it.
133903831d35Sstevel 	 *
134003831d35Sstevel 	 * Unsolicited messages (f_id == 0) are tricky because we won't know
134103831d35Sstevel 	 * when the handler has finished so that we can
134203831d35Sstevel 	 * remove the message, so, given the small brains in operation
134303831d35Sstevel 	 * here, what we do is restrict junk mail to zero-length
134403831d35Sstevel 	 * messages, then we allocate a fragment using kmem,
134503831d35Sstevel 	 * make a copy of the fragment in this memory,
134603831d35Sstevel 	 * pass this pointer to the fragment, then skip the message.
134703831d35Sstevel 	 * So even if there is data associated with the junkmail,
134803831d35Sstevel 	 * the message handler doesn't get to see it
134903831d35Sstevel 	 * We expect the mesaage handler to free the memory.
135003831d35Sstevel 	 */
135103831d35Sstevel 	if (type == SBBC_BROADCAST_MSG) {
135203831d35Sstevel 		/*
135303831d35Sstevel 		 * Broadcast message, trigger all handlers
135403831d35Sstevel 		 */
135503831d35Sstevel 		first_hdlr = 0;
135603831d35Sstevel 		last_hdlr = SBBC_MBOX_MSG_TYPES - 1;
135703831d35Sstevel 	} else if ((master_mbox->mbox_wait_list[type] == NULL) || (f_id == 0)) {
135803831d35Sstevel 		/*
135903831d35Sstevel 		 * Theres no waiters, or its unsolicited anyway
136003831d35Sstevel 		 */
136103831d35Sstevel 		first_hdlr = last_hdlr = type;
136203831d35Sstevel 	} else {
136303831d35Sstevel 		/*
136403831d35Sstevel 		 * check the fragment message type, look at the wait list for
136503831d35Sstevel 		 * that type to find its associated message
136603831d35Sstevel 		 *
136703831d35Sstevel 		 * First find the message. If we get it, take it off
136803831d35Sstevel 		 * the waiter list and read the data. We will
136903831d35Sstevel 		 * put it back on the list if necessary.
137003831d35Sstevel 		 * This avoids the problem of a second message-in
137103831d35Sstevel 		 * interrupt playing with this waiter.
137203831d35Sstevel 		 * This will cut down on mutex spinning on the wait
137303831d35Sstevel 		 * list locks, also, expect the next fragment to be
137403831d35Sstevel 		 * for this messageso we might as well have it at the
137503831d35Sstevel 		 * start of the list.
137603831d35Sstevel 		 *
137703831d35Sstevel 		 * its possible that a return message has a different type,
137803831d35Sstevel 		 * (possible but not recommended!). So, if we don't find
137903831d35Sstevel 		 * it on the list pointed to by the request type,
138003831d35Sstevel 		 * go look at all the other lists
138103831d35Sstevel 		 */
138203831d35Sstevel 
138303831d35Sstevel 		mbox_wait_lock = &master_mbox->mbox_wait_lock[type];
138403831d35Sstevel 
138503831d35Sstevel 		mutex_enter(mbox_wait_lock);
138603831d35Sstevel 		if ((waiter = mbox_find_waiter(type, f_id)) == NULL) {
138703831d35Sstevel 			for (i = 0; i < SBBC_MBOX_MSG_TYPES; i++) {
138803831d35Sstevel 				if (i == type)
138903831d35Sstevel 					continue;
139003831d35Sstevel 				if ((waiter = mbox_find_waiter(i, f_id))
139103831d35Sstevel 					!= NULL)
139203831d35Sstevel 					break;
139303831d35Sstevel 			}
139403831d35Sstevel 		}
139503831d35Sstevel 		mutex_exit(mbox_wait_lock);
139603831d35Sstevel 
139703831d35Sstevel 		if (waiter == NULL) {
139803831d35Sstevel 			rc = -1;
139903831d35Sstevel 			/*
140003831d35Sstevel 			 * there's no waiter for this message, but that
140103831d35Sstevel 			 * could mean that this message is the start of
140203831d35Sstevel 			 * a send/receive to us, and every 'first' request
140303831d35Sstevel 			 * must by definition be unsolicited,
140403831d35Sstevel 			 * so trigger the handler
140503831d35Sstevel 			 */
140603831d35Sstevel 			first_hdlr = last_hdlr = type;
140703831d35Sstevel 		} else {
140803831d35Sstevel 			SGSBBC_DBG_MBOX("%s: f_id = 0x%x, msg_id = 0x%x, "
140903831d35Sstevel 				"msg_len = 0x%x\n",
141003831d35Sstevel 					f, f_id, waiter->w_id,
141103831d35Sstevel 					waiter->w_msg->msg_len);
141203831d35Sstevel 
141303831d35Sstevel 			rc = mbox_read(&header, &frag, waiter->w_msg);
141403831d35Sstevel 
141503831d35Sstevel 			SGSBBC_DBG_MBOX("%s: f_id = 0x%x, offset = 0x%x, "
141603831d35Sstevel 				"len = 0x%x, total_len = 0x%x\n",
141703831d35Sstevel 					f, frag.f_id, frag.f_frag_offset,
141803831d35Sstevel 					frag.f_frag_len, frag.f_total_len);
141903831d35Sstevel 
142003831d35Sstevel 			if (rc || ((frag.f_frag_offset + frag.f_frag_len) ==
142103831d35Sstevel 				frag.f_total_len)) {
142203831d35Sstevel 				/*
142303831d35Sstevel 				 * failed or all the message has been read in
142403831d35Sstevel 				 */
142503831d35Sstevel 				mutex_enter(mbox_wait_lock);
142603831d35Sstevel 				waiter->w_msg->msg_status = (rc == ENOMEM)?
142703831d35Sstevel 					rc : frag.f_status;
142803831d35Sstevel 				SGSBBC_DBG_MBOX("%s: msg_status = %d\n",
142903831d35Sstevel 					f, waiter->w_msg->msg_status);
143003831d35Sstevel 				cv_signal(&waiter->w_cv);
143103831d35Sstevel 				mutex_exit(mbox_wait_lock);
143203831d35Sstevel 
143303831d35Sstevel 			} else {
143403831d35Sstevel 				/*
143503831d35Sstevel 				 * back on the wait list
143603831d35Sstevel 				 */
143703831d35Sstevel 				mutex_enter(mbox_wait_lock);
143803831d35Sstevel 				if (waiter->w_msg->msg_status == ETIMEDOUT) {
143903831d35Sstevel 					cv_signal(&waiter->w_cv);
144003831d35Sstevel 					mutex_exit(mbox_wait_lock);
144103831d35Sstevel 					goto done;
144203831d35Sstevel 				}
144303831d35Sstevel 
144403831d35Sstevel 				if (master_mbox->mbox_wait_list[type] == NULL) {
144503831d35Sstevel 					master_mbox->mbox_wait_list[type] =
144603831d35Sstevel 						waiter;
144703831d35Sstevel 					waiter->w_next = NULL;
144803831d35Sstevel 				} else {
144903831d35Sstevel 					struct sbbc_msg_waiter	*tmp;
145003831d35Sstevel 					tmp = master_mbox->mbox_wait_list[type];
145103831d35Sstevel 					master_mbox->mbox_wait_list[type] =
145203831d35Sstevel 						waiter;
145303831d35Sstevel 					waiter->w_next = tmp;
145403831d35Sstevel 				}
145503831d35Sstevel 				mutex_exit(mbox_wait_lock);
145603831d35Sstevel 			}
145703831d35Sstevel 			goto done;
145803831d35Sstevel 		}
145903831d35Sstevel 	}
146003831d35Sstevel 
146103831d35Sstevel 	/*
146203831d35Sstevel 	 * Set msg_len to f_frag_len so msg_buf will be large enough
146303831d35Sstevel 	 * to contain what is in the fragment.
146403831d35Sstevel 	 */
146503831d35Sstevel 	f_frag_len = tmpmsg.msg_len = frag.f_frag_len;
146603831d35Sstevel 	/*
146703831d35Sstevel 	 * Save the f_frag_offset for copying into client's space.
146803831d35Sstevel 	 * Set frag.f_frag_offset to 0 so we don't have to allocate
146903831d35Sstevel 	 * too much space for reading in the message.
147003831d35Sstevel 	 */
147103831d35Sstevel 	f_frag_offset = frag.f_frag_offset;
147203831d35Sstevel 	frag.f_frag_offset = 0;
147303831d35Sstevel 
147403831d35Sstevel 	/* Allocate space for msg_buf */
147503831d35Sstevel 	if (f_frag_len != 0 && (tmpmsg.msg_buf =
147603831d35Sstevel 		kmem_alloc(f_frag_len, KM_NOSLEEP)) == NULL) {
147703831d35Sstevel 
147803831d35Sstevel 		rc = ENOMEM;
147903831d35Sstevel 		cmn_err(CE_WARN, "Can't allocate memory"
148003831d35Sstevel 			" for unsolicited messages\n");
148103831d35Sstevel 	} else {
148203831d35Sstevel 		/* Save the incoming message in tmpmsg */
148303831d35Sstevel 		rc = mbox_read(&header, &frag, &tmpmsg);
148403831d35Sstevel 
148503831d35Sstevel 		for (i = first_hdlr; rc == 0 && i <= last_hdlr; i++) {
148603831d35Sstevel 
148703831d35Sstevel 			intr = master_mbox->intrs[i];
148803831d35Sstevel 			if ((intr == NULL) || (intr->sbbc_intr_id == 0)) {
148903831d35Sstevel 				continue;
149003831d35Sstevel 			}
149103831d35Sstevel 
149203831d35Sstevel 			while (intr != NULL) {
149303831d35Sstevel 				/*
149403831d35Sstevel 				 * If the client has allocated enough space
149503831d35Sstevel 				 * for incoming message, copy into the
149603831d35Sstevel 				 * client buffer.
149703831d35Sstevel 				 */
149803831d35Sstevel 				sbbc_msg_t *arg = (sbbc_msg_t *)intr->sbbc_arg;
149903831d35Sstevel 				if (arg != (void *)NULL) {
150003831d35Sstevel 					if (arg->msg_len >= frag.f_total_len) {
150103831d35Sstevel 						if (f_frag_len > 0)
150203831d35Sstevel 							bcopy(tmpmsg.msg_buf,
150303831d35Sstevel 								arg->msg_buf +
150403831d35Sstevel 								f_frag_offset,
150503831d35Sstevel 								f_frag_len);
150603831d35Sstevel 					} else {
150703831d35Sstevel 						arg->msg_status = ENOMEM;
150803831d35Sstevel 					}
150903831d35Sstevel 				}
151003831d35Sstevel 
151103831d35Sstevel 				/*
151203831d35Sstevel 				 * Only trigger the interrupt when we
151303831d35Sstevel 				 * have received the whole message.
151403831d35Sstevel 				 */
151503831d35Sstevel 				if (f_frag_offset + f_frag_len ==
151603831d35Sstevel 					frag.f_total_len) {
151703831d35Sstevel 
151803831d35Sstevel 					ddi_trigger_softintr(
151903831d35Sstevel 						intr->sbbc_intr_id);
152003831d35Sstevel 				}
152103831d35Sstevel 				intr = intr->sbbc_intr_next;
152203831d35Sstevel 			}
152303831d35Sstevel 		}
152403831d35Sstevel 
152503831d35Sstevel 		if (f_frag_len != 0) {
152603831d35Sstevel 			/* Don't forget to free the buffer */
152703831d35Sstevel 			kmem_free(tmpmsg.msg_buf, f_frag_len);
152803831d35Sstevel 		}
152903831d35Sstevel 	}
153003831d35Sstevel done:
153103831d35Sstevel 	mbox_skip_next_msg(&header);
153203831d35Sstevel 	return (rc);
153303831d35Sstevel }
153403831d35Sstevel 
153503831d35Sstevel /*
153603831d35Sstevel  * available free space in the outbox
153703831d35Sstevel  */
153803831d35Sstevel static int
153903831d35Sstevel mbox_has_free_space(struct sbbc_mbox_header *header)
154003831d35Sstevel {
154103831d35Sstevel 	uint32_t	space = 0;
154203831d35Sstevel 
154303831d35Sstevel 	ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
154403831d35Sstevel 
154503831d35Sstevel 	if (header->mailboxes[SBBC_OUTBOX].mbox_producer ==
154603831d35Sstevel 		header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
154703831d35Sstevel 		/*
154803831d35Sstevel 		 * mailbox is empty
154903831d35Sstevel 		 */
155003831d35Sstevel 		space += header->mailboxes[SBBC_OUTBOX].mbox_len -
155103831d35Sstevel 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
155203831d35Sstevel 		space +=
155303831d35Sstevel 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
155403831d35Sstevel 	} else if (header->mailboxes[SBBC_OUTBOX].mbox_producer >
155503831d35Sstevel 		header->mailboxes[SBBC_OUTBOX].mbox_consumer) {
155603831d35Sstevel 		space += header->mailboxes[SBBC_OUTBOX].mbox_len -
155703831d35Sstevel 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
155803831d35Sstevel 		space += header->mailboxes[SBBC_OUTBOX].mbox_consumer;
155903831d35Sstevel 	} else {
156003831d35Sstevel 		/*
156103831d35Sstevel 		 * mailbox wrapped around
156203831d35Sstevel 		 */
156303831d35Sstevel 		space += header->mailboxes[SBBC_OUTBOX].mbox_consumer -
156403831d35Sstevel 			header->mailboxes[SBBC_OUTBOX].mbox_producer;
156503831d35Sstevel 	}
156603831d35Sstevel 
156703831d35Sstevel 	/*
156803831d35Sstevel 	 * Need to make sure that the mailbox never
156903831d35Sstevel 	 * gets completely full, as consumer == producer is
157003831d35Sstevel 	 * our test for empty, so we drop MBOX_ALIGN_BYTES.
157103831d35Sstevel 	 */
157203831d35Sstevel 
157303831d35Sstevel 	if (space >= MBOX_ALIGN_BYTES)
157403831d35Sstevel 		space -= MBOX_ALIGN_BYTES;
157503831d35Sstevel 	else
157603831d35Sstevel 		space = 0;
157703831d35Sstevel 
157803831d35Sstevel 	return (space);
157903831d35Sstevel 
158003831d35Sstevel }
158103831d35Sstevel /*
158203831d35Sstevel  * Write the data to IOSRAM
158303831d35Sstevel  * Update the SRAM mailbox header
158403831d35Sstevel  * Update the local mailbox pointers
158503831d35Sstevel  * Only write a single fragment. If possible,
158603831d35Sstevel  * put the whole message into a fragment.
158703831d35Sstevel  *
158803831d35Sstevel  * Note: We assume that there is no 'max' message
158903831d35Sstevel  *	 size. We will just keep fragmenting.
159003831d35Sstevel  * Note: We always write to SBBC_OUTBOX and
159103831d35Sstevel  *	 read from SBBC_INBOX
159203831d35Sstevel  *
159303831d35Sstevel  * If we get an error at any time, return immediately
159403831d35Sstevel  * without updating the mailbox header in SRAM
159503831d35Sstevel  */
159603831d35Sstevel static int
159703831d35Sstevel mbox_write(struct sbbc_mbox_header *header,
159803831d35Sstevel 	struct sbbc_fragment *frag, sbbc_msg_t *msg)
159903831d35Sstevel {
160003831d35Sstevel 	int		bytes_written, bytes_remaining, free_space;
160103831d35Sstevel 	int		rc = 0;
160203831d35Sstevel 	caddr_t		src;
160303831d35Sstevel 	uint32_t	sram_dst;
160403831d35Sstevel 	int		space_at_end, space_at_start;
160503831d35Sstevel 	uint32_t	mbox_offset, mbox_len;
160603831d35Sstevel 	uint32_t	mbox_producer, mbox_consumer;
160703831d35Sstevel 	uint32_t	f_total_len, f_frag_offset;
160803831d35Sstevel 	uint32_t	frag_header_size;
160903831d35Sstevel 	static fn_t	f = "mbox_write";
161003831d35Sstevel 
161103831d35Sstevel 	ASSERT(MUTEX_HELD(&master_mbox->mbox_out->mb_lock));
161203831d35Sstevel 
161303831d35Sstevel 	/*
161403831d35Sstevel 	 * Save to local variables to make code more readable
161503831d35Sstevel 	 */
161603831d35Sstevel 	mbox_offset = header->mailboxes[SBBC_OUTBOX].mbox_offset;
161703831d35Sstevel 	mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
161803831d35Sstevel 	mbox_producer = header->mailboxes[SBBC_OUTBOX].mbox_producer;
161903831d35Sstevel 	mbox_consumer = header->mailboxes[SBBC_OUTBOX].mbox_consumer;
162003831d35Sstevel 	f_total_len = frag->f_total_len;
162103831d35Sstevel 	f_frag_offset = frag->f_frag_offset;
162203831d35Sstevel 	frag_header_size = sizeof (struct sbbc_fragment);
162303831d35Sstevel 
162403831d35Sstevel 	SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, "
162503831d35Sstevel 		"mbox_producer = 0x%x\n", f, mbox_consumer, mbox_producer);
162603831d35Sstevel 
162703831d35Sstevel 	/*
162803831d35Sstevel 	 * Write pointer in SRAM
162903831d35Sstevel 	 */
163003831d35Sstevel 	sram_dst = mbox_offset + mbox_producer;
163103831d35Sstevel 
163203831d35Sstevel 	/*
163303831d35Sstevel 	 * NB We assume that the consumer stays constant
163403831d35Sstevel 	 *    during the write. It may not necessarily
163503831d35Sstevel 	 *    be the case but it won't cause us any problems, just means
163603831d35Sstevel 	 *    we fragment more than is absolutely necessary
163703831d35Sstevel 	 *
163803831d35Sstevel 	 * possible cases
163903831d35Sstevel 	 * 1) consumer == producer, mailbox empty
164003831d35Sstevel 	 *	space_at_end == mailbox end - producer
164103831d35Sstevel 	 *	space_at_start == producer - MBOX_ALIGN_BYTES
164203831d35Sstevel 	 * 2) producer < consumer
164303831d35Sstevel 	 *	space_at_end = (consumer - producer - MBOX_ALIGN_BYTES)
164403831d35Sstevel 	 *	space_at_start == 0
164503831d35Sstevel 	 * 3) producer > consumer
164603831d35Sstevel 	 *	space_at_end = mailbox end - producer
164703831d35Sstevel 	 *	space_at_start = consumer - MBOX_ALIGN_BYTES
164803831d35Sstevel 	 *
164903831d35Sstevel 	 * (space - MBOX_ALIGN_BYTES) because we need to avoid the
165003831d35Sstevel 	 * scenario where the producer wraps around completely and
165103831d35Sstevel 	 * producer == consumer, as this is our test for 'empty'.
165203831d35Sstevel 	 * Also we want it to be 8-byte aligned.
165303831d35Sstevel 	 * Note: start is assumed = 0
165403831d35Sstevel 	 */
165503831d35Sstevel 	if (mbox_producer < mbox_consumer) {
165603831d35Sstevel 		space_at_end = mbox_consumer - mbox_producer - MBOX_ALIGN_BYTES;
165703831d35Sstevel 		if (space_at_end < 0)
165803831d35Sstevel 			space_at_end = 0;
165903831d35Sstevel 		space_at_start = 0;
166003831d35Sstevel 	} else {
166103831d35Sstevel 		space_at_end = mbox_len - mbox_producer;
166203831d35Sstevel 		if (mbox_consumer == 0)
166303831d35Sstevel 			space_at_end -= MBOX_ALIGN_BYTES;
166403831d35Sstevel 		space_at_start = mbox_consumer - MBOX_ALIGN_BYTES;
166503831d35Sstevel 		if (space_at_start < 0)
166603831d35Sstevel 			space_at_start = 0;
166703831d35Sstevel 	}
166803831d35Sstevel 
166903831d35Sstevel 	SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
167003831d35Sstevel 		f, space_at_end, space_at_start);
167103831d35Sstevel 
167203831d35Sstevel 	free_space = space_at_end + space_at_start;
167303831d35Sstevel 
167403831d35Sstevel 	if (free_space < frag_header_size) {
167503831d35Sstevel 		/*
167603831d35Sstevel 		 * can't even write a fragment header, so just return
167703831d35Sstevel 		 * the caller will block waiting for space
167803831d35Sstevel 		 */
167903831d35Sstevel 		frag->f_frag_len = 0;
168003831d35Sstevel 		return (ENOSPC);
168103831d35Sstevel 	}
168203831d35Sstevel 
168303831d35Sstevel 	/*
168403831d35Sstevel 	 * How many bytes will be in the fragment ?
168503831d35Sstevel 	 */
168603831d35Sstevel 	bytes_remaining = f_total_len - f_frag_offset;
168703831d35Sstevel 	frag->f_frag_len = min(bytes_remaining, free_space - frag_header_size);
168803831d35Sstevel 
168903831d35Sstevel 	SGSBBC_DBG_MBOX("%s: writing header:sram_dst = 0x%x\n",
169003831d35Sstevel 		f, sram_dst);
169103831d35Sstevel 
169203831d35Sstevel 	/*
169303831d35Sstevel 	 * we can write the fragment header and some data
169403831d35Sstevel 	 * First, the fragment header
169503831d35Sstevel 	 */
169603831d35Sstevel 	if (space_at_end >=  frag_header_size) {
169703831d35Sstevel 		rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, (caddr_t)frag,
169803831d35Sstevel 			frag_header_size);
169903831d35Sstevel 		if (rc)
170003831d35Sstevel 			return (rc);
170103831d35Sstevel 
170203831d35Sstevel 		sram_dst = (uint32_t)(sram_dst + frag_header_size);
170303831d35Sstevel 		/*
170403831d35Sstevel 		 * Wrap around if we reach the end
170503831d35Sstevel 		 */
170603831d35Sstevel 		if (sram_dst >= (mbox_len + mbox_offset)) {
170703831d35Sstevel 			sram_dst = mbox_offset;
170803831d35Sstevel 		}
170903831d35Sstevel 		space_at_end -= frag_header_size;
171003831d35Sstevel 	} else {
171103831d35Sstevel 		/* wraparound */
171203831d35Sstevel 		if (space_at_end) {
171303831d35Sstevel 			rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
171403831d35Sstevel 				(caddr_t)frag, space_at_end);
171503831d35Sstevel 			if (rc)
171603831d35Sstevel 				return (rc);
171703831d35Sstevel 			sram_dst = (uint32_t)mbox_offset;
171803831d35Sstevel 		}
171903831d35Sstevel 		rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst,
172003831d35Sstevel 			(caddr_t)((caddr_t)frag + space_at_end),
172103831d35Sstevel 			(frag_header_size - space_at_end));
172203831d35Sstevel 		if (rc)
172303831d35Sstevel 			return (rc);
172403831d35Sstevel 		sram_dst += frag_header_size - space_at_end;
172503831d35Sstevel 		space_at_start -= (frag_header_size - space_at_end);
172603831d35Sstevel 		space_at_end = 0;
172703831d35Sstevel 	}
172803831d35Sstevel 
172903831d35Sstevel 	SGSBBC_DBG_MBOX("%s: space_at_end = 0x%x, space_at_start = 0x%x\n",
173003831d35Sstevel 		f, space_at_end, space_at_start);
173103831d35Sstevel 
173203831d35Sstevel 	/*
173303831d35Sstevel 	 * Now the fragment data
173403831d35Sstevel 	 */
173503831d35Sstevel 	free_space -= frag_header_size;
173603831d35Sstevel 	src = (caddr_t)(msg->msg_buf + f_frag_offset);
173703831d35Sstevel 	bytes_written = 0;
173803831d35Sstevel 	if (space_at_end) {
173903831d35Sstevel 		SGSBBC_DBG_MBOX("%s: writing data:sram_dst = 0x%x, "
174003831d35Sstevel 			"bytes_remaining = 0x%x\n",
174103831d35Sstevel 				f, sram_dst, bytes_remaining);
174203831d35Sstevel 
174303831d35Sstevel 		if (space_at_end < bytes_remaining)
174403831d35Sstevel 			bytes_written = space_at_end;
174503831d35Sstevel 		else
174603831d35Sstevel 			bytes_written = bytes_remaining;
174703831d35Sstevel 		rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
174803831d35Sstevel 			bytes_written);
174903831d35Sstevel 		if (rc)
175003831d35Sstevel 			return (rc);
175103831d35Sstevel 
175203831d35Sstevel 		sram_dst = (uint32_t)(sram_dst + bytes_written);
175303831d35Sstevel 		/*
175403831d35Sstevel 		 * Wrap around if we reach the end
175503831d35Sstevel 		 */
175603831d35Sstevel 		if (sram_dst >= (mbox_len + mbox_offset)) {
175703831d35Sstevel 			sram_dst = mbox_offset;
175803831d35Sstevel 		}
175903831d35Sstevel 		src = (caddr_t)(src + bytes_written);
176003831d35Sstevel 		bytes_remaining -= bytes_written;
176103831d35Sstevel 	}
176203831d35Sstevel 
176303831d35Sstevel 	if ((bytes_remaining > 0) && space_at_start) {
176403831d35Sstevel 		SGSBBC_DBG_MBOX("%s: writing the rest:sram_dst = 0x%x, "
176503831d35Sstevel 			"bytes_remaining = 0x%x\n",
176603831d35Sstevel 				f, sram_dst, bytes_remaining);
176703831d35Sstevel 		if (space_at_start < bytes_remaining) {
176803831d35Sstevel 			rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
176903831d35Sstevel 				space_at_start);
177003831d35Sstevel 			bytes_written += space_at_start;
177103831d35Sstevel 		} else {
177203831d35Sstevel 			rc = iosram_write(SBBC_MAILBOX_KEY, sram_dst, src,
177303831d35Sstevel 				bytes_remaining);
177403831d35Sstevel 			bytes_written += bytes_remaining;
177503831d35Sstevel 		}
177603831d35Sstevel 		if (rc)
177703831d35Sstevel 			return (rc);
177803831d35Sstevel 	}
177903831d35Sstevel 
178003831d35Sstevel 	frag->f_frag_len = bytes_written;
178103831d35Sstevel 
178203831d35Sstevel 	/*
178303831d35Sstevel 	 * update header->mbox_producer (bytes_written + frag_size)
178403831d35Sstevel 	 */
178503831d35Sstevel 	sram_dst = mbox_producer + bytes_written + frag_header_size;
178603831d35Sstevel 	if (sram_dst >= mbox_len) {
178703831d35Sstevel 		sram_dst = sram_dst % mbox_len;
178803831d35Sstevel 	}
178903831d35Sstevel 
179003831d35Sstevel 	SGSBBC_DBG_MBOX("%s: after writing data:sram_dst = 0x%x, "
179103831d35Sstevel 		"bytes_written = 0x%x\n", f, sram_dst, bytes_written);
179203831d35Sstevel 
179303831d35Sstevel 	header->mailboxes[SBBC_OUTBOX].mbox_producer = sram_dst;
179403831d35Sstevel 
179503831d35Sstevel 	mbox_update_header(SBBC_OUTBOX, header);
179603831d35Sstevel 
179703831d35Sstevel 
179803831d35Sstevel 	return (rc);
179903831d35Sstevel }
180003831d35Sstevel 
180103831d35Sstevel 
180203831d35Sstevel /*
180303831d35Sstevel  * Get the next frag from IOSRAM.
180403831d35Sstevel  * Write it to the corresponding msg buf.
180503831d35Sstevel  * The caller must update the SRAM pointers etc.
180603831d35Sstevel  */
180703831d35Sstevel static int
180803831d35Sstevel mbox_read(struct sbbc_mbox_header *header,
180903831d35Sstevel 	struct sbbc_fragment *frag, sbbc_msg_t *msg)
181003831d35Sstevel {
181103831d35Sstevel 	int			rc = 0;
181203831d35Sstevel 	uint32_t		sram_src, sram_end;
181303831d35Sstevel 	caddr_t			msg_buf;
181403831d35Sstevel 	int			bytes_at_start, bytes_at_end;
181503831d35Sstevel 	int			bytes_to_read;
181603831d35Sstevel 	uint32_t		frag_header_size, frag_total_size;
181703831d35Sstevel 	uint32_t		f_frag_offset, f_frag_len;
181803831d35Sstevel 	uint32_t		mbox_producer, mbox_consumer;
181903831d35Sstevel 	uint32_t		mbox_len, mbox_offset;
182003831d35Sstevel 	static fn_t		f = "mbox_read";
182103831d35Sstevel 
182203831d35Sstevel 	ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
182303831d35Sstevel 
182403831d35Sstevel 	/*
182503831d35Sstevel 	 * Save to local variables to make code more readable
182603831d35Sstevel 	 */
182703831d35Sstevel 	mbox_producer = header->mailboxes[SBBC_INBOX].mbox_producer;
182803831d35Sstevel 	mbox_consumer = header->mailboxes[SBBC_INBOX].mbox_consumer;
182903831d35Sstevel 	mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
183003831d35Sstevel 	mbox_offset = header->mailboxes[SBBC_INBOX].mbox_offset;
183103831d35Sstevel 	frag_header_size = sizeof (struct sbbc_fragment);
183203831d35Sstevel 	f_frag_offset = frag->f_frag_offset;
183303831d35Sstevel 	f_frag_len = frag->f_frag_len;
183403831d35Sstevel 	frag_total_size = frag_header_size + f_frag_len;
183503831d35Sstevel 
183603831d35Sstevel 	/*
183703831d35Sstevel 	 * If the message buffer size is smaller than the fragment
183803831d35Sstevel 	 * size, return an error.
183903831d35Sstevel 	 */
184003831d35Sstevel 	if (msg->msg_len < f_frag_len)  {
184103831d35Sstevel 		rc = ENOMEM;
184203831d35Sstevel 		goto done;
184303831d35Sstevel 	}
184403831d35Sstevel 
184503831d35Sstevel 	msg_buf = (caddr_t)(msg->msg_buf + f_frag_offset);
184603831d35Sstevel 
184703831d35Sstevel 	/*
184803831d35Sstevel 	 * Throw in the message data
184903831d35Sstevel 	 */
185003831d35Sstevel 	bcopy(&frag->f_data, &msg->msg_data, sizeof (msg->msg_data));
185103831d35Sstevel 
185203831d35Sstevel 	/*
185303831d35Sstevel 	 * We have it all, waiter, message, so lets
185403831d35Sstevel 	 * go get that puppy!
185503831d35Sstevel 	 * Message could be in one or two chunks -
185603831d35Sstevel 	 * consumer < producer: 1 chunk, (producer - consumer)
185703831d35Sstevel 	 * consumer > producer: 2 chunks, (end - consumer)
185803831d35Sstevel 	 *				 (producer - start)
185903831d35Sstevel 	 */
186003831d35Sstevel 	sram_end =  (uint32_t)(mbox_offset + mbox_len);
186103831d35Sstevel 	sram_src = (uint32_t)(mbox_offset + mbox_consumer + frag_header_size);
186203831d35Sstevel 
186303831d35Sstevel 	/*
186403831d35Sstevel 	 * wraparound
186503831d35Sstevel 	 */
186603831d35Sstevel 	if (sram_src >= sram_end)
186703831d35Sstevel 		sram_src -= mbox_len;
186803831d35Sstevel 
186903831d35Sstevel 	/*
187003831d35Sstevel 	 * find where the data is
187103831d35Sstevel 	 * possible cases
187203831d35Sstevel 	 * 1) consumer == producer, mailbox empty
187303831d35Sstevel 	 *	error
187403831d35Sstevel 	 * 2) producer < consumer
187503831d35Sstevel 	 *	bytes_at_end =  mailbox end - consumer
187603831d35Sstevel 	 *	bytes_at_start = producer
187703831d35Sstevel 	 * 3) producer > consumer
187803831d35Sstevel 	 *	bytes_at_end =  producer - consumer
187903831d35Sstevel 	 *	bytes_at_start = 0
188003831d35Sstevel 	 */
188103831d35Sstevel 
188203831d35Sstevel 	SGSBBC_DBG_MBOX("%s: mbox_consumer = 0x%x, mbox_producer = 0x%x, "
188303831d35Sstevel 		"frag_len = 0x%x\n",
188403831d35Sstevel 			f, mbox_consumer, mbox_producer, f_frag_len);
188503831d35Sstevel 
188603831d35Sstevel 	if (mbox_producer == mbox_consumer) {
188703831d35Sstevel 		bytes_at_end = bytes_at_start = 0;
188803831d35Sstevel 	} else if (mbox_producer < mbox_consumer) {
188903831d35Sstevel 		bytes_at_end = mbox_len - mbox_consumer;
189003831d35Sstevel 		bytes_at_start = mbox_producer;
189103831d35Sstevel 	} else {
189203831d35Sstevel 		bytes_at_end = mbox_producer - mbox_consumer;
189303831d35Sstevel 		bytes_at_start = 0;
189403831d35Sstevel 	}
189503831d35Sstevel 
189603831d35Sstevel 	SGSBBC_DBG_MBOX("%s: bytes_at_end = 0x%x, "
189703831d35Sstevel 		"bytes_at_start = 0x%x\n", f, bytes_at_end, bytes_at_start);
189803831d35Sstevel 
189903831d35Sstevel 	if ((bytes_at_end + bytes_at_start) < frag_total_size) {
190003831d35Sstevel 
190103831d35Sstevel 		/*
190203831d35Sstevel 		 * mailbox is corrupt
190303831d35Sstevel 		 * but what to do ?
190403831d35Sstevel 		 */
190503831d35Sstevel 		cmn_err(CE_PANIC, "Corrupt INBOX!\n"
190603831d35Sstevel 		    "producer = %x, consumer = %x, bytes_at_start = %x, "
190703831d35Sstevel 		    "bytes_at_end = %x\n", mbox_producer, mbox_consumer,
190803831d35Sstevel 		    bytes_at_start, bytes_at_end);
190903831d35Sstevel 	}
191003831d35Sstevel 
191103831d35Sstevel 	/*
191203831d35Sstevel 	 * If bytes_at_end is greater than header size, read the
191303831d35Sstevel 	 * part at the end of the mailbox, and then update the
191403831d35Sstevel 	 * pointers and bytes_to_read.
191503831d35Sstevel 	 */
191603831d35Sstevel 	if (bytes_at_end > frag_header_size) {
191703831d35Sstevel 		/*
191803831d35Sstevel 		 * We are only interested in the data segment.
191903831d35Sstevel 		 */
192003831d35Sstevel 		bytes_at_end -= frag_header_size;
192103831d35Sstevel 		bytes_to_read = (bytes_at_end >= f_frag_len)?
192203831d35Sstevel 			f_frag_len : bytes_at_end;
192303831d35Sstevel 		SGSBBC_DBG_MBOX("%s: reading data: sram_src = 0x%x, "
192403831d35Sstevel 			"bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
192503831d35Sstevel 		rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
192603831d35Sstevel 			bytes_to_read);
192703831d35Sstevel 		if (rc) {
192803831d35Sstevel 			goto done;
192903831d35Sstevel 		}
193003831d35Sstevel 
193103831d35Sstevel 		/*
193203831d35Sstevel 		 * Update pointers in SRAM and message buffer.
193303831d35Sstevel 		 */
193403831d35Sstevel 		sram_src = (uint32_t)mbox_offset;
193503831d35Sstevel 		msg_buf = (caddr_t)(msg_buf + bytes_to_read);
193603831d35Sstevel 		bytes_to_read = f_frag_len - bytes_to_read;
193703831d35Sstevel 	} else {
193803831d35Sstevel 		bytes_to_read = f_frag_len;
193903831d35Sstevel 	}
194003831d35Sstevel 
194103831d35Sstevel 	/*
194203831d35Sstevel 	 * wraparound to start of mailbox
194303831d35Sstevel 	 */
194403831d35Sstevel 	if (bytes_to_read > 0) {
194503831d35Sstevel 		SGSBBC_DBG_MBOX("%s: reading the rest: sram_src = 0x%x, "
194603831d35Sstevel 			"bytes_to_read = 0x%x\n", f, sram_src, bytes_to_read);
194703831d35Sstevel 		rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, msg_buf,
194803831d35Sstevel 			bytes_to_read);
194903831d35Sstevel 	}
195003831d35Sstevel 
195103831d35Sstevel done:
195203831d35Sstevel 	msg->msg_bytes += f_frag_len;
195303831d35Sstevel 
195403831d35Sstevel 	return (rc);
195503831d35Sstevel }
195603831d35Sstevel 
195703831d35Sstevel /*
195803831d35Sstevel  * move past the next message in the inbox
195903831d35Sstevel  */
196003831d35Sstevel static void
196103831d35Sstevel mbox_skip_next_msg(struct sbbc_mbox_header *header)
196203831d35Sstevel {
196303831d35Sstevel 	struct sbbc_fragment	frag;
196403831d35Sstevel 	uint32_t		next_msg;
196503831d35Sstevel 
196603831d35Sstevel 	ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
196703831d35Sstevel 
196803831d35Sstevel 	if (mbox_read_frag(header, &frag)) {
196903831d35Sstevel 		cmn_err(CE_PANIC, "INBOX is Corrupt !\n");
197003831d35Sstevel 	}
197103831d35Sstevel 
197203831d35Sstevel 	/*
197303831d35Sstevel 	 * Move on to the next message
197403831d35Sstevel 	 */
197503831d35Sstevel 	next_msg = header->mailboxes[SBBC_INBOX].mbox_consumer;
197603831d35Sstevel 	next_msg += sizeof (struct sbbc_fragment);
197703831d35Sstevel 	next_msg += frag.f_frag_len;
197803831d35Sstevel 	if (next_msg >= header->mailboxes[SBBC_INBOX].mbox_len) {
197903831d35Sstevel 		next_msg = (next_msg +
198003831d35Sstevel 			header->mailboxes[SBBC_INBOX].mbox_len) %
198103831d35Sstevel 			header->mailboxes[SBBC_INBOX].mbox_len;
198203831d35Sstevel 	}
198303831d35Sstevel 	header->mailboxes[SBBC_INBOX].mbox_consumer =
198403831d35Sstevel 		next_msg;
198503831d35Sstevel 
198603831d35Sstevel 	mbox_update_header(SBBC_INBOX, header);
198703831d35Sstevel 
198803831d35Sstevel 	return;
198903831d35Sstevel 
199003831d35Sstevel }
199103831d35Sstevel 
199203831d35Sstevel static struct sbbc_msg_waiter *
199303831d35Sstevel mbox_find_waiter(uint16_t msg_type, uint32_t msg_id)
199403831d35Sstevel {
199503831d35Sstevel 	struct	sbbc_msg_waiter	*waiter, *prev;
199603831d35Sstevel 
199703831d35Sstevel 	prev = NULL;
199803831d35Sstevel 	for (waiter = master_mbox->mbox_wait_list[msg_type];
199903831d35Sstevel 		waiter != NULL; waiter = waiter->w_next) {
200003831d35Sstevel 
200103831d35Sstevel 		if (waiter->w_id == msg_id) {
200203831d35Sstevel 			if (prev != NULL) {
200303831d35Sstevel 				prev->w_next = waiter->w_next;
200403831d35Sstevel 			} else {
200503831d35Sstevel 				master_mbox->mbox_wait_list[msg_type] =
200603831d35Sstevel 					waiter->w_next;
200703831d35Sstevel 			}
200803831d35Sstevel 			break;
200903831d35Sstevel 		}
201003831d35Sstevel 		prev = waiter;
201103831d35Sstevel 	}
201203831d35Sstevel 
201303831d35Sstevel 	return (waiter);
201403831d35Sstevel }
201503831d35Sstevel 
201603831d35Sstevel static int
201703831d35Sstevel mbox_read_header(uint32_t mailbox, struct sbbc_mbox_header *header)
201803831d35Sstevel {
201903831d35Sstevel 	struct sbbc_mbox_header *hd;
202003831d35Sstevel 	uint32_t	offset;
202103831d35Sstevel 	int		rc;
202203831d35Sstevel 
202303831d35Sstevel 	/*
202403831d35Sstevel 	 * Initialize a sbbc_mbox_header pointer to 0 so that we
202503831d35Sstevel 	 * can use it to calculate the offsets of fields inside
202603831d35Sstevel 	 * the structure.
202703831d35Sstevel 	 */
202803831d35Sstevel 	hd = (struct sbbc_mbox_header *)0;
202903831d35Sstevel 
203003831d35Sstevel 	if (rc = iosram_read(SBBC_MAILBOX_KEY, 0, (caddr_t)header,
203103831d35Sstevel 	    sizeof (struct sbbc_mbox_header)))
203203831d35Sstevel 		return (rc);
203303831d35Sstevel 
203403831d35Sstevel 	/*
203503831d35Sstevel 	 * Since the header is read in a byte-by-byte fashion
203603831d35Sstevel 	 * using ddi_rep_get8, we need to re-read the producer
203703831d35Sstevel 	 * or consumer pointer as integer in case it has changed
203803831d35Sstevel 	 * after part of the previous value has been read.
203903831d35Sstevel 	 */
204003831d35Sstevel 	switch (mailbox) {
204103831d35Sstevel 
204203831d35Sstevel 	case SBBC_INBOX:
204303831d35Sstevel 		offset = (uint32_t)(uintptr_t)
204403831d35Sstevel 		    (&hd->mailboxes[SBBC_INBOX].mbox_producer);
204503831d35Sstevel 		rc = iosram_read(SBBC_MAILBOX_KEY, offset,
204603831d35Sstevel 		    (caddr_t)&header->mailboxes[SBBC_INBOX].mbox_producer,
204703831d35Sstevel 		    sizeof (uint32_t));
204803831d35Sstevel 		break;
204903831d35Sstevel 	case SBBC_OUTBOX:
205003831d35Sstevel 		offset = (uint32_t)(uintptr_t)
205103831d35Sstevel 		    (&hd->mailboxes[SBBC_OUTBOX].mbox_consumer);
205203831d35Sstevel 		rc = iosram_read(SBBC_MAILBOX_KEY, offset,
205303831d35Sstevel 		    (caddr_t)&header->mailboxes[SBBC_OUTBOX].mbox_consumer,
205403831d35Sstevel 		    sizeof (uint32_t));
205503831d35Sstevel 		break;
205603831d35Sstevel 	default:
205703831d35Sstevel 		cmn_err(CE_PANIC, "Invalid Mbox header type\n");
205803831d35Sstevel 		break;
205903831d35Sstevel 
206003831d35Sstevel 	}
206103831d35Sstevel 
206203831d35Sstevel 	return (rc);
206303831d35Sstevel }
206403831d35Sstevel 
206503831d35Sstevel /*
206603831d35Sstevel  * There are only two fields updated by the  domain,
206703831d35Sstevel  * the inbox consumer field and the outbox producer
206803831d35Sstevel  * field. These fields are protected by the respective
206903831d35Sstevel  * mbox_{in|out}->mb_lock so that accesses will
207003831d35Sstevel  * be serialised. The only coherency issue is writing
207103831d35Sstevel  * back the header, so we do it here after grabbing
207203831d35Sstevel  * the global mailbox lock.
207303831d35Sstevel  */
207403831d35Sstevel static void
207503831d35Sstevel mbox_update_header(uint32_t mailbox, struct sbbc_mbox_header *header)
207603831d35Sstevel {
207703831d35Sstevel 	struct sbbc_mbox_header	*hd;
207803831d35Sstevel 	uint32_t		value, offset, mbox_len;
207903831d35Sstevel 
208003831d35Sstevel 	/*
208103831d35Sstevel 	 * Initialize a sbbc_mbox_header pointer to 0 so that we
208203831d35Sstevel 	 * can use it to calculate the offsets of fields inside
208303831d35Sstevel 	 * the structure.
208403831d35Sstevel 	 */
208503831d35Sstevel 	hd = (struct sbbc_mbox_header *)0;
208603831d35Sstevel 
208703831d35Sstevel 	switch (mailbox) {
208803831d35Sstevel 
208903831d35Sstevel 	case SBBC_INBOX:
209003831d35Sstevel 		value = header->mailboxes[SBBC_INBOX].mbox_consumer;
209103831d35Sstevel 		offset = (uint32_t)(uintptr_t)
209203831d35Sstevel 			(&hd->mailboxes[SBBC_INBOX].mbox_consumer);
209303831d35Sstevel 
209403831d35Sstevel 		mbox_len = header->mailboxes[SBBC_INBOX].mbox_len;
209503831d35Sstevel 		break;
209603831d35Sstevel 	case SBBC_OUTBOX:
209703831d35Sstevel 		value = header->mailboxes[SBBC_OUTBOX].mbox_producer;
209803831d35Sstevel 		offset = (uint32_t)(uintptr_t)
209903831d35Sstevel 			(&hd->mailboxes[SBBC_OUTBOX].mbox_producer);
210003831d35Sstevel 		mbox_len = header->mailboxes[SBBC_OUTBOX].mbox_len;
210103831d35Sstevel 		break;
210203831d35Sstevel 	default:
210303831d35Sstevel 		cmn_err(CE_PANIC, "Invalid Mbox header type\n");
210403831d35Sstevel 		break;
210503831d35Sstevel 
210603831d35Sstevel 	}
210703831d35Sstevel 
210803831d35Sstevel 	/*
210903831d35Sstevel 	 * If the last read/write would cause the next read/write
211003831d35Sstevel 	 * to be unaligned, we skip on modulo MBOX_ALIGN_BYTES.
211103831d35Sstevel 	 * This is OK because all the mailbox handlers will
211203831d35Sstevel 	 * conform to this.
211303831d35Sstevel 	 */
211403831d35Sstevel 	if (value % MBOX_ALIGN_BYTES) {
211503831d35Sstevel 		value += (MBOX_ALIGN_BYTES - (value % MBOX_ALIGN_BYTES));
211603831d35Sstevel 		value %= mbox_len;
211703831d35Sstevel 	}
211803831d35Sstevel 
211903831d35Sstevel 	if (iosram_write(SBBC_MAILBOX_KEY, offset, (caddr_t)&value,
212003831d35Sstevel 		sizeof (uint32_t))) {
212103831d35Sstevel 		cmn_err(CE_PANIC, "Mailbox Corrupt ! \n");
212203831d35Sstevel 	}
212303831d35Sstevel 
212403831d35Sstevel 	/*
212503831d35Sstevel 	 * Update internal pointers so they won't be out of sync with
212603831d35Sstevel 	 * the values in IOSRAM.
212703831d35Sstevel 	 */
212803831d35Sstevel 	switch (mailbox) {
212903831d35Sstevel 
213003831d35Sstevel 	case SBBC_INBOX:
213103831d35Sstevel 		header->mailboxes[SBBC_INBOX].mbox_consumer = value;
213203831d35Sstevel 		break;
213303831d35Sstevel 	case SBBC_OUTBOX:
213403831d35Sstevel 		header->mailboxes[SBBC_OUTBOX].mbox_producer = value;
213503831d35Sstevel 		break;
213603831d35Sstevel 	}
213703831d35Sstevel }
213803831d35Sstevel 
213903831d35Sstevel static int
214003831d35Sstevel mbox_read_frag(struct sbbc_mbox_header *header,
214103831d35Sstevel 	struct sbbc_fragment *frag)
214203831d35Sstevel {
214303831d35Sstevel 	int			rc = 0;
214403831d35Sstevel 	uint32_t		sram_src, bytes;
214503831d35Sstevel 	caddr_t			dst;
214603831d35Sstevel 
214703831d35Sstevel 	ASSERT(MUTEX_HELD(&master_mbox->mbox_in->mb_lock));
214803831d35Sstevel 	/*
214903831d35Sstevel 	 * read the fragment header for this message
215003831d35Sstevel 	 */
215103831d35Sstevel 	sram_src = (uint32_t)(header->mailboxes[SBBC_INBOX].mbox_offset +
215203831d35Sstevel 		header->mailboxes[SBBC_INBOX].mbox_consumer);
215303831d35Sstevel 
215403831d35Sstevel 	/*
215503831d35Sstevel 	 * wraparound ?
215603831d35Sstevel 	 */
215703831d35Sstevel 	if ((header->mailboxes[SBBC_INBOX].mbox_consumer +
215803831d35Sstevel 		sizeof (struct sbbc_fragment)) >=
215903831d35Sstevel 		header->mailboxes[SBBC_INBOX].mbox_len) {
216003831d35Sstevel 
216103831d35Sstevel 		dst = (caddr_t)frag;
216203831d35Sstevel 		bytes = header->mailboxes[SBBC_INBOX].mbox_len -
216303831d35Sstevel 			header->mailboxes[SBBC_INBOX].mbox_consumer;
216403831d35Sstevel 
216503831d35Sstevel 		if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, dst, bytes)) {
216603831d35Sstevel 			return (rc);
216703831d35Sstevel 		}
216803831d35Sstevel 
216903831d35Sstevel 		dst += bytes;
217003831d35Sstevel 		sram_src = header->mailboxes[SBBC_INBOX].mbox_offset;
217103831d35Sstevel 		bytes = (header->mailboxes[SBBC_INBOX].mbox_consumer +
217203831d35Sstevel 			sizeof (struct sbbc_fragment)) %
217303831d35Sstevel 			header->mailboxes[SBBC_INBOX].mbox_len;
217403831d35Sstevel 
217503831d35Sstevel 		if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src,
217603831d35Sstevel 			dst, bytes)) {
217703831d35Sstevel 			return (rc);
217803831d35Sstevel 		}
217903831d35Sstevel 	} else {
218003831d35Sstevel 		if (rc = iosram_read(SBBC_MAILBOX_KEY, sram_src, (caddr_t)frag,
218103831d35Sstevel 			sizeof (struct sbbc_fragment))) {
218203831d35Sstevel 			return (rc);
218303831d35Sstevel 		}
218403831d35Sstevel 	}
218503831d35Sstevel 
218603831d35Sstevel 	return (0);
218703831d35Sstevel }
218803831d35Sstevel 
218903831d35Sstevel 
219003831d35Sstevel /*
219103831d35Sstevel  * This function is triggered by a soft interrupt and it's purpose is to call
219203831d35Sstevel  * to kadmin() to shutdown the Domain.
219303831d35Sstevel  */
219403831d35Sstevel /*ARGSUSED0*/
219503831d35Sstevel static uint_t
219603831d35Sstevel sbbc_do_fast_shutdown(char *arg)
219703831d35Sstevel {
219803831d35Sstevel 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
219903831d35Sstevel 
220003831d35Sstevel 	/*
220103831d35Sstevel 	 * If kadmin fails for some reason then we bring the system down
220203831d35Sstevel 	 * via power_down(), or failing that using halt().
220303831d35Sstevel 	 */
220403831d35Sstevel 	power_down("kadmin() failed, trying power_down()");
220503831d35Sstevel 
220603831d35Sstevel 	halt("power_down() failed, trying halt()");
220703831d35Sstevel 
220803831d35Sstevel 	/*
220903831d35Sstevel 	 * We should never make it this far, so something must have gone
221003831d35Sstevel 	 * horribly, horribly wrong.
221103831d35Sstevel 	 */
221203831d35Sstevel 	/*NOTREACHED*/
2213055d7c80Scarlsonj 	return (DDI_INTR_UNCLAIMED);
221403831d35Sstevel }
221503831d35Sstevel 
221603831d35Sstevel 
221703831d35Sstevel /*
221803831d35Sstevel  * This function handles unsolicited PANIC_SHUTDOWN events
221903831d35Sstevel  */
222003831d35Sstevel static uint_t
222103831d35Sstevel sbbc_panic_shutdown_handler(char *arg)
222203831d35Sstevel {
222303831d35Sstevel 	static fn_t	f = "sbbc_panic_shutdown_handler()";
222403831d35Sstevel 
222503831d35Sstevel 	sg_panic_shutdown_t	*payload = NULL;
222603831d35Sstevel 	sbbc_msg_t		*msg = NULL;
222703831d35Sstevel 
222803831d35Sstevel 	if (arg == NULL) {
222903831d35Sstevel 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
223003831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
223103831d35Sstevel 	}
223203831d35Sstevel 
223303831d35Sstevel 	msg = (sbbc_msg_t *)arg;
223403831d35Sstevel 
223503831d35Sstevel 	if (msg->msg_buf == NULL) {
223603831d35Sstevel 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
223703831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
223803831d35Sstevel 	}
223903831d35Sstevel 
224003831d35Sstevel 	payload = (sg_panic_shutdown_t *)msg->msg_buf;
224103831d35Sstevel 
224203831d35Sstevel 	switch (*payload) {
224303831d35Sstevel 	case SC_EVENT_PANIC_ENV:
224403831d35Sstevel 
224503831d35Sstevel 		/*
224603831d35Sstevel 		 * Let the user know why the domain is going down.
224703831d35Sstevel 		 */
224803831d35Sstevel 		cmn_err(CE_WARN, "%s", PANIC_ENV_EVENT_MSG);
224903831d35Sstevel 
225003831d35Sstevel 		/*
225103831d35Sstevel 		 * trigger sbbc_do_fast_shutdown().
225203831d35Sstevel 		 */
225303831d35Sstevel 		ddi_trigger_softintr(panic_softintr_id);
225403831d35Sstevel 
225503831d35Sstevel 		/*NOTREACHED*/
225603831d35Sstevel 		break;
225703831d35Sstevel 
225803831d35Sstevel 	case SC_EVENT_PANIC_KEYSWITCH:
225903831d35Sstevel 		/*
226003831d35Sstevel 		 * The SC warns a user if they try a destructive keyswitch
226103831d35Sstevel 		 * command on a Domain which is currently running Solaris.
226203831d35Sstevel 		 * If the user chooses to continue despite our best advise
226303831d35Sstevel 		 * then we bring down the Domain immediately without trying
226403831d35Sstevel 		 * to shut the system down gracefully.
226503831d35Sstevel 		 */
226603831d35Sstevel 		break;
226703831d35Sstevel 
226803831d35Sstevel 	default:
226903831d35Sstevel 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: Unknown payload:%d", f,
227003831d35Sstevel 			*payload);
227103831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
227203831d35Sstevel 	}
227303831d35Sstevel 
227403831d35Sstevel 	return (DDI_INTR_CLAIMED);
227503831d35Sstevel }
227603831d35Sstevel 
227703831d35Sstevel /*
227803831d35Sstevel  * dp_get_cores()
227903831d35Sstevel  *
228003831d35Sstevel  * Checks cpu implementation for the input cpuid and returns
228103831d35Sstevel  * the number of cores.
228203831d35Sstevel  * If implementation cannot be determined, returns 1
228303831d35Sstevel  */
228403831d35Sstevel static int
228503831d35Sstevel dp_get_cores(uint16_t cpuid)
228603831d35Sstevel {
228703831d35Sstevel 	int	bd, ii, impl, nc;
228803831d35Sstevel 
228903831d35Sstevel 	bd = cpuid / 4;
229003831d35Sstevel 	nc = SG_MAX_CPUS_PER_BD;
229103831d35Sstevel 
229203831d35Sstevel 	/* find first with valid implementation */
229303831d35Sstevel 	for (ii = 0; ii < nc; ii++)
229403831d35Sstevel 		if (cpu[MAKE_CPUID(bd, ii)]) {
229503831d35Sstevel 			impl = cpunodes[MAKE_CPUID(bd, ii)].implementation;
229603831d35Sstevel 			break;
229703831d35Sstevel 		}
229803831d35Sstevel 
229903831d35Sstevel 	if (IS_JAGUAR(impl) || IS_PANTHER(impl))
230003831d35Sstevel 		return (2);
230103831d35Sstevel 	else
230203831d35Sstevel 		return (1);
230303831d35Sstevel }
230403831d35Sstevel 
230503831d35Sstevel /*
230603831d35Sstevel  * dp_payload_add_cpus()
230703831d35Sstevel  *
230803831d35Sstevel  * From datapath mailbox message, determines the number of and safari IDs
230903831d35Sstevel  * for affected cpus, then adds this info to the datapath ereport.
231003831d35Sstevel  *
231103831d35Sstevel  */
231203831d35Sstevel static int
231303831d35Sstevel dp_payload_add_cpus(plat_datapath_info_t *dpmsg, nvlist_t *erp)
231403831d35Sstevel {
231503831d35Sstevel 	int		jj = 0, numcpus = 0;
231603831d35Sstevel 	int		bd, procpos, ii, num, ncores, ret;
231703831d35Sstevel 	uint16_t	*dparray, cpuid;
231803831d35Sstevel 	uint64_t	*snarray;
231903831d35Sstevel 
232003831d35Sstevel 	/* check for multiple core architectures */
232103831d35Sstevel 	ncores = dp_get_cores(dpmsg->cpuid);
232203831d35Sstevel 
232303831d35Sstevel 	switch (dpmsg->type) {
232403831d35Sstevel 		case DP_CDS_TYPE:
232503831d35Sstevel 			numcpus = ncores;
232603831d35Sstevel 			break;
232703831d35Sstevel 
232803831d35Sstevel 		case DP_DX_TYPE:
232903831d35Sstevel 			numcpus = 2 * ncores;
233003831d35Sstevel 			break;
233103831d35Sstevel 
233203831d35Sstevel 		case DP_RP_TYPE:
233303831d35Sstevel 			numcpus = SG_MAX_CPUS_PER_BD;
233403831d35Sstevel 			break;
233503831d35Sstevel 
233603831d35Sstevel 		default:
233703831d35Sstevel 			ASSERT(0);
233803831d35Sstevel 			return (-1);
233903831d35Sstevel 	}
234003831d35Sstevel 
234103831d35Sstevel 	num = numcpus;
234203831d35Sstevel 
234303831d35Sstevel 	/*
234403831d35Sstevel 	 * populate dparray with impacted cores (only those present)
234503831d35Sstevel 	 */
234603831d35Sstevel 	dparray = kmem_zalloc(num * sizeof (uint16_t *), KM_SLEEP);
234703831d35Sstevel 	bd = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
234803831d35Sstevel 	procpos = SG_CPUID_TO_PORTID(dpmsg->cpuid) & 0x3;
234903831d35Sstevel 
235003831d35Sstevel 	mutex_enter(&cpu_lock);
235103831d35Sstevel 
235203831d35Sstevel 	switch (dpmsg->type) {
235303831d35Sstevel 
235403831d35Sstevel 		case DP_CDS_TYPE:
235503831d35Sstevel 			/*
235603831d35Sstevel 			 * For a CDS error, it's the reporting cpuid
235703831d35Sstevel 			 * and it's other core (if present)
235803831d35Sstevel 			 */
235903831d35Sstevel 			cpuid = dpmsg->cpuid & 0x1FF;	/* core 0 */
236003831d35Sstevel 			if (cpu[cpuid])
236103831d35Sstevel 				dparray[jj++] = cpuid;
236203831d35Sstevel 
236303831d35Sstevel 			cpuid = dpmsg->cpuid | SG_CORE_ID_MASK;	/* core 1 */
236403831d35Sstevel 			if (cpu[cpuid])
236503831d35Sstevel 				dparray[jj++] = cpuid;
236603831d35Sstevel 			break;
236703831d35Sstevel 
236803831d35Sstevel 		case DP_DX_TYPE:
236903831d35Sstevel 			/*
237003831d35Sstevel 			 * For a DX error, it's the reporting cpuid (all
237103831d35Sstevel 			 * cores) and the other CPU sharing the same
237203831d35Sstevel 			 * DX<-->DCDS interface (all cores)
237303831d35Sstevel 			 */
237403831d35Sstevel 
237503831d35Sstevel 			/* reporting cpuid */
237603831d35Sstevel 			cpuid = dpmsg->cpuid & 0x1FF;	/* core 0 */
237703831d35Sstevel 			if (cpu[cpuid])
237803831d35Sstevel 				dparray[jj++] = cpuid;
237903831d35Sstevel 
238003831d35Sstevel 			cpuid = dpmsg->cpuid | SG_CORE_ID_MASK;	/* core 1 */
238103831d35Sstevel 			if (cpu[cpuid])
238203831d35Sstevel 				dparray[jj++] = cpuid;
238303831d35Sstevel 
238403831d35Sstevel 			/* find partner cpuid */
238503831d35Sstevel 			if (procpos == 0 || procpos == 2)
238603831d35Sstevel 				cpuid = dpmsg->cpuid + 1;
238703831d35Sstevel 			else
238803831d35Sstevel 				cpuid = dpmsg->cpuid - 1;
238903831d35Sstevel 
239003831d35Sstevel 			/* add partner cpuid */
239103831d35Sstevel 			cpuid &= 0x1FF;			/* core 0 */
239203831d35Sstevel 			if (cpu[cpuid])
239303831d35Sstevel 				dparray[jj++] = cpuid;
239403831d35Sstevel 
239503831d35Sstevel 			cpuid |= SG_CORE_ID_MASK;	/* core 1 */
239603831d35Sstevel 			if (cpu[cpuid])
239703831d35Sstevel 				dparray[jj++] = cpuid;
239803831d35Sstevel 			break;
239903831d35Sstevel 
240003831d35Sstevel 		case DP_RP_TYPE:
240103831d35Sstevel 			/*
240203831d35Sstevel 			 * For a RP error, it's all cpuids (all cores) on
240303831d35Sstevel 			 * the reporting board
240403831d35Sstevel 			 */
240503831d35Sstevel 			for (ii = 0; ii < SG_MAX_CMPS_PER_BD; ii++) {
240603831d35Sstevel 				cpuid = MAKE_CPUID(bd, ii);
240703831d35Sstevel 				if (cpu[cpuid])		/* core 0 */
240803831d35Sstevel 					dparray[jj++] = cpuid;
240903831d35Sstevel 				cpuid |= SG_CORE_ID_MASK;
241003831d35Sstevel 				if (cpu[cpuid])		/* core 1 */
241103831d35Sstevel 					dparray[jj++] = cpuid;
241203831d35Sstevel 			}
241303831d35Sstevel 			break;
241403831d35Sstevel 	}
241503831d35Sstevel 
241603831d35Sstevel 	mutex_exit(&cpu_lock);
241703831d35Sstevel 
241803831d35Sstevel 	/*
241903831d35Sstevel 	 * The datapath message could not be associated with any
242003831d35Sstevel 	 * configured CPU.
242103831d35Sstevel 	 */
242203831d35Sstevel 	if (!jj) {
242303831d35Sstevel 		kmem_free(dparray, num * sizeof (uint16_t *));
242403831d35Sstevel 		ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
242503831d35Sstevel 		ASSERT(ret == 0);
242603831d35Sstevel 		return (-1);
242703831d35Sstevel 	}
242803831d35Sstevel 
242903831d35Sstevel 	snarray = kmem_zalloc(jj * sizeof (uint64_t), KM_SLEEP);
243003831d35Sstevel 	for (ii = 0; ii < jj; ii++)
243103831d35Sstevel 		snarray[ii] = cpunodes[dparray[ii]].device_id;
243203831d35Sstevel 
243303831d35Sstevel 	ret = nvlist_add_uint32(erp, DP_LIST_SIZE, jj);
243403831d35Sstevel 	ret |= nvlist_add_uint16_array(erp, DP_LIST, dparray, jj);
243503831d35Sstevel 	ret |= nvlist_add_uint64_array(erp, SN_LIST, snarray, jj);
243603831d35Sstevel 	ASSERT(ret == 0);
243703831d35Sstevel 
243803831d35Sstevel 	kmem_free(dparray, num * sizeof (uint16_t *));
243903831d35Sstevel 	kmem_free(snarray, jj * sizeof (uint64_t *));
244003831d35Sstevel 
244103831d35Sstevel 	return (0);
244203831d35Sstevel }
244303831d35Sstevel 
244403831d35Sstevel /*
244503831d35Sstevel  * sbbc_dp_trans_event() - datapath message handler.
244603831d35Sstevel  *
244703831d35Sstevel  * Process datapath error and fault messages received from the SC.  Checks
244803831d35Sstevel  * for, and disregards, messages associated with I/O boards.  Otherwise,
244903831d35Sstevel  * extracts message info to produce a datapath ereport.
245003831d35Sstevel  */
245103831d35Sstevel /*ARGSUSED*/
245203831d35Sstevel static uint_t
245303831d35Sstevel sbbc_dp_trans_event(char *arg)
245403831d35Sstevel {
245503831d35Sstevel 	const char	*f = "sbbc_dp_trans_event()";
245603831d35Sstevel 	nvlist_t	*erp, *detector, *hcelem;
245703831d35Sstevel 	char		buf[FM_MAX_CLASS];
245803831d35Sstevel 	int		board;
245903831d35Sstevel 	plat_datapath_info_t	*dpmsg;
246003831d35Sstevel 	sbbc_msg_t	*msg;
246103831d35Sstevel 	int		msgtype;
246203831d35Sstevel 
246303831d35Sstevel 	/* set i/f message and payload pointers */
246403831d35Sstevel 	msg = &dp_payload_msg;
246503831d35Sstevel 	dpmsg = &dp_payload;
246603831d35Sstevel 	msgtype = msg->msg_type.type;
246703831d35Sstevel 
246803831d35Sstevel 	cmn_err(CE_NOTE, "%s: msgtype=0x%x\n", f, msgtype);
246903831d35Sstevel 	cmn_err(CE_NOTE, "type=0x%x cpuid=0x%x t_value=0x%x\n", dpmsg->type,
247003831d35Sstevel 		dpmsg->cpuid, dpmsg->t_value);
247103831d35Sstevel 
247203831d35Sstevel 	/* check for valid type */
247303831d35Sstevel 	if (dpmsg->type > DP_RP_TYPE) {
247403831d35Sstevel 		cmn_err(CE_WARN, "%s: dpmsg type 0x%x invalid\n",
247503831d35Sstevel 			f, dpmsg->type);
247603831d35Sstevel 		return (DDI_INTR_CLAIMED);
247703831d35Sstevel 	}
247803831d35Sstevel 
247903831d35Sstevel 	/* check for I/O board message -  Schizo AIDs are 25 - 30 */
248003831d35Sstevel 	if (dpmsg->cpuid > 23) {
248103831d35Sstevel 		cmn_err(CE_NOTE, "%s: ignore I/O board msg\n", f);
248203831d35Sstevel 		return (DDI_INTR_CLAIMED);
248303831d35Sstevel 	}
248403831d35Sstevel 
248503831d35Sstevel 	/* allocate space for ereport */
248603831d35Sstevel 	erp = fm_nvlist_create(NULL);
248703831d35Sstevel 
248803831d35Sstevel /*
248903831d35Sstevel  * Member Name	Data Type	   Comments
249003831d35Sstevel  * -----------	---------	   -----------
249103831d35Sstevel  * version	uint8		   0
249203831d35Sstevel  * class	string		   "asic"
249303831d35Sstevel  * ENA		uint64		   ENA Format 1
249403831d35Sstevel  * detector	fmri		   aggregated ID data for SC-DE
249503831d35Sstevel  *
249603831d35Sstevel  * Datapath ereport subclasses and data payloads:
249703831d35Sstevel  * There will be two types of ereports (error and fault) which will be
249803831d35Sstevel  * identified by the "type" member.
249903831d35Sstevel  *
250003831d35Sstevel  * ereport.asic.serengeti.cds.cds-dp
250103831d35Sstevel  * ereport.asic.serengeti.dx.dx-dp	(board)
250203831d35Sstevel  * ereport.asic.serengeti.rp.rp-dp	(centerplane)
250303831d35Sstevel  *
250403831d35Sstevel  * Member Name	Data Type	  Comments
250503831d35Sstevel  * -----------	---------	  -----------
250603831d35Sstevel  * erptype	uint16		  derived from message type: error or
250703831d35Sstevel  *				  fault
250803831d35Sstevel  * t-value	uint32		  SC's datapath SERD timeout threshold
250903831d35Sstevel  * dp-list-sz	uint8		  number of dp-list array elements
251003831d35Sstevel  * dp-list	array of uint16	  Safari IDs of affected cpus
251103831d35Sstevel  * sn-list	array of uint64	  Serial numbers of affected cpus
251203831d35Sstevel  */
251303831d35Sstevel 
251403831d35Sstevel 	/* compose common ereport elements */
251503831d35Sstevel 	detector = fm_nvlist_create(NULL);
251603831d35Sstevel 
251703831d35Sstevel 	/*
251803831d35Sstevel 	 *  Create legacy FMRI for the detector
251903831d35Sstevel 	 */
252003831d35Sstevel 	board = SG_PORTID_TO_BOARD_NUM(SG_CPUID_TO_PORTID(dpmsg->cpuid));
252103831d35Sstevel 	switch (dpmsg->type) {
252203831d35Sstevel 		case DP_CDS_TYPE:
252303831d35Sstevel 		case DP_DX_TYPE:
252403831d35Sstevel 			(void) snprintf(buf, FM_MAX_CLASS, "SB%d", board);
252503831d35Sstevel 			break;
252603831d35Sstevel 		case DP_RP_TYPE:
252703831d35Sstevel 			(void) snprintf(buf, FM_MAX_CLASS, "RP");
252803831d35Sstevel 			break;
252903831d35Sstevel 		default:
253003831d35Sstevel 			(void) snprintf(buf, FM_MAX_CLASS, "UNKNOWN");
253103831d35Sstevel 			break;
253203831d35Sstevel 	}
253303831d35Sstevel 
253403831d35Sstevel 	hcelem = fm_nvlist_create(NULL);
253503831d35Sstevel 
253603831d35Sstevel 	(void) nvlist_add_string(hcelem, FM_FMRI_HC_NAME, FM_FMRI_LEGACY_HC);
253703831d35Sstevel 	(void) nvlist_add_string(hcelem, FM_FMRI_HC_ID, buf);
253803831d35Sstevel 
253903831d35Sstevel 	(void) nvlist_add_uint8(detector, FM_VERSION, FM_HC_SCHEME_VERSION);
254003831d35Sstevel 	(void) nvlist_add_string(detector, FM_FMRI_SCHEME, FM_FMRI_SCHEME_HC);
254103831d35Sstevel 	(void) nvlist_add_string(detector, FM_FMRI_HC_ROOT, "");
254203831d35Sstevel 	(void) nvlist_add_uint32(detector, FM_FMRI_HC_LIST_SZ, 1);
254303831d35Sstevel 	(void) nvlist_add_nvlist_array(detector, FM_FMRI_HC_LIST, &hcelem, 1);
254403831d35Sstevel 
254503831d35Sstevel 	/* build ereport class name */
254603831d35Sstevel 	(void) snprintf(buf, FM_MAX_CLASS, "asic.serengeti.%s.%s-%s",
254703831d35Sstevel 		dperrtype[dpmsg->type], dperrtype[dpmsg->type],
254803831d35Sstevel 		FM_ERROR_DATAPATH);
254903831d35Sstevel 
255003831d35Sstevel 	fm_ereport_set(erp, FM_EREPORT_VERSION, buf,
255103831d35Sstevel 		fm_ena_generate(0, FM_ENA_FMT1), detector, NULL);
255203831d35Sstevel 
255303831d35Sstevel 	/* add payload elements */
255403831d35Sstevel 	if (msgtype == MBOX_EVENT_DP_ERROR)
255503831d35Sstevel 		fm_payload_set(erp,
255603831d35Sstevel 			DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_ERROR, NULL);
255703831d35Sstevel 	else
255803831d35Sstevel 		fm_payload_set(erp,
255903831d35Sstevel 			DP_EREPORT_TYPE, DATA_TYPE_UINT16, DP_FAULT, NULL);
256003831d35Sstevel 
256103831d35Sstevel 	fm_payload_set(erp, DP_TVALUE, DATA_TYPE_UINT32, dpmsg->t_value, NULL);
256203831d35Sstevel 
2563*07d06da5SSurya Prakki 	(void) dp_payload_add_cpus(dpmsg, erp);
256403831d35Sstevel 
256503831d35Sstevel 	/* post ereport */
256603831d35Sstevel 	fm_ereport_post(erp, EVCH_SLEEP);
256703831d35Sstevel 
256803831d35Sstevel 	/* free ereport memory */
256903831d35Sstevel 	fm_nvlist_destroy(erp, FM_NVA_FREE);
257003831d35Sstevel 	fm_nvlist_destroy(detector, FM_NVA_FREE);
257103831d35Sstevel 
257203831d35Sstevel 	return (DDI_INTR_CLAIMED);
257303831d35Sstevel }
257403831d35Sstevel 
257503831d35Sstevel static uint_t
257603831d35Sstevel sbbc_datapath_error_msg_handler(char *arg)
257703831d35Sstevel {
257803831d35Sstevel 	static fn_t	f = "sbbc_datapath_error_msg_handler()";
257903831d35Sstevel 	sbbc_msg_t	*msg = NULL;
258003831d35Sstevel 
258103831d35Sstevel 	if (arg == NULL) {
258203831d35Sstevel 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
258303831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
258403831d35Sstevel 	}
258503831d35Sstevel 
258603831d35Sstevel 	msg = (sbbc_msg_t *)arg;
258703831d35Sstevel 
258803831d35Sstevel 	if (msg->msg_buf == NULL) {
258903831d35Sstevel 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
259003831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
259103831d35Sstevel 	}
259203831d35Sstevel 
259303831d35Sstevel 	msg->msg_type.type = MBOX_EVENT_DP_ERROR;
259403831d35Sstevel 
259503831d35Sstevel 	/* trigger sbbc_dp_trans_event() */
259603831d35Sstevel 	ddi_trigger_softintr(dp_softintr_id);
259703831d35Sstevel 
259803831d35Sstevel 	return (DDI_INTR_CLAIMED);
259903831d35Sstevel }
260003831d35Sstevel 
260103831d35Sstevel static uint_t
260203831d35Sstevel sbbc_datapath_fault_msg_handler(char *arg)
260303831d35Sstevel {
260403831d35Sstevel 
260503831d35Sstevel 	static fn_t	f = "sbbc_datapath_fault_msg_handler()";
260603831d35Sstevel 
260703831d35Sstevel 	sbbc_msg_t		*msg = NULL;
260803831d35Sstevel 
260903831d35Sstevel 	if (arg == NULL) {
261003831d35Sstevel 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: arg == NULL", f);
261103831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
261203831d35Sstevel 	}
261303831d35Sstevel 
261403831d35Sstevel 	msg = (sbbc_msg_t *)arg;
261503831d35Sstevel 
261603831d35Sstevel 	if (msg->msg_buf == NULL) {
261703831d35Sstevel 		SGSBBC_DBG_EVENT(CE_NOTE, "%s: msg_buf == NULL", f);
261803831d35Sstevel 		return (DDI_INTR_UNCLAIMED);
261903831d35Sstevel 	}
262003831d35Sstevel 
262103831d35Sstevel 	msg->msg_type.type = MBOX_EVENT_DP_FAULT;
262203831d35Sstevel 
262303831d35Sstevel 	/* trigger sbbc_dp_trans_event() */
262403831d35Sstevel 	ddi_trigger_softintr(dp_softintr_id);
262503831d35Sstevel 
262603831d35Sstevel 	return (DDI_INTR_CLAIMED);
262703831d35Sstevel }
262803831d35Sstevel 
262903831d35Sstevel /*
263003831d35Sstevel  * Log an ECC event message to the SC.  This is called from the
263103831d35Sstevel  * sbbc_ecc_mbox_taskq or directly from plat_send_ecc_mailbox_msg
263203831d35Sstevel  * for indictment messages.
263303831d35Sstevel  */
263403831d35Sstevel int
263503831d35Sstevel sbbc_mbox_ecc_output(sbbc_ecc_mbox_t *msgp)
263603831d35Sstevel {
263703831d35Sstevel 	int				rv;
263803831d35Sstevel 	plat_capability_data_t		*cap;
263903831d35Sstevel 	plat_dimm_sid_board_data_t	*ddata;
264003831d35Sstevel 	plat_ecc_msg_hdr_t		*hdr;
264103831d35Sstevel 
264203831d35Sstevel 	rv = sbbc_mbox_request_response(&msgp->ecc_req, &msgp->ecc_resp,
264303831d35Sstevel 		sbbc_mbox_default_timeout);
264403831d35Sstevel 
264503831d35Sstevel 	if (rv != 0) {
264603831d35Sstevel 		/*
264703831d35Sstevel 		 * Indictment messages use the return value to indicate a
264803831d35Sstevel 		 * problem in the mailbox.  For Error mailbox messages, we'll
264903831d35Sstevel 		 * have to use a syslog message.
265003831d35Sstevel 		 */
265103831d35Sstevel 		if (msgp->ecc_log_error) {
265203831d35Sstevel 			if (sbbc_ecc_mbox_send_errs == 0) {
265303831d35Sstevel 				cmn_err(CE_NOTE, "!Solaris failed to send a "
265403831d35Sstevel 				    "message (0x%x/0x%x) to the System "
265503831d35Sstevel 				    "Controller. Error: %d, Message Status: %d",
265603831d35Sstevel 				    msgp->ecc_resp.msg_type.type,
265703831d35Sstevel 				    msgp->ecc_resp.msg_type.sub_type,
265803831d35Sstevel 				    rv, msgp->ecc_resp.msg_status);
265903831d35Sstevel 			}
266003831d35Sstevel 
266103831d35Sstevel 			if (++sbbc_ecc_mbox_send_errs >=
266203831d35Sstevel 			    sbbc_ecc_mbox_err_throttle) {
266303831d35Sstevel 				sbbc_ecc_mbox_send_errs = 0;
266403831d35Sstevel 			}
266503831d35Sstevel 		}
266603831d35Sstevel 
266703831d35Sstevel 	} else if (msgp->ecc_resp.msg_status != 0) {
266803831d35Sstevel 		if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
266903831d35Sstevel 			switch (msgp->ecc_resp.msg_type.sub_type) {
267003831d35Sstevel 			case INFO_MBOX_ECC:
267103831d35Sstevel 				hdr = (plat_ecc_msg_hdr_t *)
267203831d35Sstevel 				    msgp->ecc_req.msg_buf;
267303831d35Sstevel 				if (hdr->emh_msg_type ==
267403831d35Sstevel 				    PLAT_ECC_DIMM_SID_MESSAGE) {
267503831d35Sstevel 					rv = msgp->ecc_resp.msg_status;
267603831d35Sstevel 					break;
267703831d35Sstevel 				}
267803831d35Sstevel 			/*FALLTHROUGH*/
267903831d35Sstevel 			case INFO_MBOX_ECC_CAP:
268003831d35Sstevel 				/*
268103831d35Sstevel 				 * The positive response comes only
268203831d35Sstevel 				 * from the AVL FS1 updated SC.
268303831d35Sstevel 				 * If the firmware is either downgraded
268403831d35Sstevel 				 * or failover to an older version, then
268503831d35Sstevel 				 * lets reset the SC capability to
268603831d35Sstevel 				 * default.
268703831d35Sstevel 				 */
268803831d35Sstevel 				plat_ecc_capability_sc_set
268903831d35Sstevel 				    (PLAT_ECC_CAPABILITY_SC_DEFAULT);
269003831d35Sstevel 				break;
269103831d35Sstevel 			default:
269203831d35Sstevel 				break;
269303831d35Sstevel 			}
269403831d35Sstevel 		}
269503831d35Sstevel 		if (msgp->ecc_log_error) {
269603831d35Sstevel 			if (sbbc_ecc_mbox_inval_errs == 0) {
269703831d35Sstevel 				cmn_err(CE_NOTE, "!An internal error (%d) "
269803831d35Sstevel 				    "occurred in the System Controller while "
269903831d35Sstevel 				    "processing this message (0x%x/0x%x)",
270003831d35Sstevel 				    msgp->ecc_resp.msg_status,
270103831d35Sstevel 				    msgp->ecc_resp.msg_type.type,
270203831d35Sstevel 				    msgp->ecc_resp.msg_type.sub_type);
270303831d35Sstevel 			}
270403831d35Sstevel 			if (msgp->ecc_resp.msg_status == EINVAL) {
270503831d35Sstevel 				if (++sbbc_ecc_mbox_inval_errs >=
270603831d35Sstevel 				    sbbc_ecc_mbox_err_throttle) {
270703831d35Sstevel 					sbbc_ecc_mbox_inval_errs = 0;
270803831d35Sstevel 				}
270903831d35Sstevel 				rv = ENOMSG;
271003831d35Sstevel 			} else {
271103831d35Sstevel 				if (++sbbc_ecc_mbox_other_errs >=
271203831d35Sstevel 				    sbbc_ecc_mbox_err_throttle) {
271303831d35Sstevel 					sbbc_ecc_mbox_other_errs = 0;
271403831d35Sstevel 				}
271503831d35Sstevel 				rv = msgp->ecc_resp.msg_status;
271603831d35Sstevel 			}
271703831d35Sstevel 		}
271803831d35Sstevel 
271903831d35Sstevel 	} else {
272003831d35Sstevel 		if (msgp->ecc_resp.msg_type.type == INFO_MBOX) {
272103831d35Sstevel 			switch (msgp->ecc_resp.msg_type.sub_type) {
272203831d35Sstevel 			case INFO_MBOX_ECC_CAP:
272303831d35Sstevel 				/*
272403831d35Sstevel 				 * Successfully received the response
272503831d35Sstevel 				 * for the capability message, so updating
272603831d35Sstevel 				 * the SC ECC messaging capability.
272703831d35Sstevel 				 */
272803831d35Sstevel 				cap = (plat_capability_data_t *)
272903831d35Sstevel 				    msgp->ecc_resp.msg_buf;
273003831d35Sstevel 				plat_ecc_capability_sc_set
273103831d35Sstevel 				    (cap->capd_capability);
273203831d35Sstevel 				break;
273303831d35Sstevel 
273403831d35Sstevel 			case INFO_MBOX_ECC:
273503831d35Sstevel 				hdr = (plat_ecc_msg_hdr_t *)
273603831d35Sstevel 				    msgp->ecc_resp.msg_buf;
273703831d35Sstevel 				if (hdr && (hdr->emh_msg_type ==
273803831d35Sstevel 				    PLAT_ECC_DIMM_SID_MESSAGE)) {
273903831d35Sstevel 					/*
274003831d35Sstevel 					 * Successfully received a response
274103831d35Sstevel 					 * to a request for DIMM serial ids.
274203831d35Sstevel 					 */
274303831d35Sstevel 					ddata = (plat_dimm_sid_board_data_t *)
274403831d35Sstevel 					    msgp->ecc_resp.msg_buf;
274503831d35Sstevel 					(void) plat_store_mem_sids(ddata);
274603831d35Sstevel 				}
274703831d35Sstevel 				break;
274803831d35Sstevel 
274903831d35Sstevel 			default:
275003831d35Sstevel 				break;
275103831d35Sstevel 			}
275203831d35Sstevel 		}
275303831d35Sstevel 	}
275403831d35Sstevel 
275503831d35Sstevel 	if (msgp->ecc_resp.msg_buf)
275603831d35Sstevel 		kmem_free((void *)msgp->ecc_resp.msg_buf,
275703831d35Sstevel 		    (size_t)msgp->ecc_resp.msg_len);
275803831d35Sstevel 
275903831d35Sstevel 	kmem_free((void *)msgp->ecc_req.msg_buf, (size_t)msgp->ecc_req.msg_len);
276003831d35Sstevel 	kmem_free(msgp, sizeof (sbbc_ecc_mbox_t));
276103831d35Sstevel 	return (rv);
276203831d35Sstevel }
276303831d35Sstevel 
276403831d35Sstevel /*
276503831d35Sstevel  * Enqueue ECC event message on taskq to SC.  This is invoked from
276603831d35Sstevel  * plat_send_ecc_mailbox_msg() for each ECC event generating a message.
276703831d35Sstevel  */
276803831d35Sstevel void
276903831d35Sstevel sbbc_mbox_queue_ecc_event(sbbc_ecc_mbox_t *sbbc_ecc_msgp)
277003831d35Sstevel {
277103831d35Sstevel 	/*
277203831d35Sstevel 	 * Create the ECC event mailbox taskq, if it does not yet exist.
277303831d35Sstevel 	 * This must be done here rather than in sbbc_mbox_init().  The
277403831d35Sstevel 	 * sgsbbc driver is loaded very early in the boot flow.  Calling
277503831d35Sstevel 	 * taskq_create() from sbbc_mbox_init could lead to a boot deadlock.
277603831d35Sstevel 	 *
277703831d35Sstevel 	 * There might be a tiny probability that two ECC handlers on
277803831d35Sstevel 	 * different processors could arrive here simultaneously.  If
277903831d35Sstevel 	 * the taskq has not been created previously, then these two
278003831d35Sstevel 	 * simultaneous events could cause the creation of an extra taskq.
278103831d35Sstevel 	 * Given the extremely small likelihood (if not outright impossibility)
278203831d35Sstevel 	 * of this occurrence, sbbc_ecc_mbox_taskq is not protected by a lock.
278303831d35Sstevel 	 */
278403831d35Sstevel 
278503831d35Sstevel 	if (sbbc_ecc_mbox_taskq == NULL) {
278603831d35Sstevel 		sbbc_ecc_mbox_taskq = taskq_create("ECC_event_mailbox", 1,
278703831d35Sstevel 		    minclsyspri, ECC_MBOX_TASKQ_MIN, ECC_MBOX_TASKQ_MAX,
278803831d35Sstevel 		    TASKQ_PREPOPULATE);
278903831d35Sstevel 		if (sbbc_ecc_mbox_taskq == NULL) {
279003831d35Sstevel 			if (sbbc_ecc_mbox_taskq_errs == 0) {
279103831d35Sstevel 				cmn_err(CE_NOTE, "Unable to create mailbox "
279203831d35Sstevel 				    "task queue for ECC event logging to "
279303831d35Sstevel 				    "System Controller");
279403831d35Sstevel 			}
279503831d35Sstevel 			if (++sbbc_ecc_mbox_taskq_errs >=
279603831d35Sstevel 			    sbbc_ecc_mbox_err_throttle) {
279703831d35Sstevel 				sbbc_ecc_mbox_taskq_errs = 0;
279803831d35Sstevel 			}
279903831d35Sstevel 
280003831d35Sstevel 			kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
280103831d35Sstevel 				(size_t)sbbc_ecc_msgp->ecc_req.msg_len);
280203831d35Sstevel 			kmem_free((void *)sbbc_ecc_msgp,
280303831d35Sstevel 				sizeof (sbbc_ecc_mbox_t));
280403831d35Sstevel 			return;
280503831d35Sstevel 		}
280603831d35Sstevel 
280703831d35Sstevel 		/*
280803831d35Sstevel 		 * Reset error counter so that first taskq_dispatch
280903831d35Sstevel 		 * error will be output
281003831d35Sstevel 		 */
281103831d35Sstevel 		sbbc_ecc_mbox_taskq_errs = 0;
281203831d35Sstevel 	}
281303831d35Sstevel 
281403831d35Sstevel 	/*
281503831d35Sstevel 	 * Enqueue the message
281603831d35Sstevel 	 */
281703831d35Sstevel 
281803831d35Sstevel 	if (taskq_dispatch(sbbc_ecc_mbox_taskq,
281903831d35Sstevel 	    (task_func_t *)sbbc_mbox_ecc_output, sbbc_ecc_msgp,
282003831d35Sstevel 	    TQ_NOSLEEP) == NULL) {
282103831d35Sstevel 
282203831d35Sstevel 		if (sbbc_ecc_mbox_taskq_errs == 0) {
282303831d35Sstevel 			cmn_err(CE_NOTE, "Unable to send ECC event "
282403831d35Sstevel 				"message to System Controller");
282503831d35Sstevel 		}
282603831d35Sstevel 		if (++sbbc_ecc_mbox_taskq_errs >= sbbc_ecc_mbox_err_throttle) {
282703831d35Sstevel 			sbbc_ecc_mbox_taskq_errs = 0;
282803831d35Sstevel 		}
282903831d35Sstevel 
283003831d35Sstevel 		kmem_free((void *)sbbc_ecc_msgp->ecc_req.msg_buf,
283103831d35Sstevel 				(size_t)sbbc_ecc_msgp->ecc_req.msg_len);
283203831d35Sstevel 		kmem_free((void *)sbbc_ecc_msgp, sizeof (sbbc_ecc_mbox_t));
283303831d35Sstevel 	}
283403831d35Sstevel }
283503831d35Sstevel 
283603831d35Sstevel static uint_t
283703831d35Sstevel cap_ecc_msg_handler(char *addr)
283803831d35Sstevel {
283903831d35Sstevel 	sbbc_msg_t *msg = NULL;
284003831d35Sstevel 	plat_capability_data_t *cap = NULL;
284103831d35Sstevel 	static fn_t f = "cap_ecc_msg_handler";
284203831d35Sstevel 
284303831d35Sstevel 	msg = (sbbc_msg_t *)addr;
284403831d35Sstevel 
284503831d35Sstevel 	if (msg == NULL) {
284603831d35Sstevel 		SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
284703831d35Sstevel 		    "null addr");
284803831d35Sstevel 		return (DDI_INTR_CLAIMED);
284903831d35Sstevel 	}
285003831d35Sstevel 
285103831d35Sstevel 	if (msg->msg_buf == NULL) {
285203831d35Sstevel 		SGSBBC_DBG_EVENT(CE_WARN, "cap_ecc_msg_handler() called with "
285303831d35Sstevel 		    "null data buffer");
285403831d35Sstevel 		return (DDI_INTR_CLAIMED);
285503831d35Sstevel 	}
285603831d35Sstevel 
285703831d35Sstevel 	cap = (plat_capability_data_t *)msg->msg_buf;
285803831d35Sstevel 	switch (cap->capd_msg_type) {
285903831d35Sstevel 	case PLAT_ECC_CAPABILITY_MESSAGE:
286003831d35Sstevel 		SGSBBC_DBG_MBOX("%s: capability  0x%x\n", f,
286103831d35Sstevel 		    cap->capd_capability);
286203831d35Sstevel 		plat_ecc_capability_sc_set(cap->capd_capability);
286303831d35Sstevel 		break;
286403831d35Sstevel 	default:
286503831d35Sstevel 		SGSBBC_DBG_MBOX("%s: Unknown message type = 0x%x\n", f,
286603831d35Sstevel 		    cap->capd_msg_type);
286703831d35Sstevel 		break;
286803831d35Sstevel 	}
286903831d35Sstevel 
287003831d35Sstevel 	return (DDI_INTR_CLAIMED);
287103831d35Sstevel }
2872