xref: /onnv-gate/usr/src/uts/sun4u/io/rmc_comm_drvintf.c (revision 11066:cebb50cbe4f9)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*11066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel /*
281708Sstevel  * provide the interface to the layered drivers (send request/receive
291708Sstevel  * response to the RMC
301708Sstevel  *
311708Sstevel  */
321708Sstevel 
331708Sstevel /*
341708Sstevel  *  Header files
351708Sstevel  */
361708Sstevel #include <sys/conf.h>
371708Sstevel #include <sys/callb.h>
381708Sstevel #include <sys/cyclic.h>
391708Sstevel #include <sys/membar.h>
401708Sstevel #include <sys/modctl.h>
411708Sstevel #include <sys/strlog.h>
421708Sstevel #include <sys/sunddi.h>
431708Sstevel #include <sys/ddi.h>
441708Sstevel #include <sys/types.h>
451708Sstevel #include <sys/disp.h>
461708Sstevel #include <sys/rmc_comm_dp.h>
471708Sstevel #include <sys/rmc_comm_dp_boot.h>
481708Sstevel #include <sys/rmc_comm_drvintf.h>
491708Sstevel #include <sys/rmc_comm.h>
501708Sstevel 
511708Sstevel void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);
521708Sstevel void dp_wake_up_waiter(struct rmc_comm_state *, uint8_t);
531708Sstevel 
541708Sstevel static int rmc_comm_send_req_resp(struct rmc_comm_state *rcs,
551708Sstevel     rmc_comm_msg_t *request, rmc_comm_msg_t *response, uint32_t wait_time);
561708Sstevel static int rmc_comm_wait_bp_reply(struct rmc_comm_state *,
571708Sstevel     rmc_comm_dp_state_t *, dp_req_resp_t *, clock_t);
581708Sstevel static void rmc_comm_wait_enable_to_send(struct rmc_comm_state *,
591708Sstevel     rmc_comm_dp_state_t *);
601708Sstevel static void rmc_comm_wake_up_next(struct rmc_comm_state *);
611708Sstevel static void rmc_comm_send_pend_req(caddr_t arg);
621708Sstevel static int rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs);
631708Sstevel static void rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs);
641708Sstevel 
651708Sstevel /*
661708Sstevel  * leaf driver to use this function to send a request to the remote side (RMC)
671708Sstevel  * and wait for a reply
681708Sstevel  */
691708Sstevel int
rmc_comm_request_response(rmc_comm_msg_t * request,rmc_comm_msg_t * response,uint32_t wait_time)701708Sstevel rmc_comm_request_response(rmc_comm_msg_t *request,
711708Sstevel     rmc_comm_msg_t *response, uint32_t wait_time)
721708Sstevel {
731708Sstevel 	struct rmc_comm_state	*rcs;
743482Sjfrank 	int err;
751708Sstevel 
761708Sstevel 	/*
771708Sstevel 	 * get the soft state struct (instance 0)
781708Sstevel 	 */
791708Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
80*11066Srafael.vanoni@sun.com 	    "rmc_comm_request_response")) == NULL)
811708Sstevel 		return (RCENOSOFTSTATE);
821708Sstevel 
833482Sjfrank 	do {
843482Sjfrank 		err = rmc_comm_send_req_resp(rcs, request, response, wait_time);
853482Sjfrank 	} while (err == RCEGENERIC);
863482Sjfrank 	return (err);
871708Sstevel }
881708Sstevel 
891708Sstevel /*
901708Sstevel  * leaf driver to use this function to send a request to the remote side (RMC)
911708Sstevel  * without waiting for a reply. If flag is RMC_COMM_DREQ_URGENT, the request
921708Sstevel  * message is sent once-off (an eventual pending request is aborted). This
931708Sstevel  * flag must only be used when try to send a request in critical condition
941708Sstevel  * (while the system is shutting down for instance and the CPU signature
951708Sstevel  * has to be sent). Otherwise, the request is stored in a temporary location
961708Sstevel  * and delivered by a thread.
971708Sstevel  */
981708Sstevel int
rmc_comm_request_nowait(rmc_comm_msg_t * request,uint8_t flag)991708Sstevel rmc_comm_request_nowait(rmc_comm_msg_t *request, uint8_t flag)
1001708Sstevel {
1011708Sstevel 	struct rmc_comm_state		*rcs;
1021708Sstevel 	rmc_comm_dp_state_t		*dps;
1031708Sstevel 	rmc_comm_drvintf_state_t	*dis;
1041708Sstevel 	dp_message_t			req;
1051708Sstevel 	int				err = RCNOERR;
1061708Sstevel 	uint8_t				flags = 0;
1071708Sstevel 
1081708Sstevel 	/*
1091708Sstevel 	 * get the soft state struct (instance 0)
1101708Sstevel 	 */
1111708Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
112*11066Srafael.vanoni@sun.com 	    "rmc_comm_request_response")) == NULL)
1131708Sstevel 		return (RCENOSOFTSTATE);
1141708Sstevel 
1151708Sstevel 	/*
1161708Sstevel 	 * just a sanity check...
1171708Sstevel 	 */
1181708Sstevel 	if (request == NULL) {
1191708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "reqnowait, invalid args\n"));
1201708Sstevel 		return (RCEINVARG);
1211708Sstevel 	}
1221708Sstevel 
1231708Sstevel 	if (!IS_NUMBERED_MSG(request->msg_type)) {
1241708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
1251708Sstevel 		    "reqnowait, ctrl msg not allowed! req type=%x\n",
1261708Sstevel 		    request->msg_type));
1271708Sstevel 		return (RCEINVARG);
1281708Sstevel 	}
1291708Sstevel 
1301708Sstevel 	if (flag == RMC_COMM_DREQ_URGENT) {
1311708Sstevel 		/*
1321708Sstevel 		 * Send this request with high priority i.e. abort eventual
1331708Sstevel 		 * request/response pending sessions.
1341708Sstevel 		 */
1351708Sstevel 
1361708Sstevel 		dps = &rcs->dp_state;
1371708Sstevel 
1381708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "going to send request=%x (URG)\n",
1391708Sstevel 		    request->msg_type));
1401708Sstevel 
1412749Sarutz 		/*
1422749Sarutz 		 * Handle the case where we are called during panic
1432749Sarutz 		 * processing.  If that occurs, then another thread in
1442749Sarutz 		 * rmc_comm might have been idled by panic() while
1452749Sarutz 		 * holding dp_mutex.  As a result, do not unconditionally
1462749Sarutz 		 * grab dp_mutex.
1472749Sarutz 		 */
1482749Sarutz 		if (ddi_in_panic() != 0) {
1492749Sarutz 			if (mutex_tryenter(dps->dp_mutex) == 0) {
1502749Sarutz 				return (RCENODATALINK);
1512749Sarutz 			}
1522749Sarutz 		} else {
1532749Sarutz 			mutex_enter(dps->dp_mutex);
1542749Sarutz 		}
1551708Sstevel 
1561708Sstevel 		/*
1571708Sstevel 		 * send the request only if the protocol data link is up.
1581708Sstevel 		 * it is pointless to send it in the other case.
1591708Sstevel 		 */
1601708Sstevel 		if (dps->data_link_ok) {
1611708Sstevel 
1621708Sstevel 			/*
1631708Sstevel 			 * clean up an eventual pending request/response session
1641708Sstevel 			 * (save its current status)
1651708Sstevel 			 */
1661708Sstevel 			if (dps->pending_request) {
1671708Sstevel 				flags = dps->req_resp.flags;
1681708Sstevel 				rmc_comm_dp_mcleanup(rcs);
1691708Sstevel 			}
1701708Sstevel 
1711708Sstevel 			/*
1721708Sstevel 			 * send the request message
1731708Sstevel 			 */
1741708Sstevel 			req.msg_type = request->msg_type;
1751708Sstevel 			req.msg_buf = (uint8_t *)request->msg_buf;
1761708Sstevel 			req.msg_msglen = (uint16_t)request->msg_len;
1771708Sstevel 
1781708Sstevel 			DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x (URG)\n",
1791708Sstevel 			    request->msg_type));
1801708Sstevel 
1811708Sstevel 			err = rmc_comm_dp_msend(rcs, &req);
1821708Sstevel 
1831708Sstevel 			/*
1841708Sstevel 			 * wait for fifos to drain
1851708Sstevel 			 */
1861708Sstevel 			rmc_comm_serdev_drain(rcs);
1871708Sstevel 
1881708Sstevel 			/*
1891708Sstevel 			 * clean up the current session
1901708Sstevel 			 */
1911708Sstevel 			rmc_comm_dp_mcleanup(rcs);
1921708Sstevel 
1931708Sstevel 			/*
1941708Sstevel 			 * abort an old session (if any)
1951708Sstevel 			 */
1961708Sstevel 			if (dps->pending_request) {
1971708Sstevel 				dps->req_resp.flags = flags;
1981708Sstevel 				dp_wake_up_waiter(rcs, MSG_ERROR);
1991708Sstevel 			}
2001708Sstevel 		}
2011708Sstevel 
2021708Sstevel 		mutex_exit(dps->dp_mutex);
2031708Sstevel 
2041708Sstevel 	} else {
2051708Sstevel 
2061708Sstevel 		/*
2071708Sstevel 		 * Get an 'independent' thread (rmc_comm_send_pend_req)
2081708Sstevel 		 * to send this request (since the calling thread does not
2091708Sstevel 		 * want to wait). Copy the request in the drvintf state
2101708Sstevel 		 * structure and signal the thread.
2111708Sstevel 		 */
2121708Sstevel 
2131708Sstevel 		dis = &rcs->drvi_state;
2141708Sstevel 
2151708Sstevel 		mutex_enter(dis->dreq_mutex);
2161708Sstevel 
2171708Sstevel 		if (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
2181708Sstevel 
2191708Sstevel 			DPRINTF(rcs, DAPI, (CE_CONT, "get to send request=%x\n",
2201708Sstevel 			    request->msg_type));
2211708Sstevel 
2221708Sstevel 			/*
2231708Sstevel 			 * copy the request in a temporary location
2241708Sstevel 			 * (drvinf_state structure) and signal the thread
2251708Sstevel 			 * that a request message has to be delivered
2261708Sstevel 			 */
2271708Sstevel 
2281708Sstevel 			if (request->msg_len < DP_MAX_MSGLEN) {
2291708Sstevel 				dis->dreq_request.msg_type = request->msg_type;
2301708Sstevel 				dis->dreq_request.msg_len = request->msg_len;
2311708Sstevel 				dis->dreq_request.msg_buf =
2321708Sstevel 				    dis->dreq_request_buf;
2331708Sstevel 				bcopy(request->msg_buf,
2341708Sstevel 				    dis->dreq_request.msg_buf,
2351708Sstevel 				    request->msg_len);
2361708Sstevel 
2371708Sstevel 				dis->dreq_state = RMC_COMM_DREQ_ST_PROCESS;
2381708Sstevel 				cv_signal(dis->dreq_sig_cv);
2391708Sstevel 
2401708Sstevel 			} else {
2411708Sstevel 				/*
2421708Sstevel 				 * not enough space to hold the request
2431708Sstevel 				 */
2441708Sstevel 				err = RCEREPTOOBIG;
2451708Sstevel 			}
2461708Sstevel 		} else {
2471708Sstevel 
2481708Sstevel 			DPRINTF(rcs, DAPI, (CE_CONT, "cannot get to send "
2491708Sstevel 			    "request=%x (busy)\n", request->msg_type));
2501708Sstevel 
2511708Sstevel 			/*
2521708Sstevel 			 * only one request per time can be processed.
2531708Sstevel 			 * the thread is either busy (RMC_COMM_DREQ_ST_PROCESS)
2541708Sstevel 			 * or terminating (RMC_COMM_DREQ_ST_EXIT)
2551708Sstevel 			 */
2561708Sstevel 			err = RCEGENERIC;
2571708Sstevel 		}
2581708Sstevel 
2591708Sstevel 		mutex_exit(dis->dreq_mutex);
2601708Sstevel 	}
2611708Sstevel 
2621708Sstevel 	return (err);
2631708Sstevel }
2641708Sstevel 
2651708Sstevel /*
2661708Sstevel  * Function used to send a request and (eventually) wait for a response.
2671708Sstevel  * It can be called from a leaf driver (via rmc_comm_request_response) or
2681708Sstevel  * from the thread in charge of sending 'no-wait' requests
2691708Sstevel  * (rmc_comm_send_pend_req).
2701708Sstevel  */
2711708Sstevel 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)2721708Sstevel rmc_comm_send_req_resp(struct rmc_comm_state *rcs, rmc_comm_msg_t *request,
2731708Sstevel     rmc_comm_msg_t *response, uint32_t wait_time)
2741708Sstevel {
2751708Sstevel 	rmc_comm_dp_state_t	*dps;
2761708Sstevel 	dp_req_resp_t		*drr;
2771708Sstevel 	dp_message_t		*exp_resp;
2781708Sstevel 	dp_message_t		req;
279*11066Srafael.vanoni@sun.com 	clock_t			resend_clockt, delta;
2801708Sstevel 	clock_t			stop_clockt;
2811708Sstevel 	int			err;
2821708Sstevel 
2831708Sstevel 
2841708Sstevel 	/*
2851708Sstevel 	 * just a sanity check...
2861708Sstevel 	 */
2871708Sstevel 	if (request == NULL) {
2881708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp, invalid args\n"));
2891708Sstevel 		return (RCEINVARG);
2901708Sstevel 	}
2911708Sstevel 
2921708Sstevel 	/*
2931708Sstevel 	 * drivers cannot send control messages at all. They are meant to
2941708Sstevel 	 * be used at low level only.
2951708Sstevel 	 */
2961708Sstevel 	if (!IS_NUMBERED_MSG(request->msg_type)) {
2971708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
2981708Sstevel 		    "reqresp, ctrl msg not allowed! req type=%x\n",
2991708Sstevel 		    request->msg_type));
3001708Sstevel 		return (RCEINVARG);
3011708Sstevel 	}
3021708Sstevel 
3031708Sstevel 	dps = &rcs->dp_state;
3041708Sstevel 	drr = &dps->req_resp;
3051708Sstevel 	exp_resp = &drr->response;
3061708Sstevel 
3072749Sarutz 	/*
3082749Sarutz 	 * Handle the case where we are called during panic
3092749Sarutz 	 * processing.  If that occurs, then another thread in
3102749Sarutz 	 * rmc_comm might have been idled by panic() while
3112749Sarutz 	 * holding dp_mutex.  As a result, do not unconditionally
3122749Sarutz 	 * grab dp_mutex.
3132749Sarutz 	 */
3142749Sarutz 	if (ddi_in_panic() != 0) {
3152749Sarutz 		if (mutex_tryenter(dps->dp_mutex) == 0) {
3162749Sarutz 			return (RCENODATALINK);
3172749Sarutz 		}
3182749Sarutz 	} else {
3192749Sarutz 		mutex_enter(dps->dp_mutex);
3202749Sarutz 	}
3211708Sstevel 
3221708Sstevel 	/*
3231708Sstevel 	 * if the data link set up is suspended, just return.
3241708Sstevel 	 * the only time that this can happen is during firmware download
3251708Sstevel 	 * (see rmc_comm_request_response_bp). Basically, the data link is
3261708Sstevel 	 * down and the timer for setting up the data link is not running.
3271708Sstevel 	 */
3281708Sstevel 	if (!dps->data_link_ok &&
3291708Sstevel 	    dps->timer_link_setup == (timeout_id_t)0) {
3301708Sstevel 
3311708Sstevel 		mutex_exit(dps->dp_mutex);
3321708Sstevel 		return (RCENODATALINK);
3331708Sstevel 	}
3341708Sstevel 
3351708Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d, req type=%x\n",
3361708Sstevel 	    dps->pending_request, request->msg_type));
3371708Sstevel 
3381708Sstevel 	rmc_comm_wait_enable_to_send(rcs, dps);
3391708Sstevel 
3401708Sstevel 	/*
3411708Sstevel 	 * We now have control of the RMC.
3421708Sstevel 	 * Place a lower limit on the shortest amount of time to be
3431708Sstevel 	 * waited before timing out while communicating with the RMC
3441708Sstevel 	 */
3451708Sstevel 	if (wait_time < DP_MIN_TIMEOUT)
3461708Sstevel 		wait_time = DP_MIN_TIMEOUT;
3471708Sstevel 
3481708Sstevel 	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
3491708Sstevel 
3501708Sstevel 	/*
3511708Sstevel 	 * initialization of the request/response data structure
3521708Sstevel 	 */
3531708Sstevel 	drr->flags = 0;
3541708Sstevel 	drr->error_status = 0;
3551708Sstevel 
3561708Sstevel 	/*
3571708Sstevel 	 * set the 'expected reply' buffer: get the buffer already allocated
3581708Sstevel 	 * for the response (if a reply is expected!)
3591708Sstevel 	 */
3601708Sstevel 	if (response != NULL) {
3611708Sstevel 		exp_resp->msg_type = response->msg_type;
3621708Sstevel 		exp_resp->msg_buf = (uint8_t *)response->msg_buf;
3631708Sstevel 		exp_resp->msg_msglen = (uint16_t)response->msg_bytes;
3641708Sstevel 		exp_resp->msg_bufsiz = (uint16_t)response->msg_len;
3651708Sstevel 	} else {
3661708Sstevel 		exp_resp->msg_type = DP_NULL_MSG;
3671708Sstevel 		exp_resp->msg_buf = (uint8_t)NULL;
3681708Sstevel 		exp_resp->msg_bufsiz = (uint16_t)0;
3691708Sstevel 		exp_resp->msg_msglen = (uint16_t)0;
3701708Sstevel 	}
3711708Sstevel 
3721708Sstevel 	/*
3731708Sstevel 	 * send the request message
3741708Sstevel 	 */
3751708Sstevel 	req.msg_type = request->msg_type;
3761708Sstevel 	req.msg_buf = (uint8_t *)request->msg_buf;
3771708Sstevel 	req.msg_msglen = (uint16_t)request->msg_len;
3781708Sstevel 
3791708Sstevel 	/*
3801708Sstevel 	 * send the message and wait for the reply or ACKnowledgment
3811708Sstevel 	 * re-send the message if reply/ACK is not received in the
3821708Sstevel 	 * timeframe defined
3831708Sstevel 	 */
3841708Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x\n", request->msg_type));
3851708Sstevel 
386*11066Srafael.vanoni@sun.com 	delta = drv_usectohz(TX_RETRY_TIME * 1000);
387*11066Srafael.vanoni@sun.com 
3881708Sstevel 	while ((err = rmc_comm_dp_msend(rcs, &req)) == RCNOERR) {
3891708Sstevel 
390*11066Srafael.vanoni@sun.com 		resend_clockt = ddi_get_lbolt() + delta;
3911708Sstevel 
3921708Sstevel 		/*
3931708Sstevel 		 * wait for a reply or an acknowledgement
3941708Sstevel 		 */
395*11066Srafael.vanoni@sun.com 		(void) cv_reltimedwait(drr->cv_wait_reply, dps->dp_mutex,
396*11066Srafael.vanoni@sun.com 		    delta, TR_CLOCK_TICK);
3971708Sstevel 
3981708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
3991708Sstevel 		    "reqresp send status: flags=%02x req=%x resp=%x tick=%ld\n",
4001708Sstevel 		    drr->flags, request->msg_type,
4011708Sstevel 		    response ? response->msg_type : -1,
4021708Sstevel 		    stop_clockt - resend_clockt));
4031708Sstevel 
4041708Sstevel 		/*
4051708Sstevel 		 * Check for error condition first
4061708Sstevel 		 * Then, check if the command has been replied/ACKed
4071708Sstevel 		 * Then, check if it has timeout and if there is any
4081708Sstevel 		 * time left to resend the message.
4091708Sstevel 		 */
4101708Sstevel 		if ((drr->flags & MSG_ERROR) != 0) {
4111708Sstevel 			if (drr->error_status == 0) {
4121708Sstevel 				err = RCEGENERIC;
4131708Sstevel 			} else {
4141708Sstevel 				err = drr->error_status;
4151708Sstevel 			}
4161708Sstevel 			break;
4171708Sstevel 
4181708Sstevel 		} else if (response != NULL &&
4191708Sstevel 		    (drr->flags & MSG_REPLY_RXED) != 0) {
4201708Sstevel 			/*
4211708Sstevel 			 * yes! here is the reply
4221708Sstevel 			 */
4231708Sstevel 
4241708Sstevel 			/*
4251708Sstevel 			 * get the actual length of the msg
4261708Sstevel 			 * a negative value means that the reply message
4271708Sstevel 			 * was too big for the receiver buffer
4281708Sstevel 			 */
4291708Sstevel 			response->msg_bytes = exp_resp->msg_msglen;
4301708Sstevel 			if (response->msg_bytes < 0)
4311708Sstevel 				err = RCEREPTOOBIG;
4321708Sstevel 			else
4331708Sstevel 				err = RCNOERR;
4341708Sstevel 			break;
4351708Sstevel 
4361708Sstevel 		} else if (response == NULL && (drr->flags & MSG_ACKED) != 0) {
4371708Sstevel 			/*
4381708Sstevel 			 * yes! message has been acknowledged
4391708Sstevel 			 */
4401708Sstevel 
4411708Sstevel 			err = RCNOERR;
4421708Sstevel 			break;
4431708Sstevel 
4441708Sstevel 		} else if ((stop_clockt - resend_clockt) <= 0) {
4451708Sstevel 			/*
4461708Sstevel 			 * no more time left. set the error code,
4471708Sstevel 			 * exit the loop
4481708Sstevel 			 */
4491708Sstevel 
4501708Sstevel 			err = RCETIMEOUT;
4511708Sstevel 			break;
4521708Sstevel 		}
4531708Sstevel 	}
4541708Sstevel 
4551708Sstevel 	rmc_comm_dp_mcleanup(rcs);
4561708Sstevel 
4571708Sstevel 	rmc_comm_wake_up_next(rcs);
4581708Sstevel 
4591708Sstevel 	mutex_exit(dps->dp_mutex);
4601708Sstevel 
4611708Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "reqresp end: err=%d, request=%x\n",
4621708Sstevel 	    err, request->msg_type));
4631708Sstevel 
4641708Sstevel 	return (err);
4651708Sstevel }
4661708Sstevel 
4671708Sstevel /*
4681708Sstevel  * Function used to send a BP (Boot Prom) message and get the reply.
4691708Sstevel  * BP protocol is provided only to support firmware download.
4701708Sstevel  *
4711708Sstevel  * This function will look for the following key BP protocol commands:
4721708Sstevel  * BP_OBP_BOOTINIT: the data link is brought down so that request/response
4731708Sstevel  * sessions cannot be started. The reason why is that this command will cause
4741708Sstevel  * RMC fw to jump to the boot monitor (BOOTMON_FLASH) and data protocol is not
4751708Sstevel  * operational. In this context, RMC fw will only be using the BP protocol.
4761708Sstevel  * BP_OBP_RESET: data link setup timer is resumed. This command cause the RMC
4771708Sstevel  * to reboot and hence become operational.
4781708Sstevel  */
4791708Sstevel int
rmc_comm_request_response_bp(rmc_comm_msg_t * request_bp,rmc_comm_msg_t * response_bp,uint32_t wait_time)4801708Sstevel rmc_comm_request_response_bp(rmc_comm_msg_t *request_bp,
4811708Sstevel     rmc_comm_msg_t *response_bp, uint32_t wait_time)
4821708Sstevel {
4831708Sstevel 	struct rmc_comm_state	*rcs;
4841708Sstevel 	rmc_comm_dp_state_t	*dps;
4851708Sstevel 	dp_req_resp_t		*drr;
4861708Sstevel 	dp_message_t		*resp_bp;
4871708Sstevel 	bp_msg_t		*bp_msg;
4881708Sstevel 	clock_t			stop_clockt;
4891708Sstevel 	int			err = RCNOERR;
4901708Sstevel 	boolean_t		bootinit_sent = 0;
4911708Sstevel 
4921708Sstevel 	/*
4931708Sstevel 	 * get the soft state struct (instance 0)
4941708Sstevel 	 */
4951708Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
496*11066Srafael.vanoni@sun.com 	    "rmc_comm_request_response_bp")) == NULL)
4971708Sstevel 		return (RCENOSOFTSTATE);
4981708Sstevel 
4991708Sstevel 	/*
5001708Sstevel 	 * sanity check: request_bp buffer must always be provided
5011708Sstevel 	 */
5021708Sstevel 	if (request_bp == NULL) {
5031708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "reqresp_bp, invalid args\n"));
5041708Sstevel 		return (RCEINVARG);
5051708Sstevel 	}
5061708Sstevel 
5071708Sstevel 	bp_msg = (bp_msg_t *)request_bp->msg_buf;
5081708Sstevel 
5091708Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "send request_bp=%x\n", bp_msg->cmd));
5101708Sstevel 
5111708Sstevel 	/*
5121708Sstevel 	 * only BP message can be sent
5131708Sstevel 	 */
5141708Sstevel 	if (!IS_BOOT_MSG(bp_msg->cmd)) {
5151708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT,
5161708Sstevel 		    "reqresp_bp, only BP msg are allowed! type=%x\n",
5171708Sstevel 		    bp_msg->cmd));
5181708Sstevel 		return (RCEINVARG);
5191708Sstevel 	}
5201708Sstevel 
5211708Sstevel 	dps = &rcs->dp_state;
5221708Sstevel 	drr = &dps->req_resp;
5231708Sstevel 	resp_bp = &drr->response;
5241708Sstevel 
5251708Sstevel 	mutex_enter(dps->dp_mutex);
5261708Sstevel 
5271708Sstevel 	rmc_comm_wait_enable_to_send(rcs, dps);
5281708Sstevel 
5291708Sstevel 	/*
5301708Sstevel 	 * Now, before sending the message, just check what it is being sent
5311708Sstevel 	 * and take action accordingly.
5321708Sstevel 	 *
5331708Sstevel 	 * is it BP_OBP_BOOTINIT or BP_OBP_RESET command?
5341708Sstevel 	 */
5351708Sstevel 	if (bp_msg->cmd == BP_OBP_BOOTINIT) {
5361708Sstevel 
5371708Sstevel 		/*
5381708Sstevel 		 * bring down the protocol data link
5391708Sstevel 		 * (must be done before aborting a request/response session)
5401708Sstevel 		 */
5411708Sstevel 		dps->data_link_ok = 0;
5421708Sstevel 		dps->timer_link_setup = (timeout_id_t)0;
5431708Sstevel 
5441708Sstevel 		bootinit_sent = 1;
5451708Sstevel 
5461708Sstevel 	} else if (bp_msg->cmd == BP_OBP_RESET) {
5471708Sstevel 
5481708Sstevel 		/*
5491708Sstevel 		 * restart the data link set up timer. RMC is coming up...
5501708Sstevel 		 */
5511708Sstevel 
5521708Sstevel 		dp_reset(rcs, INITIAL_SEQID, 0, 1);
5531708Sstevel 	}
5541708Sstevel 
5551708Sstevel 	/*
5561708Sstevel 	 * initialization of the request/response data structure
5571708Sstevel 	 */
5581708Sstevel 	drr->flags = 0;
5591708Sstevel 	drr->error_status = 0;
5601708Sstevel 
5611708Sstevel 	/*
5621708Sstevel 	 * set the reply buffer: get the buffer already allocated
5631708Sstevel 	 * for the response
5641708Sstevel 	 */
5651708Sstevel 	if (response_bp != NULL) {
5661708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "expect BP reply. len=%d\n",
5671708Sstevel 		    response_bp->msg_len));
5681708Sstevel 
5691708Sstevel 		resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
5701708Sstevel 		resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
5711708Sstevel 	}
5721708Sstevel 
5731708Sstevel 	/*
5741708Sstevel 	 * send the BP message and wait for the reply
5751708Sstevel 	 */
5761708Sstevel 
5771708Sstevel 	rmc_comm_bp_msend(rcs, bp_msg);
5781708Sstevel 
5791708Sstevel 	if (response_bp != NULL) {
5801708Sstevel 
5811708Sstevel 		/*
5821708Sstevel 		 * place a lower limit on the shortest amount of time to be
5831708Sstevel 		 * waited before timing out while communicating with the RMC
5841708Sstevel 		 */
5851708Sstevel 		if (wait_time < DP_MIN_TIMEOUT)
5861708Sstevel 			wait_time = DP_MIN_TIMEOUT;
5871708Sstevel 
5881708Sstevel 		stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
5891708Sstevel 
5901708Sstevel 		if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
5911708Sstevel 		    stop_clockt)) == RCNOERR) {
5921708Sstevel 
5931708Sstevel 			/*
5941708Sstevel 			 * get the actual length of the msg
5951708Sstevel 			 * a negative value means that the reply message
5961708Sstevel 			 * was too big for the receiver buffer
5971708Sstevel 			 */
5981708Sstevel 			response_bp->msg_bytes = resp_bp->msg_msglen;
5991708Sstevel 			if (response_bp->msg_bytes < 0) {
6001708Sstevel 				err = RCEREPTOOBIG;
6011708Sstevel 
6021708Sstevel 			} else if (bootinit_sent) {
6031708Sstevel 
6041708Sstevel 				/*
6051708Sstevel 				 * BOOTINIT cmd may fail. In this is the case,
6061708Sstevel 				 * the RMC is still operational. Hence, we
6071708Sstevel 				 * try (once) to set up the data link
6081708Sstevel 				 * protocol.
6091708Sstevel 				 */
6101708Sstevel 				bp_msg = (bp_msg_t *)response_bp->msg_buf;
6111708Sstevel 
6121708Sstevel 				if (bp_msg->cmd == BP_RSC_BOOTFAIL &&
6131708Sstevel 				    bp_msg->dat1 == BP_DAT1_REJECTED) {
6141708Sstevel 					(void) rmc_comm_dp_ctlsend(rcs,
6151708Sstevel 					    DP_CTL_START);
6161708Sstevel 				}
6171708Sstevel 			}
6181708Sstevel 		}
6191708Sstevel 	}
6201708Sstevel 
6211708Sstevel 	rmc_comm_dp_mcleanup(rcs);
6221708Sstevel 
6231708Sstevel 	rmc_comm_wake_up_next(rcs);
6241708Sstevel 
6251708Sstevel 	mutex_exit(dps->dp_mutex);
6261708Sstevel 
6271708Sstevel 	return (err);
6281708Sstevel }
6291708Sstevel 
6301708Sstevel 
6311708Sstevel /*
6321708Sstevel  * to register for an asynchronous (via soft interrupt) notification
6331708Sstevel  * of a message from the remote side (RMC)
6341708Sstevel  */
6351708Sstevel 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)6361708Sstevel rmc_comm_reg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler,
6371708Sstevel     rmc_comm_msg_t *msgbuf, uint_t *state, kmutex_t *lock)
6381708Sstevel {
6391708Sstevel 	struct rmc_comm_state 	*rcs;
6401708Sstevel 	dp_msg_intr_t		*msgintr;
6411708Sstevel 	int			 err = RCNOERR;
6421708Sstevel 
6431708Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_reg_intr")) == NULL)
6441708Sstevel 		return (RCENOSOFTSTATE);
6451708Sstevel 
6461708Sstevel 	mutex_enter(rcs->dp_state.dp_mutex);
6471708Sstevel 
6481708Sstevel 	msgintr = &rcs->dp_state.msg_intr;
6491708Sstevel 
6501708Sstevel 	/*
6511708Sstevel 	 * lock is required. If it is not defined, the
6521708Sstevel 	 * interrupt handler routine cannot be registered.
6531708Sstevel 	 */
6541708Sstevel 	if (lock == NULL) {
6551708Sstevel 		mutex_exit(rcs->dp_state.dp_mutex);
6561708Sstevel 		return (RCEINVARG);
6571708Sstevel 	}
6581708Sstevel 
6591708Sstevel 	/*
6601708Sstevel 	 * only one interrupt handler can be registered.
6611708Sstevel 	 */
6621708Sstevel 	if (msgintr->intr_handler == NULL) {
6631708Sstevel 
6641708Sstevel 		if (ddi_add_softintr(rcs->dip, DDI_SOFTINT_HIGH,
6651708Sstevel 		    &msgintr->intr_id, NULL, NULL, intr_handler,
6661708Sstevel 		    (caddr_t)msgbuf) == DDI_SUCCESS) {
6671708Sstevel 
6681708Sstevel 			msgintr->intr_handler = intr_handler;
6691708Sstevel 			msgintr->intr_lock = lock;
6701708Sstevel 			msgintr->intr_state = state;
6711708Sstevel 			msgintr->intr_msg_type = msg_type;
6721708Sstevel 			msgintr->intr_arg = (caddr_t)msgbuf;
6731708Sstevel 		} else {
6741708Sstevel 			err = RCECANTREGINTR;
6751708Sstevel 		}
6761708Sstevel 	} else {
6771708Sstevel 		err = RCEALREADYREG;
6781708Sstevel 	}
6791708Sstevel 
6801708Sstevel 	mutex_exit(rcs->dp_state.dp_mutex);
6811708Sstevel 
6821708Sstevel 	return (err);
6831708Sstevel }
6841708Sstevel 
6851708Sstevel /*
6861708Sstevel  * To unregister for asynchronous notifications
6871708Sstevel  */
6881708Sstevel int
rmc_comm_unreg_intr(uint8_t msg_type,rmc_comm_intrfunc_t intr_handler)6891708Sstevel rmc_comm_unreg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler)
6901708Sstevel {
6911708Sstevel 	struct rmc_comm_state	*rcs;
6921708Sstevel 	dp_msg_intr_t		*msgintr;
6931708Sstevel 	int			 err = RCNOERR;
6941708Sstevel 
6951708Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_unreg_intr")) == NULL)
6961708Sstevel 		return (RCENOSOFTSTATE);
6971708Sstevel 
6981708Sstevel 	mutex_enter(rcs->dp_state.dp_mutex);
6991708Sstevel 
7001708Sstevel 	msgintr = &rcs->dp_state.msg_intr;
7011708Sstevel 
7021708Sstevel 	if (msgintr->intr_handler != NULL &&
703*11066Srafael.vanoni@sun.com 	    msgintr->intr_msg_type == msg_type &&
704*11066Srafael.vanoni@sun.com 	    msgintr->intr_handler == intr_handler) {
7051708Sstevel 
7061708Sstevel 		ddi_remove_softintr(msgintr->intr_id);
7071708Sstevel 		msgintr->intr_handler = NULL;
7081708Sstevel 		msgintr->intr_id = 0;
7091708Sstevel 		msgintr->intr_msg_type = 0;
7101708Sstevel 		msgintr->intr_arg = NULL;
7111708Sstevel 		msgintr->intr_lock = NULL;
7121708Sstevel 		msgintr->intr_state = NULL;
7131708Sstevel 	} else {
7141708Sstevel 		err = RCEGENERIC;
7151708Sstevel 	}
7161708Sstevel 
7171708Sstevel 	mutex_exit(rcs->dp_state.dp_mutex);
7181708Sstevel 
7191708Sstevel 	return (err);
7201708Sstevel }
7211708Sstevel 
7221708Sstevel /*
7231708Sstevel  * To send raw data (firmware s-records) down to the RMC.
7241708Sstevel  * It is provided only to support firmware download.
7251708Sstevel  */
7261708Sstevel int
rmc_comm_send_srecord_bp(caddr_t buf,int buflen,rmc_comm_msg_t * response_bp,uint32_t wait_time)7271708Sstevel rmc_comm_send_srecord_bp(caddr_t buf, int buflen,
7281708Sstevel     rmc_comm_msg_t *response_bp, uint32_t wait_time)
7291708Sstevel {
7301708Sstevel 	struct rmc_comm_state	*rcs;
7311708Sstevel 	rmc_comm_dp_state_t	*dps;
7321708Sstevel 	dp_req_resp_t		*drr;
7331708Sstevel 	dp_message_t		*resp_bp;
7341708Sstevel 	clock_t			stop_clockt;
7351708Sstevel 	int			err;
7361708Sstevel 
7371708Sstevel 	/*
7381708Sstevel 	 * get the soft state struct (instance 0)
7391708Sstevel 	 */
7401708Sstevel 	if ((rcs = rmc_comm_getstate(NULL, 0,
741*11066Srafael.vanoni@sun.com 	    "rmc_comm_request_response_bp")) == NULL)
7421708Sstevel 		return (RCENOSOFTSTATE);
7431708Sstevel 
7441708Sstevel 	/*
7451708Sstevel 	 * sanity check: response_bp buffer must always be provided
7461708Sstevel 	 */
7471708Sstevel 	if (buf == NULL || response_bp == NULL) {
7481708Sstevel 		DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp,invalid args\n"));
7491708Sstevel 		return (RCEINVARG);
7501708Sstevel 	}
7511708Sstevel 
7521708Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp, buflen=%d\n", buflen));
7531708Sstevel 
7541708Sstevel 	dps = &rcs->dp_state;
7551708Sstevel 	drr = &dps->req_resp;
7561708Sstevel 	resp_bp = &drr->response;
7571708Sstevel 
7581708Sstevel 	mutex_enter(dps->dp_mutex);
7591708Sstevel 
7601708Sstevel 	rmc_comm_wait_enable_to_send(rcs, dps);
7611708Sstevel 
7621708Sstevel 	/*
7631708Sstevel 	 * initialization of the request/response data structure
7641708Sstevel 	 */
7651708Sstevel 	drr->flags = 0;
7661708Sstevel 	drr->error_status = 0;
7671708Sstevel 
7681708Sstevel 	/*
7691708Sstevel 	 * set the reply buffer: get the buffer already allocated
7701708Sstevel 	 * for the response
7711708Sstevel 	 */
7721708Sstevel 	resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
7731708Sstevel 	resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
7741708Sstevel 
7751708Sstevel 	/*
7761708Sstevel 	 * send raw data (s-record) and wait for the reply (BP message)
7771708Sstevel 	 */
7781708Sstevel 
7791708Sstevel 	rmc_comm_bp_srecsend(rcs, (char *)buf, buflen);
7801708Sstevel 
7811708Sstevel 	/*
7821708Sstevel 	 * place a lower limit on the shortest amount of time to be
7831708Sstevel 	 * waited before timing out while communicating with the RMC
7841708Sstevel 	 */
7851708Sstevel 	if (wait_time < DP_MIN_TIMEOUT)
7861708Sstevel 		wait_time = DP_MIN_TIMEOUT;
7871708Sstevel 
7881708Sstevel 	stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
7891708Sstevel 
7901708Sstevel 	if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
7911708Sstevel 	    stop_clockt)) == RCNOERR) {
7921708Sstevel 		/*
7931708Sstevel 		 * get the actual length of the msg
7941708Sstevel 		 * a negative value means that the reply message
7951708Sstevel 		 * was too big for the receiver buffer
7961708Sstevel 		 */
7971708Sstevel 		response_bp->msg_bytes = resp_bp->msg_msglen;
7981708Sstevel 		if (response_bp->msg_bytes < 0) {
7991708Sstevel 			err = RCEREPTOOBIG;
8001708Sstevel 		}
8011708Sstevel 	}
8021708Sstevel 
8031708Sstevel 	rmc_comm_dp_mcleanup(rcs);
8041708Sstevel 
8051708Sstevel 	rmc_comm_wake_up_next(rcs);
8061708Sstevel 
8071708Sstevel 	mutex_exit(dps->dp_mutex);
8081708Sstevel 
8091708Sstevel 	return (err);
8101708Sstevel }
8111708Sstevel 
8121708Sstevel /*
8131708Sstevel  * To wait for (any) BP message to be received.
8141708Sstevel  * (dp_mutex must be held)
8151708Sstevel  */
8161708Sstevel 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)8171708Sstevel rmc_comm_wait_bp_reply(struct rmc_comm_state *rcs, rmc_comm_dp_state_t *dps,
8181708Sstevel     dp_req_resp_t *drr, clock_t stop_clockt)
8191708Sstevel {
8201708Sstevel 	clock_t clockleft = 1;
8211708Sstevel 	int err = RCNOERR;
8221708Sstevel 
8231708Sstevel 	clockleft = cv_timedwait(drr->cv_wait_reply, dps->dp_mutex,
8241708Sstevel 	    stop_clockt);
8251708Sstevel 
8261708Sstevel 
8271708Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT,
8281708Sstevel 	    "reqresp_bp, send: flags=%02x, clktick left=%ld\n",
8291708Sstevel 	    drr->flags, clockleft));
8301708Sstevel 
8311708Sstevel 	/*
8321708Sstevel 	 * Check for error condition first.
8331708Sstevel 	 * Then, check if it has timeout.
8341708Sstevel 	 * Then, check if the command has been replied.
8351708Sstevel 	 */
8361708Sstevel 	if ((drr->flags & MSG_ERROR) != 0) {
8371708Sstevel 
8381708Sstevel 		err = RCEGENERIC;
8391708Sstevel 
8401708Sstevel 	} else if (clockleft <= 0) {
8411708Sstevel 		/*
8421708Sstevel 		 * timeout
8431708Sstevel 		 */
8441708Sstevel 
8451708Sstevel 		err = RCETIMEOUT;
8461708Sstevel 
8471708Sstevel 	} else if ((drr->flags & MSG_RXED_BP) == 0) {
8481708Sstevel 
8491708Sstevel 		err = RCEGENERIC;
8501708Sstevel 	}
8511708Sstevel 
8521708Sstevel 	return (err);
8531708Sstevel }
8541708Sstevel 
8551708Sstevel /*
8561708Sstevel  * Wait for the pending_request flag to be cleared and acquire it for our
8571708Sstevel  * own use. The caller is then allowed to start a new request/response
8581708Sstevel  * session with the RMC.
8591708Sstevel  * Note that all send-receive actions to the RMC include a time-out, so
8601708Sstevel  * the pending-request must eventually go away - even if the RMC is down.
8611708Sstevel  * Hence there is no need to timeout the wait action of this function.
8621708Sstevel  * (dp_mutex must be held on entry).
8631708Sstevel  */
8641708Sstevel static void
rmc_comm_wait_enable_to_send(struct rmc_comm_state * rcs,rmc_comm_dp_state_t * dps)8651708Sstevel rmc_comm_wait_enable_to_send(struct rmc_comm_state *rcs,
8661708Sstevel     rmc_comm_dp_state_t *dps)
8671708Sstevel {
8681708Sstevel 	DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d\n",
8691708Sstevel 	    dps->pending_request));
8701708Sstevel 
8711708Sstevel 	/*
8721708Sstevel 	 * A new message can actually grab the lock before the thread
8731708Sstevel 	 * that has just been signaled.  Therefore, we need to double
8741708Sstevel 	 * check to make sure that pending_request is not already set
8751708Sstevel 	 * after we wake up.
8761708Sstevel 	 *
8771708Sstevel 	 * Potentially this could mean starvation for certain unfortunate
8781708Sstevel 	 * threads that keep getting woken up and putting back to sleep.
8791708Sstevel 	 * But the window of such contention is very small to begin with.
8801708Sstevel 	 */
8811708Sstevel 
8821708Sstevel 	while (dps->pending_request) {
8831708Sstevel 		/*
8841708Sstevel 		 * just 'sit and wait' until there are no pending requests
8851708Sstevel 		 */
8861708Sstevel 
8871708Sstevel 		cv_wait(dps->cv_ok_to_send, dps->dp_mutex);
8881708Sstevel 	}
8891708Sstevel 
8901708Sstevel 	/*
8911708Sstevel 	 * now a request/response can be started. Set the flag so that nobody
8921708Sstevel 	 * else will be able to send anything.
8931708Sstevel 	 */
8941708Sstevel 	dps->pending_request = 1;
8951708Sstevel }
8961708Sstevel 
8971708Sstevel /*
8981708Sstevel  * To wake up one of the threads (if any) waiting for starting a
8991708Sstevel  * request/response session.
9001708Sstevel  * (dp_mutex must be held)
9011708Sstevel  */
9021708Sstevel static void
rmc_comm_wake_up_next(struct rmc_comm_state * rcs)9031708Sstevel rmc_comm_wake_up_next(struct rmc_comm_state *rcs)
9041708Sstevel {
9051708Sstevel 	/*
9061708Sstevel 	 * wake up eventual waiting threads...
9071708Sstevel 	 */
9081708Sstevel 
9091708Sstevel 	rcs->dp_state.pending_request = 0;
9101708Sstevel 	cv_signal(rcs->dp_state.cv_ok_to_send);
9111708Sstevel }
9121708Sstevel 
9131708Sstevel 
9141708Sstevel /*
9151708Sstevel  * thread which delivers pending request message to the rmc. Some leaf drivers
9161708Sstevel  * cannot afford to wait for a request to be replied/ACKed. Hence, a request
9171708Sstevel  * message is stored temporarily in the state structure and this thread
9181708Sstevel  * gets woken up to deliver it.
9191708Sstevel  */
9201708Sstevel static void
rmc_comm_send_pend_req(caddr_t arg)9211708Sstevel rmc_comm_send_pend_req(caddr_t arg)
9221708Sstevel {
9231708Sstevel 	struct rmc_comm_state		*rcs;
9241708Sstevel 	rmc_comm_drvintf_state_t	*dis;
9251708Sstevel 	callb_cpr_t			cprinfo;
9261708Sstevel 
9271708Sstevel 	if (arg == NULL) {
9281708Sstevel 		thread_exit();
9291708Sstevel 		/* NOTREACHED */
9301708Sstevel 	}
9311708Sstevel 
9321708Sstevel 	rcs = (struct rmc_comm_state *)arg;
9331708Sstevel 	dis = &rcs->drvi_state;
9341708Sstevel 
9351708Sstevel 	CALLB_CPR_INIT(&cprinfo, dis->dreq_mutex, callb_generic_cpr,
9361708Sstevel 	    "rmc_comm_send_pend_req");
9371708Sstevel 
9381708Sstevel 	mutex_enter(dis->dreq_mutex);
9391708Sstevel 
9401708Sstevel 	if (dis->dreq_state <= RMC_COMM_DREQ_ST_READY)
9411708Sstevel 		dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
9421708Sstevel 
9431708Sstevel 	for (;;) {
9441708Sstevel 
9451708Sstevel 		/*
9461708Sstevel 		 * Wait for someone to tell me to continue.
9471708Sstevel 		 */
9481708Sstevel 		while (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
9491708Sstevel 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
9501708Sstevel 			cv_wait(dis->dreq_sig_cv, dis->dreq_mutex);
9511708Sstevel 			CALLB_CPR_SAFE_END(&cprinfo, dis->dreq_mutex);
9521708Sstevel 		}
9531708Sstevel 
9541708Sstevel 		/* RMC_COMM_DREQ_ST_EXIT implies signal by _detach(). */
9551708Sstevel 		if (dis->dreq_state == RMC_COMM_DREQ_ST_EXIT) {
9561708Sstevel 			dis->dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
9571708Sstevel 			dis->dreq_tid = 0;
9581708Sstevel 
9591708Sstevel 			/* dis->dreq_mutex is held at this point! */
9601708Sstevel 			CALLB_CPR_EXIT(&cprinfo);
9611708Sstevel 
9621708Sstevel 			thread_exit();
9631708Sstevel 			/* NOTREACHED */
9641708Sstevel 		}
9651708Sstevel 
9661708Sstevel 		ASSERT(dis->dreq_state == RMC_COMM_DREQ_ST_PROCESS);
9671708Sstevel 		mutex_exit(dis->dreq_mutex);
9681708Sstevel 
9691708Sstevel 		/*
9701708Sstevel 		 * deliver the request (and wait...)
9711708Sstevel 		 */
9723482Sjfrank 		while (rmc_comm_send_req_resp(rcs, &dis->dreq_request, NULL,
9733482Sjfrank 		    RMC_COMM_DREQ_DEFAULT_TIME) == RCEGENERIC) {
9743482Sjfrank 		}
9751708Sstevel 
9761708Sstevel 		mutex_enter(dis->dreq_mutex);
9771708Sstevel 		if (dis->dreq_state != RMC_COMM_DREQ_ST_EXIT)
9781708Sstevel 			dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
9791708Sstevel 	}
9801708Sstevel }
9811708Sstevel 
9821708Sstevel /*
9831708Sstevel  * start thread to deal with pending requests to be delivered asynchronously
9841708Sstevel  * (i.e. leaf driver do not have to/cannot wait for a reply/ACk of a request)
9851708Sstevel  */
9861708Sstevel static int
rmc_comm_dreq_thread_start(struct rmc_comm_state * rcs)9871708Sstevel rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs)
9881708Sstevel {
9891708Sstevel 	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
9901708Sstevel 	int err = 0;
9911708Sstevel 	kthread_t *tp;
9921708Sstevel 
9931708Sstevel 	mutex_enter(dis->dreq_mutex);
9941708Sstevel 
9951708Sstevel 	if (dis->dreq_state == RMC_COMM_DREQ_ST_NOTSTARTED) {
9961708Sstevel 
9971708Sstevel 		tp = thread_create(NULL, 0, rmc_comm_send_pend_req,
9981708Sstevel 		    (caddr_t)rcs, 0, &p0, TS_RUN, maxclsyspri);
9991708Sstevel 		dis->dreq_state = RMC_COMM_DREQ_ST_READY;
10001708Sstevel 		dis->dreq_tid = tp->t_did;
10011708Sstevel 	}
10021708Sstevel 
10031708Sstevel 	mutex_exit(dis->dreq_mutex);
10041708Sstevel 
10051708Sstevel 	return (err);
10061708Sstevel }
10071708Sstevel 
10081708Sstevel /*
10091708Sstevel  * stop the thread (to deliver pending request messages)
10101708Sstevel  */
10111708Sstevel static void
rmc_comm_dreq_thread_kill(struct rmc_comm_state * rcs)10121708Sstevel rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs)
10131708Sstevel {
10141708Sstevel 	rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
10151708Sstevel 	kt_did_t tid;
10161708Sstevel 
10171708Sstevel 	mutex_enter(dis->dreq_mutex);
10181708Sstevel 	tid = dis->dreq_tid;
10191708Sstevel 	if (tid != 0) {
10201708Sstevel 		dis->dreq_state = RMC_COMM_DREQ_ST_EXIT;
10211708Sstevel 		dis->dreq_tid = 0;
10221708Sstevel 		cv_signal(dis->dreq_sig_cv);
10231708Sstevel 	}
10241708Sstevel 	mutex_exit(dis->dreq_mutex);
10251708Sstevel 
10261708Sstevel 	/*
10271708Sstevel 	 * Wait for rmc_comm_send_pend_req() to finish
10281708Sstevel 	 */
10291708Sstevel 	if (tid != 0)
10301708Sstevel 		thread_join(tid);
10311708Sstevel }
10321708Sstevel 
10331708Sstevel /*
10341708Sstevel  * init function - start thread to deal with pending requests (no-wait requests)
10351708Sstevel  */
10361708Sstevel int
rmc_comm_drvintf_init(struct rmc_comm_state * rcs)10371708Sstevel rmc_comm_drvintf_init(struct rmc_comm_state *rcs)
10381708Sstevel {
10391708Sstevel 	int err = 0;
10401708Sstevel 
10411708Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_init\n"));
10421708Sstevel 	rcs->drvi_state.dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
10431708Sstevel 	rcs->drvi_state.dreq_tid = 0;
10441708Sstevel 
10451708Sstevel 	mutex_init(rcs->drvi_state.dreq_mutex, NULL, MUTEX_DRIVER, NULL);
10461708Sstevel 	cv_init(rcs->drvi_state.dreq_sig_cv, NULL, CV_DRIVER, NULL);
10471708Sstevel 
10481708Sstevel 	err = rmc_comm_dreq_thread_start(rcs);
10491708Sstevel 	if (err != 0) {
10501708Sstevel 		cv_destroy(rcs->drvi_state.dreq_sig_cv);
10511708Sstevel 		mutex_destroy(rcs->drvi_state.dreq_mutex);
10521708Sstevel 	}
10531708Sstevel 
10541708Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "thread started? err=%d\n", err));
10551708Sstevel 
10561708Sstevel 	return (err);
10571708Sstevel }
10581708Sstevel 
10591708Sstevel /*
10601708Sstevel  * fini function - kill thread to deal with pending requests (no-wait requests)
10611708Sstevel  */
10621708Sstevel void
rmc_comm_drvintf_fini(struct rmc_comm_state * rcs)10631708Sstevel rmc_comm_drvintf_fini(struct rmc_comm_state *rcs)
10641708Sstevel {
10651708Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:stop thread\n"));
10661708Sstevel 
10671708Sstevel 	rmc_comm_dreq_thread_kill(rcs);
10681708Sstevel 
10691708Sstevel 	DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:destroy Mx/CVs\n"));
10701708Sstevel 
10711708Sstevel 	cv_destroy(rcs->drvi_state.dreq_sig_cv);
10721708Sstevel 	mutex_destroy(rcs->drvi_state.dreq_mutex);
10731708Sstevel }
1074