xref: /titanic_44/usr/src/uts/sun4u/io/rmc_comm_drvintf.c (revision d3d50737e566cade9a08d73d2af95105ac7cd960)
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*d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel /*
2803831d35Sstevel  * provide the interface to the layered drivers (send request/receive
2903831d35Sstevel  * response to the RMC
3003831d35Sstevel  *
3103831d35Sstevel  */
3203831d35Sstevel 
3303831d35Sstevel /*
3403831d35Sstevel  *  Header files
3503831d35Sstevel  */
3603831d35Sstevel #include <sys/conf.h>
3703831d35Sstevel #include <sys/callb.h>
3803831d35Sstevel #include <sys/cyclic.h>
3903831d35Sstevel #include <sys/membar.h>
4003831d35Sstevel #include <sys/modctl.h>
4103831d35Sstevel #include <sys/strlog.h>
4203831d35Sstevel #include <sys/sunddi.h>
4303831d35Sstevel #include <sys/ddi.h>
4403831d35Sstevel #include <sys/types.h>
4503831d35Sstevel #include <sys/disp.h>
4603831d35Sstevel #include <sys/rmc_comm_dp.h>
4703831d35Sstevel #include <sys/rmc_comm_dp_boot.h>
4803831d35Sstevel #include <sys/rmc_comm_drvintf.h>
4903831d35Sstevel #include <sys/rmc_comm.h>
5003831d35Sstevel 
5103831d35Sstevel void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);
5203831d35Sstevel void dp_wake_up_waiter(struct rmc_comm_state *, uint8_t);
5303831d35Sstevel 
5403831d35Sstevel static int rmc_comm_send_req_resp(struct rmc_comm_state *rcs,
5503831d35Sstevel     rmc_comm_msg_t *request, rmc_comm_msg_t *response, uint32_t wait_time);
5603831d35Sstevel static int rmc_comm_wait_bp_reply(struct rmc_comm_state *,
5703831d35Sstevel     rmc_comm_dp_state_t *, dp_req_resp_t *, clock_t);
5803831d35Sstevel static void rmc_comm_wait_enable_to_send(struct rmc_comm_state *,
5903831d35Sstevel     rmc_comm_dp_state_t *);
6003831d35Sstevel static void rmc_comm_wake_up_next(struct rmc_comm_state *);
6103831d35Sstevel static void rmc_comm_send_pend_req(caddr_t arg);
6203831d35Sstevel static int rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs);
6303831d35Sstevel static void rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs);
6403831d35Sstevel 
6503831d35Sstevel /*
6603831d35Sstevel  * leaf driver to use this function to send a request to the remote side (RMC)
6703831d35Sstevel  * and wait for a reply
6803831d35Sstevel  */
6903831d35Sstevel int
rmc_comm_request_response(rmc_comm_msg_t * request,rmc_comm_msg_t * response,uint32_t wait_time)7003831d35Sstevel rmc_comm_request_response(rmc_comm_msg_t *request,
7103831d35Sstevel     rmc_comm_msg_t *response, uint32_t wait_time)
7203831d35Sstevel {
7303831d35Sstevel 	struct rmc_comm_state	*rcs;
7452f4394bSjfrank 	int err;
7503831d35Sstevel 
7603831d35Sstevel 	/*
7703831d35Sstevel 	 * get the soft state struct (instance 0)
7803831d35Sstevel 	 */
7903831d35Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
8003831d35Sstevel 	    "rmc_comm_request_response")) == NULL)
8103831d35Sstevel 		return (RCENOSOFTSTATE);
8203831d35Sstevel 
8352f4394bSjfrank 	do {
8452f4394bSjfrank 		err = rmc_comm_send_req_resp(rcs, request, response, wait_time);
8552f4394bSjfrank 	} while (err == RCEGENERIC);
8652f4394bSjfrank 	return (err);
8703831d35Sstevel }
8803831d35Sstevel 
8903831d35Sstevel /*
9003831d35Sstevel  * leaf driver to use this function to send a request to the remote side (RMC)
9103831d35Sstevel  * without waiting for a reply. If flag is RMC_COMM_DREQ_URGENT, the request
9203831d35Sstevel  * message is sent once-off (an eventual pending request is aborted). This
9303831d35Sstevel  * flag must only be used when try to send a request in critical condition
9403831d35Sstevel  * (while the system is shutting down for instance and the CPU signature
9503831d35Sstevel  * has to be sent). Otherwise, the request is stored in a temporary location
9603831d35Sstevel  * and delivered by a thread.
9703831d35Sstevel  */
9803831d35Sstevel int
rmc_comm_request_nowait(rmc_comm_msg_t * request,uint8_t flag)9903831d35Sstevel rmc_comm_request_nowait(rmc_comm_msg_t *request, uint8_t flag)
10003831d35Sstevel {
10103831d35Sstevel 	struct rmc_comm_state		*rcs;
10203831d35Sstevel 	rmc_comm_dp_state_t		*dps;
10303831d35Sstevel 	rmc_comm_drvintf_state_t	*dis;
10403831d35Sstevel 	dp_message_t			req;
10503831d35Sstevel 	int				err = RCNOERR;
10603831d35Sstevel 	uint8_t				flags = 0;
10703831d35Sstevel 
10803831d35Sstevel 	/*
10903831d35Sstevel 	 * get the soft state struct (instance 0)
11003831d35Sstevel 	 */
11103831d35Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
11203831d35Sstevel 	    "rmc_comm_request_response")) == NULL)
11303831d35Sstevel 		return (RCENOSOFTSTATE);
11403831d35Sstevel 
11503831d35Sstevel 	/*
11603831d35Sstevel 	 * just a sanity check...
11703831d35Sstevel 	 */
11803831d35Sstevel 	if (request == NULL) {
11903831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "reqnowait, invalid args\n"));
12003831d35Sstevel 		return (RCEINVARG);
12103831d35Sstevel 	}
12203831d35Sstevel 
12303831d35Sstevel 	if (!IS_NUMBERED_MSG(request->msg_type)) {
12403831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
12503831d35Sstevel 		    "reqnowait, ctrl msg not allowed! req type=%x\n",
12603831d35Sstevel 		    request->msg_type));
12703831d35Sstevel 		return (RCEINVARG);
12803831d35Sstevel 	}
12903831d35Sstevel 
13003831d35Sstevel 	if (flag == RMC_COMM_DREQ_URGENT) {
13103831d35Sstevel 		/*
13203831d35Sstevel 		 * Send this request with high priority i.e. abort eventual
13303831d35Sstevel 		 * request/response pending sessions.
13403831d35Sstevel 		 */
13503831d35Sstevel 
13603831d35Sstevel 		dps = &rcs->dp_state;
13703831d35Sstevel 
13803831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "going to send request=%x (URG)\n",
13903831d35Sstevel 		    request->msg_type));
14003831d35Sstevel 
1416fa6856eSarutz 		/*
1426fa6856eSarutz 		 * Handle the case where we are called during panic
1436fa6856eSarutz 		 * processing.  If that occurs, then another thread in
1446fa6856eSarutz 		 * rmc_comm might have been idled by panic() while
1456fa6856eSarutz 		 * holding dp_mutex.  As a result, do not unconditionally
1466fa6856eSarutz 		 * grab dp_mutex.
1476fa6856eSarutz 		 */
1486fa6856eSarutz 		if (ddi_in_panic() != 0) {
1496fa6856eSarutz 			if (mutex_tryenter(dps->dp_mutex) == 0) {
1506fa6856eSarutz 				return (RCENODATALINK);
1516fa6856eSarutz 			}
1526fa6856eSarutz 		} else {
15303831d35Sstevel 			mutex_enter(dps->dp_mutex);
1546fa6856eSarutz 		}
15503831d35Sstevel 
15603831d35Sstevel 		/*
15703831d35Sstevel 		 * send the request only if the protocol data link is up.
15803831d35Sstevel 		 * it is pointless to send it in the other case.
15903831d35Sstevel 		 */
16003831d35Sstevel 		if (dps->data_link_ok) {
16103831d35Sstevel 
16203831d35Sstevel 			/*
16303831d35Sstevel 			 * clean up an eventual pending request/response session
16403831d35Sstevel 			 * (save its current status)
16503831d35Sstevel 			 */
16603831d35Sstevel 			if (dps->pending_request) {
16703831d35Sstevel 				flags = dps->req_resp.flags;
16803831d35Sstevel 				rmc_comm_dp_mcleanup(rcs);
16903831d35Sstevel 			}
17003831d35Sstevel 
17103831d35Sstevel 			/*
17203831d35Sstevel 			 * send the request message
17303831d35Sstevel 			 */
17403831d35Sstevel 			req.msg_type = request->msg_type;
17503831d35Sstevel 			req.msg_buf = (uint8_t *)request->msg_buf;
17603831d35Sstevel 			req.msg_msglen = (uint16_t)request->msg_len;
17703831d35Sstevel 
17803831d35Sstevel 			DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x (URG)\n",
17903831d35Sstevel 			    request->msg_type));
18003831d35Sstevel 
18103831d35Sstevel 			err = rmc_comm_dp_msend(rcs, &req);
18203831d35Sstevel 
18303831d35Sstevel 			/*
18403831d35Sstevel 			 * wait for fifos to drain
18503831d35Sstevel 			 */
18603831d35Sstevel 			rmc_comm_serdev_drain(rcs);
18703831d35Sstevel 
18803831d35Sstevel 			/*
18903831d35Sstevel 			 * clean up the current session
19003831d35Sstevel 			 */
19103831d35Sstevel 			rmc_comm_dp_mcleanup(rcs);
19203831d35Sstevel 
19303831d35Sstevel 			/*
19403831d35Sstevel 			 * abort an old session (if any)
19503831d35Sstevel 			 */
19603831d35Sstevel 			if (dps->pending_request) {
19703831d35Sstevel 				dps->req_resp.flags = flags;
19803831d35Sstevel 				dp_wake_up_waiter(rcs, MSG_ERROR);
19903831d35Sstevel 			}
20003831d35Sstevel 		}
20103831d35Sstevel 
20203831d35Sstevel 		mutex_exit(dps->dp_mutex);
20303831d35Sstevel 
20403831d35Sstevel 	} else {
20503831d35Sstevel 
20603831d35Sstevel 		/*
20703831d35Sstevel 		 * Get an 'independent' thread (rmc_comm_send_pend_req)
20803831d35Sstevel 		 * to send this request (since the calling thread does not
20903831d35Sstevel 		 * want to wait). Copy the request in the drvintf state
21003831d35Sstevel 		 * structure and signal the thread.
21103831d35Sstevel 		 */
21203831d35Sstevel 
21303831d35Sstevel 		dis = &rcs->drvi_state;
21403831d35Sstevel 
21503831d35Sstevel 		mutex_enter(dis->dreq_mutex);
21603831d35Sstevel 
21703831d35Sstevel 		if (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
21803831d35Sstevel 
21903831d35Sstevel 			DPRINTF(rcs, DAPI, (CE_CONT, "get to send request=%x\n",
22003831d35Sstevel 			    request->msg_type));
22103831d35Sstevel 
22203831d35Sstevel 			/*
22303831d35Sstevel 			 * copy the request in a temporary location
22403831d35Sstevel 			 * (drvinf_state structure) and signal the thread
22503831d35Sstevel 			 * that a request message has to be delivered
22603831d35Sstevel 			 */
22703831d35Sstevel 
22803831d35Sstevel 			if (request->msg_len < DP_MAX_MSGLEN) {
22903831d35Sstevel 				dis->dreq_request.msg_type = request->msg_type;
23003831d35Sstevel 				dis->dreq_request.msg_len = request->msg_len;
23103831d35Sstevel 				dis->dreq_request.msg_buf =
23203831d35Sstevel 				    dis->dreq_request_buf;
23303831d35Sstevel 				bcopy(request->msg_buf,
23403831d35Sstevel 				    dis->dreq_request.msg_buf,
23503831d35Sstevel 				    request->msg_len);
23603831d35Sstevel 
23703831d35Sstevel 				dis->dreq_state = RMC_COMM_DREQ_ST_PROCESS;
23803831d35Sstevel 				cv_signal(dis->dreq_sig_cv);
23903831d35Sstevel 
24003831d35Sstevel 			} else {
24103831d35Sstevel 				/*
24203831d35Sstevel 				 * not enough space to hold the request
24303831d35Sstevel 				 */
24403831d35Sstevel 				err = RCEREPTOOBIG;
24503831d35Sstevel 			}
24603831d35Sstevel 		} else {
24703831d35Sstevel 
24803831d35Sstevel 			DPRINTF(rcs, DAPI, (CE_CONT, "cannot get to send "
24903831d35Sstevel 			    "request=%x (busy)\n", request->msg_type));
25003831d35Sstevel 
25103831d35Sstevel 			/*
25203831d35Sstevel 			 * only one request per time can be processed.
25303831d35Sstevel 			 * the thread is either busy (RMC_COMM_DREQ_ST_PROCESS)
25403831d35Sstevel 			 * or terminating (RMC_COMM_DREQ_ST_EXIT)
25503831d35Sstevel 			 */
25603831d35Sstevel 			err = RCEGENERIC;
25703831d35Sstevel 		}
25803831d35Sstevel 
25903831d35Sstevel 		mutex_exit(dis->dreq_mutex);
26003831d35Sstevel 	}
26103831d35Sstevel 
26203831d35Sstevel 	return (err);
26303831d35Sstevel }
26403831d35Sstevel 
26503831d35Sstevel /*
26603831d35Sstevel  * Function used to send a request and (eventually) wait for a response.
26703831d35Sstevel  * It can be called from a leaf driver (via rmc_comm_request_response) or
26803831d35Sstevel  * from the thread in charge of sending 'no-wait' requests
26903831d35Sstevel  * (rmc_comm_send_pend_req).
27003831d35Sstevel  */
27103831d35Sstevel static int
rmc_comm_send_req_resp(struct rmc_comm_state * rcs,rmc_comm_msg_t * request,rmc_comm_msg_t * response,uint32_t wait_time)27203831d35Sstevel rmc_comm_send_req_resp(struct rmc_comm_state *rcs, rmc_comm_msg_t *request,
27303831d35Sstevel     rmc_comm_msg_t *response, uint32_t wait_time)
27403831d35Sstevel {
27503831d35Sstevel 	rmc_comm_dp_state_t	*dps;
27603831d35Sstevel 	dp_req_resp_t		*drr;
27703831d35Sstevel 	dp_message_t		*exp_resp;
27803831d35Sstevel 	dp_message_t		req;
279*d3d50737SRafael Vanoni 	clock_t			resend_clockt, delta;
28003831d35Sstevel 	clock_t			stop_clockt;
28103831d35Sstevel 	int			err;
28203831d35Sstevel 
28303831d35Sstevel 
28403831d35Sstevel 	/*
28503831d35Sstevel 	 * just a sanity check...
28603831d35Sstevel 	 */
28703831d35Sstevel 	if (request == NULL) {
28803831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp, invalid args\n"));
28903831d35Sstevel 		return (RCEINVARG);
29003831d35Sstevel 	}
29103831d35Sstevel 
29203831d35Sstevel 	/*
29303831d35Sstevel 	 * drivers cannot send control messages at all. They are meant to
29403831d35Sstevel 	 * be used at low level only.
29503831d35Sstevel 	 */
29603831d35Sstevel 	if (!IS_NUMBERED_MSG(request->msg_type)) {
29703831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
29803831d35Sstevel 		    "reqresp, ctrl msg not allowed! req type=%x\n",
29903831d35Sstevel 		    request->msg_type));
30003831d35Sstevel 		return (RCEINVARG);
30103831d35Sstevel 	}
30203831d35Sstevel 
30303831d35Sstevel 	dps = &rcs->dp_state;
30403831d35Sstevel 	drr = &dps->req_resp;
30503831d35Sstevel 	exp_resp = &drr->response;
30603831d35Sstevel 
3076fa6856eSarutz 	/*
3086fa6856eSarutz 	 * Handle the case where we are called during panic
3096fa6856eSarutz 	 * processing.  If that occurs, then another thread in
3106fa6856eSarutz 	 * rmc_comm might have been idled by panic() while
3116fa6856eSarutz 	 * holding dp_mutex.  As a result, do not unconditionally
3126fa6856eSarutz 	 * grab dp_mutex.
3136fa6856eSarutz 	 */
3146fa6856eSarutz 	if (ddi_in_panic() != 0) {
3156fa6856eSarutz 		if (mutex_tryenter(dps->dp_mutex) == 0) {
3166fa6856eSarutz 			return (RCENODATALINK);
3176fa6856eSarutz 		}
3186fa6856eSarutz 	} else {
31903831d35Sstevel 		mutex_enter(dps->dp_mutex);
3206fa6856eSarutz 	}
32103831d35Sstevel 
32203831d35Sstevel 	/*
32303831d35Sstevel 	 * if the data link set up is suspended, just return.
32403831d35Sstevel 	 * the only time that this can happen is during firmware download
32503831d35Sstevel 	 * (see rmc_comm_request_response_bp). Basically, the data link is
32603831d35Sstevel 	 * down and the timer for setting up the data link is not running.
32703831d35Sstevel 	 */
32803831d35Sstevel 	if (!dps->data_link_ok &&
32903831d35Sstevel 	    dps->timer_link_setup == (timeout_id_t)0) {
33003831d35Sstevel 
33103831d35Sstevel 		mutex_exit(dps->dp_mutex);
33203831d35Sstevel 		return (RCENODATALINK);
33303831d35Sstevel 	}
33403831d35Sstevel 
33503831d35Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d, req type=%x\n",
33603831d35Sstevel 	    dps->pending_request, request->msg_type));
33703831d35Sstevel 
33803831d35Sstevel 	rmc_comm_wait_enable_to_send(rcs, dps);
33903831d35Sstevel 
34003831d35Sstevel 	/*
34103831d35Sstevel 	 * We now have control of the RMC.
34203831d35Sstevel 	 * Place a lower limit on the shortest amount of time to be
34303831d35Sstevel 	 * waited before timing out while communicating with the RMC
34403831d35Sstevel 	 */
34503831d35Sstevel 	if (wait_time < DP_MIN_TIMEOUT)
34603831d35Sstevel 		wait_time = DP_MIN_TIMEOUT;
34703831d35Sstevel 
34803831d35Sstevel 	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
34903831d35Sstevel 
35003831d35Sstevel 	/*
35103831d35Sstevel 	 * initialization of the request/response data structure
35203831d35Sstevel 	 */
35303831d35Sstevel 	drr->flags = 0;
35403831d35Sstevel 	drr->error_status = 0;
35503831d35Sstevel 
35603831d35Sstevel 	/*
35703831d35Sstevel 	 * set the 'expected reply' buffer: get the buffer already allocated
35803831d35Sstevel 	 * for the response (if a reply is expected!)
35903831d35Sstevel 	 */
36003831d35Sstevel 	if (response != NULL) {
36103831d35Sstevel 		exp_resp->msg_type = response->msg_type;
36203831d35Sstevel 		exp_resp->msg_buf = (uint8_t *)response->msg_buf;
36303831d35Sstevel 		exp_resp->msg_msglen = (uint16_t)response->msg_bytes;
36403831d35Sstevel 		exp_resp->msg_bufsiz = (uint16_t)response->msg_len;
36503831d35Sstevel 	} else {
36603831d35Sstevel 		exp_resp->msg_type = DP_NULL_MSG;
36703831d35Sstevel 		exp_resp->msg_buf = (uint8_t)NULL;
36803831d35Sstevel 		exp_resp->msg_bufsiz = (uint16_t)0;
36903831d35Sstevel 		exp_resp->msg_msglen = (uint16_t)0;
37003831d35Sstevel 	}
37103831d35Sstevel 
37203831d35Sstevel 	/*
37303831d35Sstevel 	 * send the request message
37403831d35Sstevel 	 */
37503831d35Sstevel 	req.msg_type = request->msg_type;
37603831d35Sstevel 	req.msg_buf = (uint8_t *)request->msg_buf;
37703831d35Sstevel 	req.msg_msglen = (uint16_t)request->msg_len;
37803831d35Sstevel 
37903831d35Sstevel 	/*
38003831d35Sstevel 	 * send the message and wait for the reply or ACKnowledgment
38103831d35Sstevel 	 * re-send the message if reply/ACK is not received in the
38203831d35Sstevel 	 * timeframe defined
38303831d35Sstevel 	 */
38403831d35Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x\n", request->msg_type));
38503831d35Sstevel 
386*d3d50737SRafael Vanoni 	delta = drv_usectohz(TX_RETRY_TIME * 1000);
387*d3d50737SRafael Vanoni 
38803831d35Sstevel 	while ((err = rmc_comm_dp_msend(rcs, &req)) == RCNOERR) {
38903831d35Sstevel 
390*d3d50737SRafael Vanoni 		resend_clockt = ddi_get_lbolt() + delta;
39103831d35Sstevel 
39203831d35Sstevel 		/*
39303831d35Sstevel 		 * wait for a reply or an acknowledgement
39403831d35Sstevel 		 */
395*d3d50737SRafael Vanoni 		(void) cv_reltimedwait(drr->cv_wait_reply, dps->dp_mutex,
396*d3d50737SRafael Vanoni 		    delta, TR_CLOCK_TICK);
39703831d35Sstevel 
39803831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
39903831d35Sstevel 		    "reqresp send status: flags=%02x req=%x resp=%x tick=%ld\n",
40003831d35Sstevel 		    drr->flags, request->msg_type,
40103831d35Sstevel 		    response ? response->msg_type : -1,
40203831d35Sstevel 		    stop_clockt - resend_clockt));
40303831d35Sstevel 
40403831d35Sstevel 		/*
40503831d35Sstevel 		 * Check for error condition first
40603831d35Sstevel 		 * Then, check if the command has been replied/ACKed
40703831d35Sstevel 		 * Then, check if it has timeout and if there is any
40803831d35Sstevel 		 * time left to resend the message.
40903831d35Sstevel 		 */
41003831d35Sstevel 		if ((drr->flags & MSG_ERROR) != 0) {
41103831d35Sstevel 			if (drr->error_status == 0) {
41203831d35Sstevel 				err = RCEGENERIC;
41303831d35Sstevel 			} else {
41403831d35Sstevel 				err = drr->error_status;
41503831d35Sstevel 			}
41603831d35Sstevel 			break;
41703831d35Sstevel 
41803831d35Sstevel 		} else if (response != NULL &&
41903831d35Sstevel 		    (drr->flags & MSG_REPLY_RXED) != 0) {
42003831d35Sstevel 			/*
42103831d35Sstevel 			 * yes! here is the reply
42203831d35Sstevel 			 */
42303831d35Sstevel 
42403831d35Sstevel 			/*
42503831d35Sstevel 			 * get the actual length of the msg
42603831d35Sstevel 			 * a negative value means that the reply message
42703831d35Sstevel 			 * was too big for the receiver buffer
42803831d35Sstevel 			 */
42903831d35Sstevel 			response->msg_bytes = exp_resp->msg_msglen;
43003831d35Sstevel 			if (response->msg_bytes < 0)
43103831d35Sstevel 				err = RCEREPTOOBIG;
43203831d35Sstevel 			else
43303831d35Sstevel 				err = RCNOERR;
43403831d35Sstevel 			break;
43503831d35Sstevel 
43603831d35Sstevel 		} else if (response == NULL && (drr->flags & MSG_ACKED) != 0) {
43703831d35Sstevel 			/*
43803831d35Sstevel 			 * yes! message has been acknowledged
43903831d35Sstevel 			 */
44003831d35Sstevel 
44103831d35Sstevel 			err = RCNOERR;
44203831d35Sstevel 			break;
44303831d35Sstevel 
44403831d35Sstevel 		} else if ((stop_clockt - resend_clockt) <= 0) {
44503831d35Sstevel 			/*
44603831d35Sstevel 			 * no more time left. set the error code,
44703831d35Sstevel 			 * exit the loop
44803831d35Sstevel 			 */
44903831d35Sstevel 
45003831d35Sstevel 			err = RCETIMEOUT;
45103831d35Sstevel 			break;
45203831d35Sstevel 		}
45303831d35Sstevel 	}
45403831d35Sstevel 
45503831d35Sstevel 	rmc_comm_dp_mcleanup(rcs);
45603831d35Sstevel 
45703831d35Sstevel 	rmc_comm_wake_up_next(rcs);
45803831d35Sstevel 
45903831d35Sstevel 	mutex_exit(dps->dp_mutex);
46003831d35Sstevel 
46103831d35Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "reqresp end: err=%d, request=%x\n",
46203831d35Sstevel 	    err, request->msg_type));
46303831d35Sstevel 
46403831d35Sstevel 	return (err);
46503831d35Sstevel }
46603831d35Sstevel 
46703831d35Sstevel /*
46803831d35Sstevel  * Function used to send a BP (Boot Prom) message and get the reply.
46903831d35Sstevel  * BP protocol is provided only to support firmware download.
47003831d35Sstevel  *
47103831d35Sstevel  * This function will look for the following key BP protocol commands:
47203831d35Sstevel  * BP_OBP_BOOTINIT: the data link is brought down so that request/response
47303831d35Sstevel  * sessions cannot be started. The reason why is that this command will cause
47403831d35Sstevel  * RMC fw to jump to the boot monitor (BOOTMON_FLASH) and data protocol is not
47503831d35Sstevel  * operational. In this context, RMC fw will only be using the BP protocol.
47603831d35Sstevel  * BP_OBP_RESET: data link setup timer is resumed. This command cause the RMC
47703831d35Sstevel  * to reboot and hence become operational.
47803831d35Sstevel  */
47903831d35Sstevel int
rmc_comm_request_response_bp(rmc_comm_msg_t * request_bp,rmc_comm_msg_t * response_bp,uint32_t wait_time)48003831d35Sstevel rmc_comm_request_response_bp(rmc_comm_msg_t *request_bp,
48103831d35Sstevel     rmc_comm_msg_t *response_bp, uint32_t wait_time)
48203831d35Sstevel {
48303831d35Sstevel 	struct rmc_comm_state	*rcs;
48403831d35Sstevel 	rmc_comm_dp_state_t	*dps;
48503831d35Sstevel 	dp_req_resp_t		*drr;
48603831d35Sstevel 	dp_message_t		*resp_bp;
48703831d35Sstevel 	bp_msg_t		*bp_msg;
48803831d35Sstevel 	clock_t			stop_clockt;
48903831d35Sstevel 	int			err = RCNOERR;
49003831d35Sstevel 	boolean_t		bootinit_sent = 0;
49103831d35Sstevel 
49203831d35Sstevel 	/*
49303831d35Sstevel 	 * get the soft state struct (instance 0)
49403831d35Sstevel 	 */
49503831d35Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
49603831d35Sstevel 	    "rmc_comm_request_response_bp")) == NULL)
49703831d35Sstevel 		return (RCENOSOFTSTATE);
49803831d35Sstevel 
49903831d35Sstevel 	/*
50003831d35Sstevel 	 * sanity check: request_bp buffer must always be provided
50103831d35Sstevel 	 */
50203831d35Sstevel 	if (request_bp == NULL) {
50303831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp_bp, invalid args\n"));
50403831d35Sstevel 		return (RCEINVARG);
50503831d35Sstevel 	}
50603831d35Sstevel 
50703831d35Sstevel 	bp_msg = (bp_msg_t *)request_bp->msg_buf;
50803831d35Sstevel 
50903831d35Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "send request_bp=%x\n", bp_msg->cmd));
51003831d35Sstevel 
51103831d35Sstevel 	/*
51203831d35Sstevel 	 * only BP message can be sent
51303831d35Sstevel 	 */
51403831d35Sstevel 	if (!IS_BOOT_MSG(bp_msg->cmd)) {
51503831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
51603831d35Sstevel 		    "reqresp_bp, only BP msg are allowed! type=%x\n",
51703831d35Sstevel 		    bp_msg->cmd));
51803831d35Sstevel 		return (RCEINVARG);
51903831d35Sstevel 	}
52003831d35Sstevel 
52103831d35Sstevel 	dps = &rcs->dp_state;
52203831d35Sstevel 	drr = &dps->req_resp;
52303831d35Sstevel 	resp_bp = &drr->response;
52403831d35Sstevel 
52503831d35Sstevel 	mutex_enter(dps->dp_mutex);
52603831d35Sstevel 
52703831d35Sstevel 	rmc_comm_wait_enable_to_send(rcs, dps);
52803831d35Sstevel 
52903831d35Sstevel 	/*
53003831d35Sstevel 	 * Now, before sending the message, just check what it is being sent
53103831d35Sstevel 	 * and take action accordingly.
53203831d35Sstevel 	 *
53303831d35Sstevel 	 * is it BP_OBP_BOOTINIT or BP_OBP_RESET command?
53403831d35Sstevel 	 */
53503831d35Sstevel 	if (bp_msg->cmd == BP_OBP_BOOTINIT) {
53603831d35Sstevel 
53703831d35Sstevel 		/*
53803831d35Sstevel 		 * bring down the protocol data link
53903831d35Sstevel 		 * (must be done before aborting a request/response session)
54003831d35Sstevel 		 */
54103831d35Sstevel 		dps->data_link_ok = 0;
54203831d35Sstevel 		dps->timer_link_setup = (timeout_id_t)0;
54303831d35Sstevel 
54403831d35Sstevel 		bootinit_sent = 1;
54503831d35Sstevel 
54603831d35Sstevel 	} else if (bp_msg->cmd == BP_OBP_RESET) {
54703831d35Sstevel 
54803831d35Sstevel 		/*
54903831d35Sstevel 		 * restart the data link set up timer. RMC is coming up...
55003831d35Sstevel 		 */
55103831d35Sstevel 
55203831d35Sstevel 		dp_reset(rcs, INITIAL_SEQID, 0, 1);
55303831d35Sstevel 	}
55403831d35Sstevel 
55503831d35Sstevel 	/*
55603831d35Sstevel 	 * initialization of the request/response data structure
55703831d35Sstevel 	 */
55803831d35Sstevel 	drr->flags = 0;
55903831d35Sstevel 	drr->error_status = 0;
56003831d35Sstevel 
56103831d35Sstevel 	/*
56203831d35Sstevel 	 * set the reply buffer: get the buffer already allocated
56303831d35Sstevel 	 * for the response
56403831d35Sstevel 	 */
56503831d35Sstevel 	if (response_bp != NULL) {
56603831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "expect BP reply. len=%d\n",
56703831d35Sstevel 		    response_bp->msg_len));
56803831d35Sstevel 
56903831d35Sstevel 		resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
57003831d35Sstevel 		resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
57103831d35Sstevel 	}
57203831d35Sstevel 
57303831d35Sstevel 	/*
57403831d35Sstevel 	 * send the BP message and wait for the reply
57503831d35Sstevel 	 */
57603831d35Sstevel 
57703831d35Sstevel 	rmc_comm_bp_msend(rcs, bp_msg);
57803831d35Sstevel 
57903831d35Sstevel 	if (response_bp != NULL) {
58003831d35Sstevel 
58103831d35Sstevel 		/*
58203831d35Sstevel 		 * place a lower limit on the shortest amount of time to be
58303831d35Sstevel 		 * waited before timing out while communicating with the RMC
58403831d35Sstevel 		 */
58503831d35Sstevel 		if (wait_time < DP_MIN_TIMEOUT)
58603831d35Sstevel 			wait_time = DP_MIN_TIMEOUT;
58703831d35Sstevel 
58803831d35Sstevel 		stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
58903831d35Sstevel 
59003831d35Sstevel 		if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
59103831d35Sstevel 		    stop_clockt)) == RCNOERR) {
59203831d35Sstevel 
59303831d35Sstevel 			/*
59403831d35Sstevel 			 * get the actual length of the msg
59503831d35Sstevel 			 * a negative value means that the reply message
59603831d35Sstevel 			 * was too big for the receiver buffer
59703831d35Sstevel 			 */
59803831d35Sstevel 			response_bp->msg_bytes = resp_bp->msg_msglen;
59903831d35Sstevel 			if (response_bp->msg_bytes < 0) {
60003831d35Sstevel 				err = RCEREPTOOBIG;
60103831d35Sstevel 
60203831d35Sstevel 			} else if (bootinit_sent) {
60303831d35Sstevel 
60403831d35Sstevel 				/*
60503831d35Sstevel 				 * BOOTINIT cmd may fail. In this is the case,
60603831d35Sstevel 				 * the RMC is still operational. Hence, we
60703831d35Sstevel 				 * try (once) to set up the data link
60803831d35Sstevel 				 * protocol.
60903831d35Sstevel 				 */
61003831d35Sstevel 				bp_msg = (bp_msg_t *)response_bp->msg_buf;
61103831d35Sstevel 
61203831d35Sstevel 				if (bp_msg->cmd == BP_RSC_BOOTFAIL &&
61303831d35Sstevel 				    bp_msg->dat1 == BP_DAT1_REJECTED) {
61403831d35Sstevel 					(void) rmc_comm_dp_ctlsend(rcs,
61503831d35Sstevel 					    DP_CTL_START);
61603831d35Sstevel 				}
61703831d35Sstevel 			}
61803831d35Sstevel 		}
61903831d35Sstevel 	}
62003831d35Sstevel 
62103831d35Sstevel 	rmc_comm_dp_mcleanup(rcs);
62203831d35Sstevel 
62303831d35Sstevel 	rmc_comm_wake_up_next(rcs);
62403831d35Sstevel 
62503831d35Sstevel 	mutex_exit(dps->dp_mutex);
62603831d35Sstevel 
62703831d35Sstevel 	return (err);
62803831d35Sstevel }
62903831d35Sstevel 
63003831d35Sstevel 
63103831d35Sstevel /*
63203831d35Sstevel  * to register for an asynchronous (via soft interrupt) notification
63303831d35Sstevel  * of a message from the remote side (RMC)
63403831d35Sstevel  */
63503831d35Sstevel int
rmc_comm_reg_intr(uint8_t msg_type,rmc_comm_intrfunc_t intr_handler,rmc_comm_msg_t * msgbuf,uint_t * state,kmutex_t * lock)63603831d35Sstevel rmc_comm_reg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler,
63703831d35Sstevel     rmc_comm_msg_t *msgbuf, uint_t *state, kmutex_t *lock)
63803831d35Sstevel {
63903831d35Sstevel 	struct rmc_comm_state 	*rcs;
64003831d35Sstevel 	dp_msg_intr_t		*msgintr;
64103831d35Sstevel 	int			 err = RCNOERR;
64203831d35Sstevel 
64303831d35Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_reg_intr")) == NULL)
64403831d35Sstevel 		return (RCENOSOFTSTATE);
64503831d35Sstevel 
64603831d35Sstevel 	mutex_enter(rcs->dp_state.dp_mutex);
64703831d35Sstevel 
64803831d35Sstevel 	msgintr = &rcs->dp_state.msg_intr;
64903831d35Sstevel 
65003831d35Sstevel 	/*
65103831d35Sstevel 	 * lock is required. If it is not defined, the
65203831d35Sstevel 	 * interrupt handler routine cannot be registered.
65303831d35Sstevel 	 */
65403831d35Sstevel 	if (lock == NULL) {
65503831d35Sstevel 		mutex_exit(rcs->dp_state.dp_mutex);
65603831d35Sstevel 		return (RCEINVARG);
65703831d35Sstevel 	}
65803831d35Sstevel 
65903831d35Sstevel 	/*
66003831d35Sstevel 	 * only one interrupt handler can be registered.
66103831d35Sstevel 	 */
66203831d35Sstevel 	if (msgintr->intr_handler == NULL) {
66303831d35Sstevel 
66403831d35Sstevel 		if (ddi_add_softintr(rcs->dip, DDI_SOFTINT_HIGH,
66503831d35Sstevel 		    &msgintr->intr_id, NULL, NULL, intr_handler,
66603831d35Sstevel 		    (caddr_t)msgbuf) == DDI_SUCCESS) {
66703831d35Sstevel 
66803831d35Sstevel 			msgintr->intr_handler = intr_handler;
66903831d35Sstevel 			msgintr->intr_lock = lock;
67003831d35Sstevel 			msgintr->intr_state = state;
67103831d35Sstevel 			msgintr->intr_msg_type = msg_type;
67203831d35Sstevel 			msgintr->intr_arg = (caddr_t)msgbuf;
67303831d35Sstevel 		} else {
67403831d35Sstevel 			err = RCECANTREGINTR;
67503831d35Sstevel 		}
67603831d35Sstevel 	} else {
67703831d35Sstevel 		err = RCEALREADYREG;
67803831d35Sstevel 	}
67903831d35Sstevel 
68003831d35Sstevel 	mutex_exit(rcs->dp_state.dp_mutex);
68103831d35Sstevel 
68203831d35Sstevel 	return (err);
68303831d35Sstevel }
68403831d35Sstevel 
68503831d35Sstevel /*
68603831d35Sstevel  * To unregister for asynchronous notifications
68703831d35Sstevel  */
68803831d35Sstevel int
rmc_comm_unreg_intr(uint8_t msg_type,rmc_comm_intrfunc_t intr_handler)68903831d35Sstevel rmc_comm_unreg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler)
69003831d35Sstevel {
69103831d35Sstevel 	struct rmc_comm_state	*rcs;
69203831d35Sstevel 	dp_msg_intr_t		*msgintr;
69303831d35Sstevel 	int			 err = RCNOERR;
69403831d35Sstevel 
69503831d35Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_unreg_intr")) == NULL)
69603831d35Sstevel 		return (RCENOSOFTSTATE);
69703831d35Sstevel 
69803831d35Sstevel 	mutex_enter(rcs->dp_state.dp_mutex);
69903831d35Sstevel 
70003831d35Sstevel 	msgintr = &rcs->dp_state.msg_intr;
70103831d35Sstevel 
70203831d35Sstevel 	if (msgintr->intr_handler != NULL &&
70303831d35Sstevel 	    msgintr->intr_msg_type == msg_type &&
70403831d35Sstevel 	    msgintr->intr_handler == intr_handler) {
70503831d35Sstevel 
70603831d35Sstevel 		ddi_remove_softintr(msgintr->intr_id);
70703831d35Sstevel 		msgintr->intr_handler = NULL;
70803831d35Sstevel 		msgintr->intr_id = 0;
70903831d35Sstevel 		msgintr->intr_msg_type = 0;
71003831d35Sstevel 		msgintr->intr_arg = NULL;
71103831d35Sstevel 		msgintr->intr_lock = NULL;
71203831d35Sstevel 		msgintr->intr_state = NULL;
71303831d35Sstevel 	} else {
71403831d35Sstevel 		err = RCEGENERIC;
71503831d35Sstevel 	}
71603831d35Sstevel 
71703831d35Sstevel 	mutex_exit(rcs->dp_state.dp_mutex);
71803831d35Sstevel 
71903831d35Sstevel 	return (err);
72003831d35Sstevel }
72103831d35Sstevel 
72203831d35Sstevel /*
72303831d35Sstevel  * To send raw data (firmware s-records) down to the RMC.
72403831d35Sstevel  * It is provided only to support firmware download.
72503831d35Sstevel  */
72603831d35Sstevel int
rmc_comm_send_srecord_bp(caddr_t buf,int buflen,rmc_comm_msg_t * response_bp,uint32_t wait_time)72703831d35Sstevel rmc_comm_send_srecord_bp(caddr_t buf, int buflen,
72803831d35Sstevel     rmc_comm_msg_t *response_bp, uint32_t wait_time)
72903831d35Sstevel {
73003831d35Sstevel 	struct rmc_comm_state	*rcs;
73103831d35Sstevel 	rmc_comm_dp_state_t	*dps;
73203831d35Sstevel 	dp_req_resp_t		*drr;
73303831d35Sstevel 	dp_message_t		*resp_bp;
73403831d35Sstevel 	clock_t			stop_clockt;
73503831d35Sstevel 	int			err;
73603831d35Sstevel 
73703831d35Sstevel 	/*
73803831d35Sstevel 	 * get the soft state struct (instance 0)
73903831d35Sstevel 	 */
74003831d35Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
74103831d35Sstevel 	    "rmc_comm_request_response_bp")) == NULL)
74203831d35Sstevel 		return (RCENOSOFTSTATE);
74303831d35Sstevel 
74403831d35Sstevel 	/*
74503831d35Sstevel 	 * sanity check: response_bp buffer must always be provided
74603831d35Sstevel 	 */
74703831d35Sstevel 	if (buf == NULL || response_bp == NULL) {
74803831d35Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp,invalid args\n"));
74903831d35Sstevel 		return (RCEINVARG);
75003831d35Sstevel 	}
75103831d35Sstevel 
75203831d35Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp, buflen=%d\n", buflen));
75303831d35Sstevel 
75403831d35Sstevel 	dps = &rcs->dp_state;
75503831d35Sstevel 	drr = &dps->req_resp;
75603831d35Sstevel 	resp_bp = &drr->response;
75703831d35Sstevel 
75803831d35Sstevel 	mutex_enter(dps->dp_mutex);
75903831d35Sstevel 
76003831d35Sstevel 	rmc_comm_wait_enable_to_send(rcs, dps);
76103831d35Sstevel 
76203831d35Sstevel 	/*
76303831d35Sstevel 	 * initialization of the request/response data structure
76403831d35Sstevel 	 */
76503831d35Sstevel 	drr->flags = 0;
76603831d35Sstevel 	drr->error_status = 0;
76703831d35Sstevel 
76803831d35Sstevel 	/*
76903831d35Sstevel 	 * set the reply buffer: get the buffer already allocated
77003831d35Sstevel 	 * for the response
77103831d35Sstevel 	 */
77203831d35Sstevel 	resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
77303831d35Sstevel 	resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
77403831d35Sstevel 
77503831d35Sstevel 	/*
77603831d35Sstevel 	 * send raw data (s-record) and wait for the reply (BP message)
77703831d35Sstevel 	 */
77803831d35Sstevel 
77903831d35Sstevel 	rmc_comm_bp_srecsend(rcs, (char *)buf, buflen);
78003831d35Sstevel 
78103831d35Sstevel 	/*
78203831d35Sstevel 	 * place a lower limit on the shortest amount of time to be
78303831d35Sstevel 	 * waited before timing out while communicating with the RMC
78403831d35Sstevel 	 */
78503831d35Sstevel 	if (wait_time < DP_MIN_TIMEOUT)
78603831d35Sstevel 		wait_time = DP_MIN_TIMEOUT;
78703831d35Sstevel 
78803831d35Sstevel 	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
78903831d35Sstevel 
79003831d35Sstevel 	if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
79103831d35Sstevel 	    stop_clockt)) == RCNOERR) {
79203831d35Sstevel 		/*
79303831d35Sstevel 		 * get the actual length of the msg
79403831d35Sstevel 		 * a negative value means that the reply message
79503831d35Sstevel 		 * was too big for the receiver buffer
79603831d35Sstevel 		 */
79703831d35Sstevel 		response_bp->msg_bytes = resp_bp->msg_msglen;
79803831d35Sstevel 		if (response_bp->msg_bytes < 0) {
79903831d35Sstevel 			err = RCEREPTOOBIG;
80003831d35Sstevel 		}
80103831d35Sstevel 	}
80203831d35Sstevel 
80303831d35Sstevel 	rmc_comm_dp_mcleanup(rcs);
80403831d35Sstevel 
80503831d35Sstevel 	rmc_comm_wake_up_next(rcs);
80603831d35Sstevel 
80703831d35Sstevel 	mutex_exit(dps->dp_mutex);
80803831d35Sstevel 
80903831d35Sstevel 	return (err);
81003831d35Sstevel }
81103831d35Sstevel 
81203831d35Sstevel /*
81303831d35Sstevel  * To wait for (any) BP message to be received.
81403831d35Sstevel  * (dp_mutex must be held)
81503831d35Sstevel  */
81603831d35Sstevel static int
rmc_comm_wait_bp_reply(struct rmc_comm_state * rcs,rmc_comm_dp_state_t * dps,dp_req_resp_t * drr,clock_t stop_clockt)81703831d35Sstevel rmc_comm_wait_bp_reply(struct rmc_comm_state *rcs, rmc_comm_dp_state_t *dps,
81803831d35Sstevel     dp_req_resp_t *drr, clock_t stop_clockt)
81903831d35Sstevel {
82003831d35Sstevel 	clock_t clockleft = 1;
82103831d35Sstevel 	int err = RCNOERR;
82203831d35Sstevel 
82303831d35Sstevel 	clockleft = cv_timedwait(drr->cv_wait_reply, dps->dp_mutex,
82403831d35Sstevel 	    stop_clockt);
82503831d35Sstevel 
82603831d35Sstevel 
82703831d35Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT,
82803831d35Sstevel 	    "reqresp_bp, send: flags=%02x, clktick left=%ld\n",
82903831d35Sstevel 	    drr->flags, clockleft));
83003831d35Sstevel 
83103831d35Sstevel 	/*
83203831d35Sstevel 	 * Check for error condition first.
83303831d35Sstevel 	 * Then, check if it has timeout.
83403831d35Sstevel 	 * Then, check if the command has been replied.
83503831d35Sstevel 	 */
83603831d35Sstevel 	if ((drr->flags & MSG_ERROR) != 0) {
83703831d35Sstevel 
83803831d35Sstevel 		err = RCEGENERIC;
83903831d35Sstevel 
84003831d35Sstevel 	} else if (clockleft <= 0) {
84103831d35Sstevel 		/*
84203831d35Sstevel 		 * timeout
84303831d35Sstevel 		 */
84403831d35Sstevel 
84503831d35Sstevel 		err = RCETIMEOUT;
84603831d35Sstevel 
84703831d35Sstevel 	} else if ((drr->flags & MSG_RXED_BP) == 0) {
84803831d35Sstevel 
84903831d35Sstevel 		err = RCEGENERIC;
85003831d35Sstevel 	}
85103831d35Sstevel 
85203831d35Sstevel 	return (err);
85303831d35Sstevel }
85403831d35Sstevel 
85503831d35Sstevel /*
85603831d35Sstevel  * Wait for the pending_request flag to be cleared and acquire it for our
85703831d35Sstevel  * own use. The caller is then allowed to start a new request/response
85803831d35Sstevel  * session with the RMC.
85903831d35Sstevel  * Note that all send-receive actions to the RMC include a time-out, so
86003831d35Sstevel  * the pending-request must eventually go away - even if the RMC is down.
86103831d35Sstevel  * Hence there is no need to timeout the wait action of this function.
86203831d35Sstevel  * (dp_mutex must be held on entry).
86303831d35Sstevel  */
86403831d35Sstevel static void
rmc_comm_wait_enable_to_send(struct rmc_comm_state * rcs,rmc_comm_dp_state_t * dps)86503831d35Sstevel rmc_comm_wait_enable_to_send(struct rmc_comm_state *rcs,
86603831d35Sstevel     rmc_comm_dp_state_t *dps)
86703831d35Sstevel {
86803831d35Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d\n",
86903831d35Sstevel 	    dps->pending_request));
87003831d35Sstevel 
87103831d35Sstevel 	/*
87203831d35Sstevel 	 * A new message can actually grab the lock before the thread
87303831d35Sstevel 	 * that has just been signaled.  Therefore, we need to double
87403831d35Sstevel 	 * check to make sure that pending_request is not already set
87503831d35Sstevel 	 * after we wake up.
87603831d35Sstevel 	 *
87703831d35Sstevel 	 * Potentially this could mean starvation for certain unfortunate
87803831d35Sstevel 	 * threads that keep getting woken up and putting back to sleep.
87903831d35Sstevel 	 * But the window of such contention is very small to begin with.
88003831d35Sstevel 	 */
88103831d35Sstevel 
88203831d35Sstevel 	while (dps->pending_request) {
88303831d35Sstevel 		/*
88403831d35Sstevel 		 * just 'sit and wait' until there are no pending requests
88503831d35Sstevel 		 */
88603831d35Sstevel 
88703831d35Sstevel 		cv_wait(dps->cv_ok_to_send, dps->dp_mutex);
88803831d35Sstevel 	}
88903831d35Sstevel 
89003831d35Sstevel 	/*
89103831d35Sstevel 	 * now a request/response can be started. Set the flag so that nobody
89203831d35Sstevel 	 * else will be able to send anything.
89303831d35Sstevel 	 */
89403831d35Sstevel 	dps->pending_request = 1;
89503831d35Sstevel }
89603831d35Sstevel 
89703831d35Sstevel /*
89803831d35Sstevel  * To wake up one of the threads (if any) waiting for starting a
89903831d35Sstevel  * request/response session.
90003831d35Sstevel  * (dp_mutex must be held)
90103831d35Sstevel  */
90203831d35Sstevel static void
rmc_comm_wake_up_next(struct rmc_comm_state * rcs)90303831d35Sstevel rmc_comm_wake_up_next(struct rmc_comm_state *rcs)
90403831d35Sstevel {
90503831d35Sstevel 	/*
90603831d35Sstevel 	 * wake up eventual waiting threads...
90703831d35Sstevel 	 */
90803831d35Sstevel 
90903831d35Sstevel 	rcs->dp_state.pending_request = 0;
91003831d35Sstevel 	cv_signal(rcs->dp_state.cv_ok_to_send);
91103831d35Sstevel }
91203831d35Sstevel 
91303831d35Sstevel 
91403831d35Sstevel /*
91503831d35Sstevel  * thread which delivers pending request message to the rmc. Some leaf drivers
91603831d35Sstevel  * cannot afford to wait for a request to be replied/ACKed. Hence, a request
91703831d35Sstevel  * message is stored temporarily in the state structure and this thread
91803831d35Sstevel  * gets woken up to deliver it.
91903831d35Sstevel  */
92003831d35Sstevel static void
rmc_comm_send_pend_req(caddr_t arg)92103831d35Sstevel rmc_comm_send_pend_req(caddr_t arg)
92203831d35Sstevel {
92303831d35Sstevel 	struct rmc_comm_state		*rcs;
92403831d35Sstevel 	rmc_comm_drvintf_state_t	*dis;
92503831d35Sstevel 	callb_cpr_t			cprinfo;
92603831d35Sstevel 
92703831d35Sstevel 	if (arg == NULL) {
92803831d35Sstevel 		thread_exit();
92903831d35Sstevel 		/* NOTREACHED */
93003831d35Sstevel 	}
93103831d35Sstevel 
93203831d35Sstevel 	rcs = (struct rmc_comm_state *)arg;
93303831d35Sstevel 	dis = &rcs->drvi_state;
93403831d35Sstevel 
93503831d35Sstevel 	CALLB_CPR_INIT(&cprinfo, dis->dreq_mutex, callb_generic_cpr,
93603831d35Sstevel 	    "rmc_comm_send_pend_req");
93703831d35Sstevel 
93803831d35Sstevel 	mutex_enter(dis->dreq_mutex);
93903831d35Sstevel 
94003831d35Sstevel 	if (dis->dreq_state <= RMC_COMM_DREQ_ST_READY)
94103831d35Sstevel 		dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
94203831d35Sstevel 
94303831d35Sstevel 	for (;;) {
94403831d35Sstevel 
94503831d35Sstevel 		/*
94603831d35Sstevel 		 * Wait for someone to tell me to continue.
94703831d35Sstevel 		 */
94803831d35Sstevel 		while (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
94903831d35Sstevel 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
95003831d35Sstevel 			cv_wait(dis->dreq_sig_cv, dis->dreq_mutex);
95103831d35Sstevel 			CALLB_CPR_SAFE_END(&cprinfo, dis->dreq_mutex);
95203831d35Sstevel 		}
95303831d35Sstevel 
95403831d35Sstevel 		/* RMC_COMM_DREQ_ST_EXIT implies signal by _detach(). */
95503831d35Sstevel 		if (dis->dreq_state == RMC_COMM_DREQ_ST_EXIT) {
95603831d35Sstevel 			dis->dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
95703831d35Sstevel 			dis->dreq_tid = 0;
95803831d35Sstevel 
95903831d35Sstevel 			/* dis->dreq_mutex is held at this point! */
96003831d35Sstevel 			CALLB_CPR_EXIT(&cprinfo);
96103831d35Sstevel 
96203831d35Sstevel 			thread_exit();
96303831d35Sstevel 			/* NOTREACHED */
96403831d35Sstevel 		}
96503831d35Sstevel 
96603831d35Sstevel 		ASSERT(dis->dreq_state == RMC_COMM_DREQ_ST_PROCESS);
96703831d35Sstevel 		mutex_exit(dis->dreq_mutex);
96803831d35Sstevel 
96903831d35Sstevel 		/*
97003831d35Sstevel 		 * deliver the request (and wait...)
97103831d35Sstevel 		 */
97252f4394bSjfrank 		while (rmc_comm_send_req_resp(rcs, &dis->dreq_request, NULL,
97352f4394bSjfrank 		    RMC_COMM_DREQ_DEFAULT_TIME) == RCEGENERIC) {
97452f4394bSjfrank 		}
97503831d35Sstevel 
97603831d35Sstevel 		mutex_enter(dis->dreq_mutex);
97703831d35Sstevel 		if (dis->dreq_state != RMC_COMM_DREQ_ST_EXIT)
97803831d35Sstevel 			dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
97903831d35Sstevel 	}
98003831d35Sstevel }
98103831d35Sstevel 
98203831d35Sstevel /*
98303831d35Sstevel  * start thread to deal with pending requests to be delivered asynchronously
98403831d35Sstevel  * (i.e. leaf driver do not have to/cannot wait for a reply/ACk of a request)
98503831d35Sstevel  */
98603831d35Sstevel static int
rmc_comm_dreq_thread_start(struct rmc_comm_state * rcs)98703831d35Sstevel rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs)
98803831d35Sstevel {
98903831d35Sstevel 	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
99003831d35Sstevel 	int err = 0;
99103831d35Sstevel 	kthread_t *tp;
99203831d35Sstevel 
99303831d35Sstevel 	mutex_enter(dis->dreq_mutex);
99403831d35Sstevel 
99503831d35Sstevel 	if (dis->dreq_state == RMC_COMM_DREQ_ST_NOTSTARTED) {
99603831d35Sstevel 
99703831d35Sstevel 		tp = thread_create(NULL, 0, rmc_comm_send_pend_req,
99803831d35Sstevel 		    (caddr_t)rcs, 0, &p0, TS_RUN, maxclsyspri);
99903831d35Sstevel 		dis->dreq_state = RMC_COMM_DREQ_ST_READY;
100003831d35Sstevel 		dis->dreq_tid = tp->t_did;
100103831d35Sstevel 	}
100203831d35Sstevel 
100303831d35Sstevel 	mutex_exit(dis->dreq_mutex);
100403831d35Sstevel 
100503831d35Sstevel 	return (err);
100603831d35Sstevel }
100703831d35Sstevel 
100803831d35Sstevel /*
100903831d35Sstevel  * stop the thread (to deliver pending request messages)
101003831d35Sstevel  */
101103831d35Sstevel static void
rmc_comm_dreq_thread_kill(struct rmc_comm_state * rcs)101203831d35Sstevel rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs)
101303831d35Sstevel {
101403831d35Sstevel 	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
101503831d35Sstevel 	kt_did_t tid;
101603831d35Sstevel 
101703831d35Sstevel 	mutex_enter(dis->dreq_mutex);
101803831d35Sstevel 	tid = dis->dreq_tid;
101903831d35Sstevel 	if (tid != 0) {
102003831d35Sstevel 		dis->dreq_state = RMC_COMM_DREQ_ST_EXIT;
102103831d35Sstevel 		dis->dreq_tid = 0;
102203831d35Sstevel 		cv_signal(dis->dreq_sig_cv);
102303831d35Sstevel 	}
102403831d35Sstevel 	mutex_exit(dis->dreq_mutex);
102503831d35Sstevel 
102603831d35Sstevel 	/*
102703831d35Sstevel 	 * Wait for rmc_comm_send_pend_req() to finish
102803831d35Sstevel 	 */
102903831d35Sstevel 	if (tid != 0)
103003831d35Sstevel 		thread_join(tid);
103103831d35Sstevel }
103203831d35Sstevel 
103303831d35Sstevel /*
103403831d35Sstevel  * init function - start thread to deal with pending requests (no-wait requests)
103503831d35Sstevel  */
103603831d35Sstevel int
rmc_comm_drvintf_init(struct rmc_comm_state * rcs)103703831d35Sstevel rmc_comm_drvintf_init(struct rmc_comm_state *rcs)
103803831d35Sstevel {
103903831d35Sstevel 	int err = 0;
104003831d35Sstevel 
104103831d35Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_init\n"));
104203831d35Sstevel 	rcs->drvi_state.dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
104303831d35Sstevel 	rcs->drvi_state.dreq_tid = 0;
104403831d35Sstevel 
104503831d35Sstevel 	mutex_init(rcs->drvi_state.dreq_mutex, NULL, MUTEX_DRIVER, NULL);
104603831d35Sstevel 	cv_init(rcs->drvi_state.dreq_sig_cv, NULL, CV_DRIVER, NULL);
104703831d35Sstevel 
104803831d35Sstevel 	err = rmc_comm_dreq_thread_start(rcs);
104903831d35Sstevel 	if (err != 0) {
105003831d35Sstevel 		cv_destroy(rcs->drvi_state.dreq_sig_cv);
105103831d35Sstevel 		mutex_destroy(rcs->drvi_state.dreq_mutex);
105203831d35Sstevel 	}
105303831d35Sstevel 
105403831d35Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "thread started? err=%d\n", err));
105503831d35Sstevel 
105603831d35Sstevel 	return (err);
105703831d35Sstevel }
105803831d35Sstevel 
105903831d35Sstevel /*
106003831d35Sstevel  * fini function - kill thread to deal with pending requests (no-wait requests)
106103831d35Sstevel  */
106203831d35Sstevel void
rmc_comm_drvintf_fini(struct rmc_comm_state * rcs)106303831d35Sstevel rmc_comm_drvintf_fini(struct rmc_comm_state *rcs)
106403831d35Sstevel {
106503831d35Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:stop thread\n"));
106603831d35Sstevel 
106703831d35Sstevel 	rmc_comm_dreq_thread_kill(rcs);
106803831d35Sstevel 
106903831d35Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:destroy Mx/CVs\n"));
107003831d35Sstevel 
107103831d35Sstevel 	cv_destroy(rcs->drvi_state.dreq_sig_cv);
107203831d35Sstevel 	mutex_destroy(rcs->drvi_state.dreq_mutex);
107303831d35Sstevel }
1074